[Home] [Downloads] [Search] [Help/forum]

Gammon Software Solutions forum

See www.mushclient.com/spam for dealing with forum spam. Please read the MUSHclient FAQ!

[Folder]  Entire forum
-> [Folder]  SMAUG
. -> [Folder]  Compiling the server
. . -> [Subject]  How to add Global Unique IDs to objects and characters

Home  |  Users  |  Search  |  FAQ
Username:
Register forum user name
Password:
Forgotten password?

How to add Global Unique IDs to objects and characters

[Reply to this subject]  Reply to this subject   [New subject]  Start a new subject   [Refresh] Refresh page


Posted by Nick Gammon   Australia  (18,800 posts)  [Biography] bio   Forum Administrator
Date Tue 23 Feb 2010 04:07 AM (UTC)  quote  ]
Message
As part of experimenting with improved client/server interaction, I have added GUIDs to characters and objects in SMAUG. In case anyone else wants to do it, this is how I did it...

Compile as C++


Edit the Makefile and force compilation to be C++ rather than C by adding "-x c++" to the line that does the compiles:


o/%.o: %.c
    echo "  Compiling $@....";
    $(CC) -x c++ -c $(C_FLAGS) $< -o $@


This should compile OK without any errors or warnings under SmaugFUSS 1.9.

Add some STL stuff to mud.h


There are some useful STL headers you can grab (Standard Template Library). Near the start of mud.h add:


#include <list>
#include <map>
#include <vector>
#include <string>
#include <algorithm>


Add typedef for GUID



// global unique IDs
typedef long long GUID;


Add guid field to char_data and obj_data



struct char_data
{

...

   GUID guid;   // unique ID - stored in guid_char_map
};

...

struct obj_data
{

...

   GUID guid;  // unique ID - stored in guid_object_map
};


Add function to make the GUID



GUID makeguid ();  // prototype in mud.h


... and in comm.c (or some file) add the function:


GUID makeguid ()  // get a new GUID
  {
  static GUID _guid = 0;
  return ++_guid;
  }



Add maps to map GUIDs to pointers



// in mud.h

extern std::map<GUID, OBJ_DATA *> guid_object_map;
extern std::map<GUID, CHAR_DATA *> guid_char_map;


Create GUIDs when things are created


In db.c add the actual declarations for the maps:


std::map<GUID,OBJ_DATA *> guid_object_map;
std::map<GUID, CHAR_DATA *> guid_char_map;


In create_object add the object to the map:


// after the CREATE line:

   obj->guid = makeguid ();
   guid_object_map [obj->guid] = obj;


And in clone_object do the same thing:


// after the CREATE line:

   clone->guid = makeguid ();
   guid_object_map [clone->guid] = clone;


And in create_mobile do the same for mobiles:


// after the CREATE line:

   mob->guid = makeguid ();
   guid_char_map [mob->guid] = mob;


And in save.c, function fread_obj:


// after the CREATE line:

   obj->guid = makeguid ();
   guid_object_map [obj->guid] = obj;


Also in save.c, in load_char_obj, do something similar:


// after the CREATE line:

   mob->guid = makeguid ();
   guid_char_map [mob->guid] = mob;


Remove object from map when removed from world


In handler.c, in extract_obj function, after the line:


  UNLINK( obj, first_object, last_object, next, prev );


Add:


   std::map<GUID,OBJ_DATA *>::iterator i = guid_object_map.find (obj->guid);
   if (i != guid_object_map.end ())
     guid_object_map.erase (i);


And in free_obj before the line:


DISPOSE( obj );


Add:


   std::map<GUID,OBJ_DATA *>::iterator i = guid_object_map.find (obj->guid);
   if (i != guid_object_map.end ())
     guid_object_map.erase (i);


