Crossfire Server, Trunk
|
Fields that the rest of loader.l doesn't understand are recorded in the object as 'key/value fields'. The first word on the line is the key, and the rest of the line (leading and trailing whitespace is stripped) is the value (which may be an empty string!).
These allow arbitrary addition of new fields to the object definition without any changes to the loader/saver, and without any increase in object space. Use these to reduce the overloading of various fields for sets of infrequently used objects.
Storing as key/value pairing makes it so that it is only parsed once during loading - this makes accessing data much faster than if it was just a free block of text that needed parsing each time something might need to get a key. The data itself does remain as text, so if the data is desired in another format, function will need to convert it (eg, atoi) as needed.
From inside crossfire-server (these are defined in object.c):
key_value *object_get_key_value(const object *ob, const char *key)
Returns the value parameter for the given key, or NULL if the object doesn't have such an extra_field. The string returned is a shared string so should not be modified.
Note there is no way to tell if the field does not exist or has been set to an empty value. If this differentiation is necessary, a value should be stored away.
int object_set_value(object *op, const char *key, const char *value, int add_key)
Sets the value for the given key. If add_key is true, it will add the key if not defined, otherwise, it only updates existing keys. Returns true/false based on success.
The passed in value is converted to a shared string. Thus, once this function is called, modifying value should not be done.
Note - usually add_key should be false - there should be little reason to only update existing keys - if you want to store that data away, you should do it regardless of the type.
Passing in NULL as the value effectively clears the value.
Just add the lines to the object, eg
foo bar
will automatically be handled so that key 'foo' is set to 'bar'
The following should be considered:
1) Access to key/value fields is slower than accessing a field directly. Thus, should not be used in time sensitive areas (combat related basically)
For example, if the only time the key/value would need to be used is by player activating the object, that doesn't happen all that often, so good option.
If however the key/value would be examined for map updates (say glow_radius) probably not a good option.
Note also that the performance impact of key/values depend on the number of key/values used for the specific object. If an object has 10 key/value pairs, access is much worse than if it just has one.
However, in all cases, performance is slow because a the key has to looked up in the shared string database and then pointer comparisons done.
This performance consideration is only something that needs to be if the actual key/value is used in the time critical area, and not the object as a whole. For example, setting a key/value for a monster isn't a problem if that key/value isn't examined during combat (maybe something related to conversation). However, storing attack related value there would not be a good idea.
2) Use of key values is more memory intensive than just adding fields for objects. On a 32 bit system, eg key/value structure is 12 bytes, compared to just 4 bytes for a pointer to a string.
One big advantage of the key value is this memory is only used when a key is set. So if you do the math, if more than about 1/3 of the items would have that key/value pair, actually uses less memory to just add a field.
3) Key/value fields shouldn't be used if they are related to an existing field. For example, if there is a 'foo' field, add you want a maxfoo value, it should be done as another field and not as a get_ob_key_value(op, "maxfoo") - trying to main that is difficult.
key/values don't guard against real fields being used: set_ob_key_value(ob, "hp", "acidic") will not have the desired effect.
The load/save code is such that it will save the key/value lists before actual field values. When loading, the last value is used, so in the above example, there may be two lines:
hp acidic hp 20
After load, the value of hp will be 20, since that is the last value loaded.
get_ob_key_value(...)
with a key for an actual field in the object will not work. Eg, get_ob_key_value(op, "hp")
will not return the hp value of the object - there is no easy way for the functions to to access all the fields.startfoo line1 line2 endfoo
and have it work. However, the line length allowed in the loader is quite long.