Difference between revisions of "AMX Mod X 1.75 Scripting Changes"

From AlliedModders Wiki
Jump to: navigation, search
(Full Changelog: - slided this down a bit)
m (Module Requirement Syusstem)
 
(9 intermediate revisions by 3 users not shown)
Line 6: Line 6:
 
</pawn>
 
</pawn>
 
This means "require library".  Plugins still compiled with <tt>#pragma library</tt> will still fail on load if the given module is not found.  However, core will try to load each module given the rules below under "Automatic Module Loading".
 
This means "require library".  Plugins still compiled with <tt>#pragma library</tt> will still fail on load if the given module is not found.  However, core will try to load each module given the rules below under "Automatic Module Loading".
 
  
 
=Auto Plugin Files=
 
=Auto Plugin Files=
Line 80: Line 79:
  
 
==Usage==
 
==Usage==
Since SQLX is an expansive API, showing a single plugin of its usage would be difficult.  It is highly recommended that users interested in the new API look at the [http://cvs.tcwonline.org/viewvc.cgi/amxmodx/plugins/testsuite/sqlxtest.sma?view=log SQLxTest] plugin, which compares two different DBI methods and both SQL methods of querying a database.  It is highly useful for both regression testing and for getting an idea of how the API works.
+
Since SQLX is an expansive API, showing a single plugin of its usage would be difficult.  It is highly recommended that users interested in the new API look at the [http://svn.tcwonline.org/viewvc.cgi/trunk/plugins/testsuite/sqlxtest.sma?root=amxmodx SQLxTest] plugin, which compares two different DBI methods and both SQL methods of querying a database.  It is highly useful for both regression testing and for getting an idea of how the API works.
  
 
The most important concept of SQLX is "Handles," which are a precursor to a system planned for SourceMod.  Handles are datatypes that store internal information that you should not modify.  Whenever you create a handle, you must also free it, with <tt>SQL_FreeHandle</tt>().
 
The most important concept of SQLX is "Handles," which are a precursor to a system planned for SourceMod.  Handles are datatypes that store internal information that you should not modify.  Whenever you create a handle, you must also free it, with <tt>SQL_FreeHandle</tt>().
Line 96: Line 95:
 
*<tt>SQL_ThreadQuery</tt> - Places a query and connection info into a threaded queue.  In another thread, the connection is established, the query is executed, and the connection is dropped.  The query results are then posted back into the main thread and given to the plugin on the next server-frame.   
 
*<tt>SQL_ThreadQuery</tt> - Places a query and connection info into a threaded queue.  In another thread, the connection is established, the query is executed, and the connection is dropped.  The query results are then posted back into the main thread and given to the plugin on the next server-frame.   
 
**Note that while powerful, the mechanism is very simplistic.  Similar to <tt>set_task</tt>, you must differentiate multiple queries having the same callback by packing binary data into an array.  Furthermore, you can only make one query at a time, since the queue is "push one, resolve one, pop one."  If you plan on making five queries in a row in order to get aggregate information about a player, you must make each of these five queries in separate stages, and you must also take into account asynchronous factors such as the player dropping during the middle of a query.  (One way to do this is to pack the player's authid and client index into the callback data and verify it when the query finishes.)
 
**Note that while powerful, the mechanism is very simplistic.  Similar to <tt>set_task</tt>, you must differentiate multiple queries having the same callback by packing binary data into an array.  Furthermore, you can only make one query at a time, since the queue is "push one, resolve one, pop one."  If you plan on making five queries in a row in order to get aggregate information about a player, you must make each of these five queries in separate stages, and you must also take into account asynchronous factors such as the player dropping during the middle of a query.  (One way to do this is to pack the player's authid and client index into the callback data and verify it when the query finishes.)
 +
**Note that you should currently not call this native during <tt>plugin_end</tt>().  The backend threader freezes the query queue at this time and flushes all remaining queries back to the main thread.  It is likely your plugin will simply deadlock (freeze idly).  Even if this is corrected, there is no reason to use threaded queries in <tt>plugin_end</tt>() anyway, because all remaining threaded queries are executed as non-threaded before the mapchanges.
  
 
=New Natives / Native Changes=
 
=New Natives / Native Changes=
Line 155: Line 155:
 
*<tt>cs_set_user_zoom</tt> - Sets a zooming type on a player for any weapon.
 
*<tt>cs_set_user_zoom</tt> - Sets a zooming type on a player for any weapon.
 
*<tt>cs_get_user_zoom</tt> - Returns the zooming type of a player.
 
*<tt>cs_get_user_zoom</tt> - Returns the zooming type of a player.
 +
 +
==DoD==
 +
===Forwards===
 +
*<tt>dod_client_changeteam(id, team, oldteam)</tt> - This Forward is called when a player changes team, it contains the player id, the new team they have joined and the old team they were on.
 +
*<tt>dod_client_changeclass(id, class, oldclass)</tt> - This Forward is called just after player spawns if a player changes class, it contains the player id, the new class number and the old class number.
 +
*<tt>dod_client_spawn(id)</tt> - This Forward is called when a player spawns, it contains the player id.
 +
 +
===Stocks===
 +
*<tt>dod_set_model(id, model[])</tt> - Sets the model for a player, good for if you want to have special models.  The models used MUST be located in the following directory: dod/models/player/ And should look something like this: dod/models/player/models-name/model-name.mdl dod/models/player/models-name/model-nameT.mdl
 +
*<tt>dod_set_body_number(id, bodynumber)</tt> - Sets the model number for a players model
 +
*<tt>dod_clear_model(id)</tt> - Turns off the special model for a player
  
 
==New Stocks==
 
==New Stocks==
Line 250: Line 261:
 
** Added IsInWorld() stock (thanks Twilight Suzuka!).
 
** Added IsInWorld() stock (thanks Twilight Suzuka!).
 
** Added nvault_remove().
 
** Added nvault_remove().
 +
** Added emessage_begin()...emessage_end() natives for advanced messaging capabilities.
 
** Moved various natives from Engine and into Core:
 
** Moved various natives from Engine and into Core:
 
*** register_message
 
*** register_message
Line 276: Line 288:
 
** Fixed a bug where removing the current parent task could damage the task queue.
 
** Fixed a bug where removing the current parent task could damage the task queue.
 
** Fixed a bug in get_entity_visibility (thanks VEN!).
 
** Fixed a bug in get_entity_visibility (thanks VEN!).
 +
** Fixed some menu item calculation bugs on single paged menus.
 +
** Fixed CreateOneForward() not working as intended.
 
** Removed non-existant take_damage() entry.
 
** Removed non-existant take_damage() entry.
 
** Expanded XS Stock Library with addition of xs_vec_make2d.
 
** Expanded XS Stock Library with addition of xs_vec_make2d.
 
** Expanded usage of cs_set_user_vip().
 
** Expanded usage of cs_set_user_vip().
 
** Improved get_brush_entity_origin() and ViewContents (thanks Orangutanz!).
 
** Improved get_brush_entity_origin() and ViewContents (thanks Orangutanz!).
** Fixed some menu item calculation bugs on single paged menus.
+
** Added core natives:
** Fixed CreateOneForward() not working as intended.
 
** Added natives:
 
 
*** fputs()
 
*** fputs()
 
*** get_weaponid()
 
*** get_weaponid()
 
*** rename_file()
 
*** rename_file()
 
*** vformat()
 
*** vformat()
 +
 +
* DoD Changes:
 +
** Added forwards:
 +
*** dod_client_changeteam
 +
*** dod_client_changeclass
 +
*** dod_client_spawn
 +
** Added stocks:
 +
*** dod_set_model
 +
*** dod_set_body_number
 +
*** dod_clear_model
  
 
* Plugin Changes:
 
* Plugin Changes:
Line 325: Line 347:
  
  
[[Category:AMX Mod X]]
+
 
 +
[[Category:Scripting (AMX Mod X)]]

Latest revision as of 13:13, 22 December 2006

The AMX Mod X 1.75 release made important changes. While backwards compatibility was kept, intended functionality shifted in esoteric areas that will affect some plugins. Reading this article is highly recommended.

Module Requirement System

As part of the new Automatic Module Loading, the old #pragma library has been deprecated. You must now use:

#pragma reqlib <library>

This means "require library". Plugins still compiled with #pragma library will still fail on load if the given module is not found. However, core will try to load each module given the rules below under "Automatic Module Loading".

Auto Plugin Files

With the introduction of callfunc and register_native, many people now distribute plugins in large sets. To make this easier on users, you can now distribute an "extended plugin file." For example, say you have five plugins in a set. Normally, you would have to ask the user to write each name into plugins.ini, like so:

 file1.amxx   ;file1
 file2.amxx   ;file2
 file3.amxx   ;file3
 file4.amxx   ;file4
 file5.amxx   ;file5

With AMX Mod X 1.75, any file in the configs folder which follows a certain name pattern will be auto-loaded as an additional plugins.ini file. The pattern is: plugins-*.ini.

For example, you could distribute plugins-carmod.ini as:

;put a semi-colon to disable a plugin
cars_honda.amxx  
cars_toyota.amxx
cars_ford.amxx
cars_bmw.amxx
cars_gaben.amxx

This way, you can easily distribute an extractable install, rather than giving the user overcomplicated instructions. Furthermore, the file is easily disabled by simply modifying the name (for example, "disabled-carmod.ini").


Automatic Module Loading

Overview

As of AMX Mod X 1.75, there is a new, powerful automatic module loading system. That means that modules.ini is largely deprecated for general use. Instead, modules are loaded as plugins need them. This is done dynamically by core before plugins are even loaded, without the need to patch modules.ini and then change the map.

Modules can now define "Libraries" and "Library Classes". A library is a specific identifier that should match the filename of the module. For example, Engine's defined library is "engine". FakeMeta's library is "fakemeta", and so on.

A "library class" defines membership to a set of modules. For example, CSX has the library name "csx", but it is part of the library class "xstats". Library classes are also used for DBI and SQLX. This is very useful for being able to require one module type of any implementation.

New API

Compiler Pragmas

This is expanded with a number of new #pragma directives:

  • #pragma reqlib <library> - Requires that a given library must be loaded.
  • #pragma reqclass <libclass> - Requires that a given library class must be loaded.
  • #pragma loadlib <library> - Automatically attempts to load a given library (see more info below).
  • #pragma expectlib <library1> <library2> - If the first library is not loaded, the second one will be attempted to load (not very useful).
  • #pragma expectclass <class> <library> - If the expected class is not found, the given library will be attempted to load. This is useful for defining a default module to be loaded with a given class of modules.
  • #pragma defclasslib <class> <library> - Same as expectclass, however, defclasslib waits until all expectations are resolved. This lets plugins override defaults by adding their own expectations.

Module Filtering

The module_filter prototype now includes a second parameter, which tells you whether the requirement is a class or library.

Furthermore, module_exists has been deprecated for LibraryExists().

How it Works

The precise order of events is as follows:

  • When the first entity is spawned, AMX Mod X loads all unloaded modules in modules.ini.
  • After modules.ini is parsed, plugins.ini is read. Each file is mapped into a cache.
  • The cache is previewed.
    • First, the "library" table is read. This table is read for backwards compatibility with AMX Mod X 1.71 and prior. Each entry is read as a module file shortname, and the module is loaded if it exists.
    • Next, the "pubtags" table is read. Each entry is decoded to one of the special #pragma commands.
      • All loadlib commands are executed, and the modules loaded.
      • All expect commands are executed.
      • All defclasslib commands are executed.
  • AMX Mod X then waits until ServerActivate is called.
  • All plugins are loaded. If the plugin is in the cache, the cache is read instead. For each plugin...
    • The library table is read. For each library that is both nonexistant and unhandled by a module filter, the plugin fails to load.
    • The pubtags table is read. For each reqlib and reqclass entry that are both nonexistant and unhandled by a module filter, the plugin fails to load.
  • The plugin cache is invalidated and the server is considered "loaded".


SQLX

Introduction

SQLX is a new Database API that supercedes DBI. Its main feature is that you can load two SQLX modules at once, whereas you cannot with DBI. It also supports threaded queries, which let you process data without interrupting gameplay from a bad network connection.

These additions come at a hefty price. The SQLX API is significantly more complex than DBI. Although some might find it easier to use due to its simpler error checking and iteration, it requires more manual memory management and has less simple abstraction. Furthermore, taking advantage of the new SQL_ThreadQuery native will require nothing short of a rewrite for most plugins, as it is asynchronous instead of synchronous. Because of this, the new SQLX modules also implement the old DBI functionality, for both backwards compatibility and for plugin developers familiar with the old API. All three APIs - SQL-threaded, SQL-non-threaded, and DBI, are still fully supported.

Usage

Since SQLX is an expansive API, showing a single plugin of its usage would be difficult. It is highly recommended that users interested in the new API look at the SQLxTest plugin, which compares two different DBI methods and both SQL methods of querying a database. It is highly useful for both regression testing and for getting an idea of how the API works.

The most important concept of SQLX is "Handles," which are a precursor to a system planned for SourceMod. Handles are datatypes that store internal information that you should not modify. Whenever you create a handle, you must also free it, with SQL_FreeHandle().

Native Overview

The basic natives of SQLX are:

  • SQL_MakeDbTuple (or simple stock version, SQL_MakeStdTuple) - This creates a variable that holds information about a database. It does not connect to the database. This is so you don't have to keep retrieving cvar info on every connection. You do not have to free these handles, although it is a good idea if you create them dynamically.
  • SQL_Connect - Connects to a database and returns a new Handle, or Empty_Handle on failure.
  • SQL_PrepareQuery - Prepares a query for execution, and returns a new Handle to the query.
  • SQL_Execute - Executes a prepared query, and returns 0 on failure (1 on success).
  • SQL_MoreResults - Returns 1 if there are more results in the query result queue, 0 if none.
  • SQL_ReadResult - Reads the current row result by the column's numerical index, similar to dbi_field/dbi_result.
  • SQL_NextRow - Advances to the next result row. Compatibility Warning: This does not need to be called first! Unlike dbi_nextrow, the query is automatically at the first row. If you call SQL_NextRow before SQL_ReadResult, your are actually skipping a row.
  • SQL_FreeHandle - Frees a Handle. You must do this or else memory will leak.
  • SQL_ThreadQuery - Places a query and connection info into a threaded queue. In another thread, the connection is established, the query is executed, and the connection is dropped. The query results are then posted back into the main thread and given to the plugin on the next server-frame.
    • Note that while powerful, the mechanism is very simplistic. Similar to set_task, you must differentiate multiple queries having the same callback by packing binary data into an array. Furthermore, you can only make one query at a time, since the queue is "push one, resolve one, pop one." If you plan on making five queries in a row in order to get aggregate information about a player, you must make each of these five queries in separate stages, and you must also take into account asynchronous factors such as the player dropping during the middle of a query. (One way to do this is to pack the player's authid and client index into the callback data and verify it when the query finishes.)
    • Note that you should currently not call this native during plugin_end(). The backend threader freezes the query queue at this time and flushes all remaining queries back to the main thread. It is likely your plugin will simply deadlock (freeze idly). Even if this is corrected, there is no reason to use threaded queries in plugin_end() anyway, because all remaining threaded queries are executed as non-threaded before the mapchanges.

New Natives / Native Changes

Register Message

The register_message set of natives, including get/set_msg_block, has been moved to Core. This is to facilitate users who prefer to use FakeMeta, and like the simplicity and speed of using Engine's message interception functions.
This change is backwards compatible.

Argument Formatting

The format_args function is now replaced with a much faster, more compatible vformat function. Its usage is slightly different (read the include file, string.inc), but it accepts %L, whereas format_args does not. A quick example:

debugprint(const fmt[], ...)
{
   static temp[2048]
   vformat(temp, sizeof(temp)-1, fmt, 2)
   log_message("[DEBUG] %s", temp)
}

Other Core Natives

  • register_plugin - Now returns a plugin id.
  • get_amxx_verstring - Returns the AMX Mod X version string.
  • get_weaponid - Gets a weapon id from a weapon name.

FakeMeta

New Natives

  • get_orig_retval - Gets the original return value of an engine or game DLL function.
  • copy_infokey_buffer - Copies the given infobuffer pointer into output buffer.
  • get/set_cd - Gets or sets members of a clientdata data structure (used with UpdateClientData).
  • get/set_es - Gets or sets members of an entity_state data structure (used with AddToFullPack).
  • get/set_uc - Gets or sets members of a usecmd data structure (used with CmdStart).

New Engine/GameDLL Functions

The register_forward native now allows for hooking a number of new functions from the engine or game DLL including:

  • UpdateClientData
  • AddToFullPack
  • CmdStart
  • CmdEnd
  • CreateInstBaselines - Game DLL function
  • CreateInstBaseline - Engine function
  • CreateBaseline
  • GetInfoKeyBuffer
  • AlertMessage - This now can be called via engfunc
  • ClientPrintf

These functions can also be called via engfunc or dllfunc.

Breaking Changes

Using engfunc in order to call EngFunc_InfoKeyValue, EngFunc_SetKeyValue, or EngFunc_SetClientKeyValue now requires passing an infobuffer pointer. An infobuffer pointer can be obtained by calling EngFunc_GetInfoKeyBuffer. For example:

some_function(id, name[])
{
   new infokey = engfunc(EngFunc_GetInfoKeyBuffer, id)
   engfunc(EngFunc_SetClientKeyValue, id, infokey, "model", name)
}

If you hook ClientUserInfoChanged via Fakemeta, an infobuffer pointer is now also forwarded to your function in addition to the client id.

Cstrike

  • cs_get_user_mapzones - Returns bitwise flags of where on the map a player is located such as buy zone, bomb site, hostage rescue zone, VIP safety zone, and escape zone.
  • cs_set_user_vip - Now takes two additional (but optional) parameters for determining whether or not the player model and scoreboard get updated.
  • cs_set_user_zoom - Sets a zooming type on a player for any weapon.
  • cs_get_user_zoom - Returns the zooming type of a player.

DoD

Forwards

  • dod_client_changeteam(id, team, oldteam) - This Forward is called when a player changes team, it contains the player id, the new team they have joined and the old team they were on.
  • dod_client_changeclass(id, class, oldclass) - This Forward is called just after player spawns if a player changes class, it contains the player id, the new class number and the old class number.
  • dod_client_spawn(id) - This Forward is called when a player spawns, it contains the player id.

Stocks

  • dod_set_model(id, model[]) - Sets the model for a player, good for if you want to have special models. The models used MUST be located in the following directory: dod/models/player/ And should look something like this: dod/models/player/models-name/model-name.mdl dod/models/player/models-name/model-nameT.mdl
  • dod_set_body_number(id, bodynumber) - Sets the model number for a players model
  • dod_clear_model(id) - Turns off the special model for a player

New Stocks

Engine

  • IsInWorld - Checks if an entity is within the bounds of the world (from HLSDK).

FakeMeta

  • DF_UpdateClientData - Calls UpdateClientData game DLL function.
  • DF_AddToFullPack - Calls AddToFullPack game DLL function.
  • DF_CmdStart - Calls CmdStart game DLL function.
  • DF_CmdEnd - Calls CmdEnd game DLL function.
  • DF_CreateBaseline - Calls CreateBaseline game DLL function.
  • DF_CreateInstBaselines - Calls CreateInstancedBaselines game DLL function.
  • EF_CreateInstBaseline - Calls CreateInstancedBaseline engine function.
  • EF_GetInfoKeyBuffer - Calls GetInfoKeyBuffer engine function.
  • EF_ClientPrintf - Calls ClientPrintf engine function.

New Constants

Various sound constants (SND_SPAWNING, SND_STOP, SND_CHANGE_VOL, and SND_CHANGE_PITCH) from the HL SDK as well as a constant for pi were added to amxconst.inc. TE_* (temp entity) message constants have also been added which will automatically be included with #include <amxmodx>. And finally, a new hlsdk_const.inc file has been added that contains many more constants from the SDK. This is automatically included with #include <engine> or #include <fakemeta>.


Module API

The AMX Mod X module API received a small overhaul for 1.75.

Versioning/Interface Additions

The internal module interface version is 4. However, modules from M/SDK 3 will still load.

M/SDK Version 4 modules have two new members of public information: the library string, and the library class string. These contain comma delimited names of whatever libraries or library classes the module would like to be registered.

For backwards compatibility, M/SDK Version 4 modules have an automatically empty library class string, and a library string equal to the logtag string.

New Callbacks

Modules can now be informed of when plugins are about to be unloaded, and when plugins have been fully unloaded. This means that modules don't have to hook ServerActivate and ServerActivate_Post (implementation dependent), don't need to be loaded by Metamod, and don't need to detach simply to release resources.

New Functions

  • MF_GetLocalInfo - Intended for modules using LOCALINFO, which required a Metamod attachment. This is equal to AMX Mod X's core function get_localinfo.
  • MF_OverrideNatives - Given a native list, this specifies that any other module already providing these natives will no longer provide them. This was added to force backwards compatibility between SQLITE and MySQLX. Usage is not recommended.
  • MF_FindLibrary - Essentially the same function as LibraryExists() for plugins.
  • MF_AddLibraries - Adds a comma delimited list of libraries or library classes. You must specify a "parent" pointer that identifies the module. This is easily accomplished by taking the address of any static variable.
  • MF_RemoveLibraries - Removes all library entries with the given parent pointer. This is useful if your module adds custom entries not in its defined list.

MSVC8 Compatibility

M/SDK Version 4 now contains preprocessor definitions for compatibility with Microsoft's Visual Studio 2005/8.0 (provided by Damaged Soul). These macros can be turned off in the moduleconfig.h file by uncommenting the definition of NO_MSVC8_AUTO_COMPAT.


Minor Changes

Compiler Defines

Since __DATE__ was fixed in 1.75, the macro __TIME__ was also added.


Full Changelog

This changelog is tentative. Developers will be editing it as things are changed.

  • Core Changes
    • Modules are now automatically loaded by detecting plugin usage.
      • modules.ini is now primarily deprecated, except for SQL and unsupported libraries.
    • Rewrote SQLX modules (again). You can now load multiple SQL modules and toggle them per-plugin.
      • Added new SQL API called SQLX. DBI was kept backwards-compatible.
      • Added new function to thread SQL queries (which prevents lag from a bad connection).
    • Fixed some very small memory leaks.
    • Fixed binary logging building wrong on Linux.
    • Fixed some debugging crashes.
    • Fixed serious bug when creating plugin_pause/plugin_unpause forwards.
    • Fixed serious crash bug involving set_hudmessage and plugins using high hudchannels.
    • Any file in the configs folder starting with "plugins-" and ending in ".ini" will now be treated as an extended plugins file. This is useful for distributing sets of plugins separately, and easily disabling them with a name change.
  • Fakemeta Changes
    • Added new hookable/callable functions:
      • AddToFullPack
      • AlertMessage
      • ClientPrintf
      • ClientUserInfoChanged
      • CmdEnd
      • CmdStart
      • CreateBaseline
      • CreateInstancedBaseline
      • GetInfoKeyBuffer
      • InfoKeyValue
      • UpdateClientData
      • SetClientKeyValue
      • SetKeyValue
    • Added new natives:
      • copy_infokey_buffer
      • get_orig_retval
    • Fixed various trace bugs in FakeMeta with certain natives.
    • Fixed some unhooking on server-deactivate problems in FakeMeta.
  • Scripting Changes:
    • Added __DATE__ and __TIME__ auto-macros to the compiler.
    • Added many HLSDK constants and improved include organization.
    • Added new compiler #pragma directives for using the module autoloading system.
    • Added cs_get_user_mapzones (thanks VEN!).
    • Added cs_set_user_zoom.
    • Added cs_get_user_zoom.
    • Added IsInWorld() stock (thanks Twilight Suzuka!).
    • Added nvault_remove().
    • Added emessage_begin()...emessage_end() natives for advanced messaging capabilities.
    • Moved various natives from Engine and into Core:
      • register_message
      • get_msg_args
      • get/set_msg_arg_type
      • get/set_msg_arg_int
      • get/set_msg_arg_float
      • get/set_msg_arg_string
      • get_msg_origin
      • get/set_msg_block
      • velocity_by_aim
      • vector_to_angle
      • angle_vector
      • vector_length
      • vector_distance
      • precache_generic
    • Dynamic natives that are paused now also pause parent plugins (with an error).
    • register_plugin() now returns the plugin ID.
    • Fixed a corruption bug in strip_user_weapons().
    • Fixed a bug in set_view() (thanks jtp10181!).
    • Fixed is_in_viewcone() always returning 0.
    • Fixed is_visible causing a crash, improved accuracy.
    • Fixed a crash bug in get_tr2() (thanks Orangutanz!).
    • Fixed cs_get_user_buyzone() returning existence in other areas.
    • Fixed a rare floatround() bug where values ending strictly in .5 were IEEE rounded.
    • Fixed a bug where removing the current parent task could damage the task queue.
    • Fixed a bug in get_entity_visibility (thanks VEN!).
    • Fixed some menu item calculation bugs on single paged menus.
    • Fixed CreateOneForward() not working as intended.
    • Removed non-existant take_damage() entry.
    • Expanded XS Stock Library with addition of xs_vec_make2d.
    • Expanded usage of cs_set_user_vip().
    • Improved get_brush_entity_origin() and ViewContents (thanks Orangutanz!).
    • Added core natives:
      • fputs()
      • get_weaponid()
      • rename_file()
      • vformat()
  • DoD Changes:
    • Added forwards:
      • dod_client_changeteam
      • dod_client_changeclass
      • dod_client_spawn
    • Added stocks:
      • dod_set_model
      • dod_set_body_number
      • dod_clear_model
  • Plugin Changes:
    • Added amx_showrcon command to show rcon results in admincmd.sma.
    • Added weapon restriction compatibility for bots in CS (KWo).
    • Added admin identifier (asterisk which is red if colors are available) to all menus with a user list.
    • Upgraded admin.sma to the new SQLX API.
    • Fixed a bug in amx_reloadadmins not giving admins new access.
    • Fixed a weapon restriction exploit in restmenu.sma.
    • Fixed an HTML exploit in /top15 displaying player names.
    • Fixed various and plentiful amx_addadmin bugs.
    • Fixed telemenu.sma not letting you teleport yourself (thanks jtp10181!).
    • Fixed a bug with adminslots.sma lowering the slots by 1.
    • Fixed overlapping hud messages with miscstats.
  • Module API Changes:
    • Added and fixed module API for inter-module communication.
    • Added native overriding and replacing to module API.
    • Added plugin unloading/unloaded callbacks to module API.
    • Added MSVC8 project files to most CVS projects.
  • Configuration Changes:
    • Added amx_sql_type cvar to sql.cfg.
    • Re-organized and simplified modules.ini.
    • Error logging can now be redirected to separate logs.
    • Fixed modules.ini parsing bugs and simplified parsing.
    • Fixed a double entry in cvars.ini.
  • Other Changes:
    • Added a Bulgarian translation (thanks lubb!).
    • Added a leetspeak translation (thanks, I think, Twilight Suzuka!).
    • Improved FTP option of the graphical installer.
    • Updated the GeoIP library to June.
    • Updated PCRE from v6.1 to v6.4.
    • Updated sqLite from 3.3.4 to 3.3.5.
    • Fixed a steam account path bug in the installer.