AMX Mod Compatibility Layer (AMX Mod X)
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.
- 1 How to Use/Enable
- 2 List of Tested Plugins
- 3 Makeshift Features/Tracker
- 4 Userland Information
- 5 Technical/Coding Details
How to Use/Enable
Simply make sure 'amxmod_compat.amxx' is in your plugins folder and enabled in configs/plugins.ini. It comes with AMX Mod X but is disabled by default.
List of Tested Plugins
The following is a list of plugins that have been verified as working. (D) denotes a default plugin shipped with AMX Mod.
Dev Team List
- Source+Binary Compatible
- Source Compatible Only
- admincmd (D)
- cmdmenu (D)
- statscfg (D)
- No Compatibility
- Source+Binary Compatible
- Source Compatible Only
- No Compatibility
- VexdUM Port
- 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.
- All done but monster_hurt.
- MySQL Port
- Entirely done.
- Fun Port
- Core Port
- Translator functionality nearly complete. Helper functions not done yet.
- Added detection for AMX Mod's localinfo keys.
- Math functions ported.
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 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.
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.
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.
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/.
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 identifier is decoded and translated.
Note: The current implementation has restrictions, which are in-place due to the way the AMX Mod translation system is written. The backwards compatibility layer will fail to translate any translation key past the 4,095th entry. Furthermore, it will fail to translate any language with a language id of above 30.
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.
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.
The amxmod_compat.sma file is the main file to compile when compiling the compatibility plugin. You should not attempt to compile core.sma, vexdum.sma, mysql.sma, or anything else that may be there.
When editing the compatibility layer, it is organized into separate .sma files, which are included into the main amxmod_compat.sma file. They are named .sma rather than .inc for two reasons: they're technically not include files, and some editors have syntax highlighting for .sma but not .inc.
The include files are located in include/amxmod_compat. The 1.76 compiler automatically searches the amxmod_compat include folder, so you do not need to move files around.