Difference between revisions of "AMX Mod Compatibility Layer (AMX Mod X)"
(oh) |
m (→ABI/API Compatibility: - forgot forwards) |
||
Line 76: | Line 76: | ||
For binary compatibility, both forwards and natives must be considered. Natives are implemented as dynamic natives. If they already exist as an AMX Mod compatibility stock, they simply wrap the stock. For example, <tt>get_offset_int()</tt> is implemented as a stock. However, the name <tt>get_offset_int</tt> is also registered as a dynamic native. Internally, it simply calls the <tt>get_offset_int()</tt> stock. For natives which require more complexity, there are no stocks, and they are implemented entirely as dynamic natives. For example, traceresult is implemented this way. | For binary compatibility, both forwards and natives must be considered. Natives are implemented as dynamic natives. If they already exist as an AMX Mod compatibility stock, they simply wrap the stock. For example, <tt>get_offset_int()</tt> is implemented as a stock. However, the name <tt>get_offset_int</tt> is also registered as a dynamic native. Internally, it simply calls the <tt>get_offset_int()</tt> stock. For natives which require more complexity, there are no stocks, and they are implemented entirely as dynamic natives. For example, traceresult is implemented this way. | ||
+ | |||
+ | Forwards are implemented as calls to <tt>CreateMultiForward()</tt> and <tt>ExecuteForward()</tt>. | ||
In short, the majority of functions are implemented "twice" - as stocks, for recompiling plugins, and as natives, for not recompiling plugins. The duality lets you have flexibility. You can recompile for extra speed (dynamic natives are considered expensive) or run straight binaries for ease of use. | In short, the majority of functions are implemented "twice" - as stocks, for recompiling plugins, and as natives, for not recompiling plugins. The duality lets you have flexibility. You can recompile for extra speed (dynamic natives are considered expensive) or run straight binaries for ease of use. |
Revision as of 19:58, 8 September 2006
As of 1.76, AMX Mod X has an AMX Mod backward compatibility layer. This layer has two goals. The first goal is API compatibility: AMX Mod plugins should compile without modification and subsequently run normally on AMX Mod X. The second goal is ABI compatibility: AMX Mod plugins should run as raw '.amx' files, without recompilation, on AMX Mod X.
This current status of this side project is experimental. While it will work for most plugins, there may be niches or obscure details yet unresolved.
Contents
List of Tested Plugins
The following is a list of plugins the development or beta testing team has verified to be working. (D) denotes a default plugin shipped with AMX Mod.
- Dev Tested
- admin (D)
- admin SQL (D)
- hackmod
- sentryguns
- shoot_at_nades
- ultimate_c4
Makeshift Tracker
- VexdUM Port
- Natives
- Engine's entity_[g|s]_*() natives should no longer bail out if the player is not ingame.
- radius_damage replacement (NOTE port done in Engine)
- traceresult and its related natives
- get/set offset functions
- TODO: Port the TFC natives.
- Forwards
- All done but monster_hurt.
- Natives
- MySQL Port
- Entirely done.
- Fun Port
- Unknown
- Core Port
- Translator functionality nearly complete. Helper functions not done yet.
- Added detection for AMX Mod's localinfo keys.
- Math functions ported.
Userland Information
Language Files
AMX Mod language files, if needed, can be used. They are marshalled by the bcompat plugin and it must be loaded. Simply place the files in amxmodx/data/amxmod-lang/. You do not need to modify or rename them.
localinfo/core.ini
Localinfo support is marshalled by core. If you have copied your core.ini from AMX Mod, you will want to change the paths to match your AMX Mod X installation.
Technical/Coding Details
The AMX Mod compatibility layer (codenamed 'bcompat') is largely done through a plugin called amxmod_bcompat. Our goal was to keep C++ editing to a minimum. Unfortunately, there are a few limited things that could only be done in C++. However, keeping the majority of code in a plugin reduces the amount of work ordinary plugin developers need to add features or fix problems. It also makes it easy to centralize the system rather than adding spidery code across modules.
Internal Compatibility (Core)
Core adds specific backwards compatibility for plugins detected as AMX Mod plugins. It has two methods of detection. On detection, the plugin is flagged with AMX_FLAG_OLDFILE.
- Any plugin being loaded with a '.amx' extension is flagged as a bcompat plugin. Although this extension was used with AMX Mod X 0.16, the core version was similar enough to AMX Mod 0.9.7 that the time gap should be about the same.
- Plugins recompiled while including 'amxmod.inc' automatically get a special identifier added to their public variable table. Core identifies any plugin carrying this identifier and flags it as a bcompat plugin.
Plugins flagged as bcompat act almost entirely the same. The two major differences are in which natives they are allowed to use and how they format strings.
Translator
AMX Mod's translator can be broken into two pieces. The first is the file format, which must be loaded and added to a database. The second is the actual translation API itself and how plugins use it.
File Format
The file format is completely loaded and parsed by the bcompat plugin. The translation key pairs are added to the internal AMX Mod X storage engine through the CreateLangKey() and AddTranslation() natives. As noted earlier, the files are loaded from amxx_datadir/amxmod-lang/.
Backend Translation
The backend is provided through Core. The _T() stock remains and the translate() is provided to all plugins (whether having AMX_FLAG_OLDFILE or not). The translate native takes in the translation info and creates a unique translation identifier returned to the plugin for use with the '%s' format specifier. If the get_amxstring(), get_amxstring_r(), or format() routines are used from a flagged plugin, and one of these unique identifiers is used for '%s', the translation identifier is freed and the translation performed.
This implementation allows for up to 1,024 translation identifiers to be in the translation pool at once. If for some reason a translation fails to pop off the internal stack, for example:
format(buffer, sizeof(buffer)-1, "%s %d %s %s", _T(...), b(), _T(...), _T(...))
If b() fails, the first translation passed will "leak." These leaked identifiers are freed on mapchange (and only take up about 12 bytes), but if a continuous flaw exists in a plugin, the translation pool will be filled and future translations will fail. This may or may not be addressed in a future release.
Native Restrictions
There are some natives for which backwards compatibility cannot be added easily. An example of this is DispatchKeyValue(), which has completely different calling conventions between mods. A new module function exported from Core, MF_AddNewNatives(), lets modules add lists of natives that can only be used by non-AMX Mod compatibility flagged plugins. This lets Engine specify that its version of DispatchKeyValue() should only be used with newer plugins, so the bcompat plugin can provide its own version.
ABI/API Compatibility
There are two major sides to plugin compatibility: Source, and Binary. Binary compatible deals with making sure compiled plugins have all of their natives and forwards available. Source compatible means making sure plugins can compile and work as-is. The bcompat layer deals with both.
For source compatibility, all stocks are mimicked from their original includes into the AMX Mod compatibility includes. Natives that can be wrapped around original AMX Mod X natives are implemented as stocks. This means that recompiling plugins from AMX Mod has relatively low overhead, as most natives are simply stock wrappers.
For binary compatibility, both forwards and natives must be considered. Natives are implemented as dynamic natives. If they already exist as an AMX Mod compatibility stock, they simply wrap the stock. For example, get_offset_int() is implemented as a stock. However, the name get_offset_int is also registered as a dynamic native. Internally, it simply calls the get_offset_int() stock. For natives which require more complexity, there are no stocks, and they are implemented entirely as dynamic natives. For example, traceresult is implemented this way.
Forwards are implemented as calls to CreateMultiForward() and ExecuteForward().
In short, the majority of functions are implemented "twice" - as stocks, for recompiling plugins, and as natives, for not recompiling plugins. The duality lets you have flexibility. You can recompile for extra speed (dynamic natives are considered expensive) or run straight binaries for ease of use.