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:
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:
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:
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 );
|