The small rewrite of the Crossfire Plugin subsystem is nearing completion -
finally. I think that a couple of explanations are necessary.
Why ?
-
The main reason to answer the why a rewrite is that the older way of managing
plugins was simply too complex and obscure. Using the CFParm structure to pass
arguments between the server and the plugins initially seemed a good idea - but
it came at the price of an heavy-to-write, heavy-to-use system. Having to fill
in a table of CFParms and to ready a return CFParm structure is
counterintuitive to what most C programmers usually do.
What will change and how ?
--
1. Code Internals.
The rewrite somewhat simplifies the code writing. Basically, most server
functions are now exposed with the following prototype:
void* cfapi_funct(int* type, ...)
type indicates the type of the return value, as defined by constants in
include/plugins.h. The arguments are now transmitted using the more
conventional va_args mechanism. There is obviously a drawback in that the
server now assumes that the correct parameter values are passed by the plugin -
but expecting that the plugin developper passes a correct parameter type isn't
too much asking IMHO.
Moreover, a group of common server function wrappers has been written. When a
new plugin is being developed, the coder should include those common wrappers
in its compilation process. Those ensure that (1) the plugin coder doesn't need
to directly call the server callback function pointers and (2) limits the
possibility of passing wrong argument types. So for example, instead of having
to use the void* cfapi_map_create_path(int* type, ...) callback and taking the
risk of passing wrong argument types and having to manually manage typecasts
and callback bindings, the plugin coder can call the common wrapper char*
cf_get_maps_directory(char* str) and use it without worrying on how the
plugin-server communication works.
On the server-side, the code is also somewhat simpler, because the CFParm
wrapping doesn't exist anymore - calling a plugin event now takes one or two
lines, to compare with the dozen (or more) before.
2. Event hooks
The second important change is how the events are bound to objects. Previously,
plugins used various event_xxx fields in the object to bind. It has a rather
annoying drawback: you couldn't bind more than one action to one event (chains
of events weren't possible). The new system uses a new archetype type EVENT;
the map-maker now wraps the binding in an event_xxx *object* that is stored in
the inventory of the bound object. It allows to bind several plugins to a
single event in a single object. It also removes the need for supplementary
fields and makes the event subsystem more consistent with the everything is an
object motto.
3. Compatibility with v1.0 of the plugin system
The v1.0 plugins (there are currently 3) are *not* compatible, even at source
level, with the v2.0 interface. There is also no effort made to ensure
compatibility with objects bound with the 1.0 event_xxx tags, so map-makers
will have to adapt them accordingly. Note that it isn't a lot of work - it
isn't as if there were thousands of objects to convert.
I could have assured compatibility, but it the work and code complexity it
introduced seemed much greater than the work needed to convert the few objects
that were using the old system.
A potential problem may arise with players owning scripted objects, as those
will cease to work. But unless there are *lots* of such cases (which is
doubtful), simple manual exchange with newer versions of the objects from the
DMs should suffice.
What about the current plugins ?
1. CFPython
The CFPython plugin has been rewritten to match the new interface. The occasion
to rewrite it has been used to make the CFPython interface cleaner and
(hopefully) easier from a Python-coder point-of-view.
The most important change is the introduction of Python object types wrapping
Crossfire entities (Crossfire Objects, Maps, and Players). Available functions
are mostly the same (only those rendered obsolete by the new plugin
infrastructure were removed), but they are now properties and methods of Python
objects.
Examples
CFPython.AcquireSpell(target, spell) now becomes target.AcquireSpell(spell).
CFPython.GetStrength(who) now becomes who.Str.
CFPython.SetStrength(who,val) now becomes who.str = val.
The conversion process is rather straightforward and shouldn't cause trouble -
it took me an afternoon to convert all scripts supplied in maps-bigworld/python
and I'm no Python specialist.
2. The Animator
The Animator is currently being rewritten to work with the new plugin
interface. Apart from the event-binding procedure, there should be no other
change in the way it works. Work on it isn't finished, but shouldn't take years
to complete, unlike CFPython :).
3. The Logger
The Logger is outdated