(It may be overkill doing it twice, but I wanted to make sure that once the object is unlinked from the world's object list it was removed from the map).

In free_char do the same for mobs/characters ... before the line:


   DISPOSE( ch );


Do this:


   // get rid of it from GUID map
   std::map<GUID,CHAR_DATA *>::iterator i = guid_char_map.find (ch->guid);
   if (i != guid_char_map.end ())
     guid_char_map.erase (i);



Also in save.c if the object is created and then disposed we need to get rid of the map as well:

Before the line:


DISPOSE( obj );


Add:


   std::map<GUID,OBJ_DATA *>::iterator i = guid_object_map.find (obj->guid);
   if (i != guid_object_map.end ())
     guid_object_map.erase (i);


Now we can use the GUIDs


After this work, we can be pretty confident that, given a GUID we can get back the original pointer.

eg.


// for some guid we know about:

  std::map<GUID,CHAR_DATA *>::iterator iter = guid_char_map.find (guid);
 
  if (iter != guid_char_map.end ())
    {
    CHAR_DATA * ch = iter->second;
    
    // play with character here
    }


And similarly for objects:


 std::map<GUID,OBJ_DATA *>::iterator iter = guid_object_map.find (guid);
 
  if (iter != guid_object_map.end ())
    {
    OBJ_DATA * obj = iter->second;

    // play with object here
    }


Allow players to use GUIDs


You can modify get_obj_list and get_obj_list_rev to allow players to use GUIDs in their commands, like this:


get <4587483>
examine <858756>


I did that by looking for <id> inside those functions (extra lines in bold):


/*
 * Find an obj in a list.
 */
OBJ_DATA *get_obj_list( CHAR_DATA * ch, char *argument, OBJ_DATA * list )
{
   char arg[MAX_INPUT_LENGTH];
   OBJ_DATA *obj;
   int number;
   int count;
   GUID guid;

   number = sscanf(argument, "<%Ld>", &guid );

   if (number == 1 && guid > 0)
     {
     for( obj = list; obj; obj = obj->next_content )
       if( can_see_obj( ch, obj ) && obj->guid == guid )
            return obj;

     return NULL;
     }

   number = number_argument( argument, arg );



/*
 * Find an obj in a list...going the other way          -Thoric
 */
OBJ_DATA *get_obj_list_rev( CHAR_DATA * ch, const char *argument, OBJ_DATA * list )
{
   char arg[MAX_INPUT_LENGTH];
   OBJ_DATA *obj;
   int number;
   int count;

   GUID guid;

   number = sscanf(argument, "<%Ld>", &guid );

   if (number == 1 && guid > 0)
     {
     for( obj = list; obj; obj = obj->prev_content )
       if( can_see_obj( ch, obj ) && obj->guid == guid )
            return obj;

     return NULL;
     }

   number = number_argument( argument, arg );


- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (18,800 posts)  [Biography] bio   Forum Administrator
Date Reply #1 on Tue 23 Feb 2010 04:12 AM (UTC)  quote  ]

Amended on Tue 23 Feb 2010 04:18 AM (UTC) by Nick Gammon

Message
So what is the point of all this you ask?

Well, when working on scripted interactions between client and server, we need a precise way of defining things, and the old "get 2.sword" just doesn't cut it.

Let's imagine there are three swords on the ground, and two players try to pick up the second one (ie. "get 2.sword").

Well the server can only process one command at a time, so by the time the second player's command is processed 2.sword is gone, and the original 3.sword is now 2.sword.

There is also confusion if there are two types of swords and you inadvertently grab the wrong one.

All these problems go away if the server assigns every object a unique ID (the GUID). Then as part of server->client interaction (the subject of another thread) it can send down the GUIDs to positively identify things (like, in an inventory).

Now the client can send back "get <3487384>" to get an object, positively and uniquely identified by its own number, and not have confusion about some other object of a similar name. Ditto for mobs.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by ThomasWatts   USA  (66 posts)  [Biography] bio
Date Reply #2 on Tue 23 Feb 2010 05:08 AM (UTC)  quote  ]

Amended on Tue 23 Feb 2010 05:11 AM (UTC) by ThomasWatts

Message
Quote:
Let's imagine there are three swords on the ground, and two players try to pick up the second one (ie. "get 2.sword").

Well the server can only process one command at a time, so by the time the second player's command is processed 2.sword is gone, and the original 3.sword is now 2.sword.


Or if you allow players to use more descriptive actions.
IE. 'get blue sword'

However, things having unique global identification can allow for anything to be cloned and then have temporary changes without having to create a physical(written to disk) copy.
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


1,539 views.

[Reply to this subject]  Reply to this subject   [New subject]  Start a new subject   [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

[Home]

Written by Nick Gammon - 5K

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( http://www.gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Internet Contents Rating Association (ICRA) - 2K]    [Web site powered by FutureQuest.Net]