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

Gammon 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

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Tue 23 Feb 2010 04:07 AM (UTC)
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  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #1 on Tue 23 Feb 2010 04:12 AM (UTC)

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)

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

Posted by Llarn   (19 posts)  [Biography] bio
Date Reply #3 on Sun 20 Oct 2013 02:29 PM (UTC)

Amended on Sun 20 Oct 2013 02:31 PM (UTC) by Llarn

Message
Hi Nick,

I thought this concept is cool enough to give it a go.

Once I installed it though, my compiler does not like GUID used in mud.h for
In file included from act_comm.c:16:0:
h/mud.h:2226:5: error: ‘GUID’ does not name a type
h/mud.h:2741:5: error: ‘GUID’ does not name a type

These lines .....
GUID guid;

Instead it wants something like
it wants something like long guid I think.

I have gcc 4.6.3
[Go to top] top

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #4 on Sun 20 Oct 2013 10:26 PM (UTC)
Message
Try this:


typedef struct _GUID {          // size is 16
    DWORD Data1;
    WORD   Data2;
    WORD   Data3;
    BYTE  Data4[8];
} GUID;


- Nick Gammon

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

Posted by Llarn   (19 posts)  [Biography] bio
Date Reply #5 on Mon 21 Oct 2013 01:14 AM (UTC)

Amended on Mon 21 Oct 2013 01:42 AM (UTC) by Llarn

Message
Adding that gives me this.

In file included from act_comm.c:16:0:
h/mud.h:2748:5: error: ‘GUID’ does not name a type
make[2]: *** [o/act_comm.o] Error 1


This is how my mud.h looks

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

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

// global unique IDs
// typedef long long GUID;

typedef struct _GUID {          // size is 16
    DWORD  Data1;
    WORD   Data2;
    WORD   Data3;
    BYTE   Data4[8];
} GUID;

And further down in the obj structure at line 2748
    
GUID                    guid;  // unique ID - stored in guid_object_map 
};

[Go to top] top

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #6 on Mon 21 Oct 2013 11:10 PM (UTC)
Message
Well, you have to have the typedef before you use it.

- Nick Gammon

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

Posted by Llarn   (19 posts)  [Biography] bio
Date Reply #7 on Mon 21 Oct 2013 11:52 PM (UTC)
Message
you mean like this right?


typedef long long GUID;


typedef struct _GUID {          // size is 16
    DWORD  Data1;
    WORD   Data2;
    WORD   Data3;
    BYTE   Data4[8];
} GUID;



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



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




Doing this gives me

Compiling o/act_comm.o
In file included from act_comm.c:16:0:
h/mud.h:2745:5: error: ‘GUID’ does not name a type
make[1]: *** [o/act_comm.o] Error 1

Sorry if I am misunderstanding what I need to do.
[Go to top] top

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #8 on Tue 22 Oct 2013 09:39 AM (UTC)
Message
On my Linux machine this compiles with no errors:


#include <map>

typedef long DWORD;
typedef int WORD;
typedef unsigned char BYTE;

typedef struct _GUID {          // size is 16
    DWORD  Data1;
    WORD   Data2;
    WORD   Data3;
    BYTE   Data4[8];
} GUID;

extern std::map<GUID, int> guid_object_map;

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

int main ()
 {
 return 0;
 }

- Nick Gammon

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

Posted by Llarn   (19 posts)  [Biography] bio
Date Reply #9 on Tue 22 Oct 2013 10:38 AM (UTC)
Message
Yeah, it has me baffled. I am using gcc 4.6.3 though.

My Makefile looks like
o/%.o: %.c $(H_FILES)
echo " Compiling $@";
$(CC) -x c++ -c $(C_FLAGS) $< -o $@
# $(CC) -c $(C_FLAGS) $< -o $@

So pretty sure I have the c++ gcc compile.

My mud.h looks identical to what you have now, and it still gives me that error in obj data structure unless I change the GUID to a int guid;

But that then errors out in comm.c in makeguid function
comm.c:3702:1: error: ‘GUID’ does not name a type
make[2]: *** [o/comm.o] Error 1

Thanks for trying Nick.

[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.


7,337 views.

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

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

[Best viewed with any browser - 2K]    [Hosted at FutureQuest]