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

From AlliedModders Wiki
Jump to: navigation, search
(Upgraded Systems: - newmenu stuff)
(New Menus)
Line 174: Line 174:
 
     if (item == MENU_EXIT)
 
     if (item == MENU_EXIT)
 
     {
 
     {
 +
        //Note that you must remember to destroy the menu here, since
 +
        // you won't hit the one below.
 
         menu_destroy(menu);
 
         menu_destroy(menu);
 
         return PLUGIN_HANDLED;
 
         return PLUGIN_HANDLED;

Revision as of 02:34, 1 March 2006

This article goes over the new scripting changes available in AMX Mod X version 1.70.

New Systems

These are entirely new categories of functions added to AMX Mod X in 1.70.

CVAR Pointers

CVAR pointers are a new method of getting/retrieving CVAR values. It is a much faster method than using the get/set_cvar functions. The difference is instead of passing a "cvar name", you pass the "cvar pointer", which is a direct memory access rather than a string lookup. In order to easily accomodate this, register_cvar now returns a CVAR pointer from the CVAR you created. You can also use get_cvar_pointer. Usage is mapped as such:

  • get_cvar_flags -> get_pcvar_flags
  • set_cvar_flags -> set_pcvar_flags
  • get_cvar_string -> get_pcvar_string
  • set_cvar_string -> NONE (not implemented)
  • get_cvar_num -> get_pcvar_num
  • set_cvar_num -> set_pcvar_num
  • get_cvar_float -> get_pcvar_float

An example of old code might be:

#include <amxmodx>
 
public plugin_init()
{
   register_cvar("csdm_active", "1")
}
 
public pfn_touch()
{
   if (!get_cvar_num("csdm_active"))
      return PLUGIN_CONTINUE
}

To use CVAR pointers and optimize your code:

#include <amxmodx>
 
new g_csdm_active
 
public plugin_init()
{
   g_csdm_active = register_cvar("csdm_active", "1")
}
 
public pfn_touch()
{
   if (!get_pcvar_num(g_csdm_active))
      return PLUGIN_CONTINUE
}

Event Forwarding

One of AMX Mod X's first advanced API additions was "callfunc", which let plugins intercommunicate. This expanded with the "module forward API", then to "dynamic natives". Now, AMX Mod X has a "plugin forward API". This lets you call a public function in all plugins at once, which is very useful for global event/messaging creation. The new natives are:

  • CreateMultiForward - Creates a global forward to a public function in all plugins.
  • CreateOneForward - Creates a single, per-plugin forward to a public function.
  • PrepareArray - Prepares an array to be passed into a forward.
  • ExecuteForward - Executes a forward, calling all the public functions it contains.
  • DestroyForward - Removes a forward from memory.

An example of creating a forward is below. This plugin creates the forward, and fires it when the command is typed.

#include <amxmodx>
 
new g_fwd_what
 
public plugin_init()
{
    register_plugin("forward test", "1.0", "BAILOPAN")
    register_srvcmd("fwd_test", "Command_FwdTest")
 
    g_fwd_what = CreateMultiForward("Event_What", ET_STOP, FP_STRING, FP_CELL, FP_ARRAY)
}
 
public Event_What(str[], d, arr[])
{
    server_print("Event what called with (%s) num = %d|%d (should be 5|37)", str, d, arr[d])
 
    return PLUGIN_CONTINUE
}
 
public Command_FwdTest()
{
    new array[6], ret
    array[5] = 37
 
    new pArray = PrepareArray(array, 6)
    if (!ExecuteForward(g_fwd_what, ret, "gaben", 5, pArray))
    {
        server_print("FAILED!")
    }
}

Fast File Natives

One of the biggest flaws in the original AMX Mod core is the tendency to replace direct access handles with bad abstraction layers. Write_file and read_file are examples of this. To read or write line N to a file, they must open the file, seek to line N, then close it. For reading sequentially, this is an O(n^2) operation -- very slow!

These have been made obsolete with:

  • fopen - Opens and returns a file handle on success.
  • fclose - Closes a file handle.
  • feof - Checks for end of file.
  • fprintf - Writes to a file.
  • fgets - Reads text from a file.
  • fseek - Seek to a position in a file.
  • ftell - Get the current position of a file.
  • fread/fread_blocks/fread_raw - Read binary data from a file.
  • fwrite/fwrite_blocks/fwrite_raw - Write binary data to a file.

An example of reading a file with the new system:

#include <file>
 
stock CopyTextFile(const infile[], const outfile[])
{
   new infp = fopen(infile, "rt")    //read text
   new outfp = fopen(outfile, "wt")  //write text
 
   if (!infp || !outfp)
      return 0
 
   new buffer[2048]
   while (!feof(infp))
   {
      fgets(infp, buffer, 2047)
      fprintf(outfp, "%s", buffer)
   }
   fclose(infp)
   fclose(outfp)
}

Note that fgets includes a newline if one is reached. A quick way to strip newlines is:

new len = strlen(string)
if (string[len-1] == '^n')
   string[--len] = 0

Upgraded Systems

A few systems in AMX Mod X 1.70 were either slightly or completely overhauled.

New Menus

The major changes included in the "new newmenu" system:

  • menu_setprop - You can now set per-menu properties.
    • MPROP_PERPAGE - Sets the level of pagination.
    • MPROP_BACKNAME - Sets the text for the "Back" button.
    • MPROP_NEXTNAME - Sets the text for the "More" button.
    • MPROP_EXITNAME - Sets the text for the "Exit" button.
    • MPROP_TITLE - Sets the menu title.
    • MPROP_EXIT - Controls how "Exit" buttons are displayed.
    • MPROP_ORDER - Sets menu item order.
    • MPROP_NOCOLORS - Forces no colors.
    • MPROP_PADMENU - Sets how items/spaces are padded.
  • menu_destroy - Menus are now destroyable.
  • Menus calculate items correctly.
  • Menu callbacks no longer send MENU_BACK or MENU_MORE, it is handled internally.
  • Menu callbacks will received MENU_EXIT if the player disconnects or is sent another menu.
  • Menu callbacks are guaranteed to be called in any situation (so you can destroy them).
  • menu_addblank - Menus can have blank entries.
  • player_menu_info - Newmenu synchronization.

An example of using destroyable, per-player newmenus is below:

#include <amxmodx>
 
public plugin_init()
{
    register_plugin("forward test", "1.0", "BAILOPAN"); 
    register_clcmd("menu_test", "Command_MenuTest");  
}
 
public gaben_handler(id, menu, item)
{
    client_print(0, print_chat, "Player %d selected menu %d item %d", id, menu, item);
    if (item == MENU_EXIT)
    {
        //Note that you must remember to destroy the menu here, since 
        // you won't hit the one below.
        menu_destroy(menu);
        return PLUGIN_HANDLED;
    }
 
    new cmd[6], iName[64];
    new access, callback;
 
    menu_item_getinfo(menu, item, access, cmd,5, iName, 63, callback);
 
    client_print(0, print_chat, " ---> Evaluated to: %s[%s]", iName, cmd);
 
    menu_destroy(menu);
 
    return PLUGIN_HANDLED;
}
 
public Command_MenuTest(id)
{
    new menu = menu_create("Gaben?", "gaben_handler");
    menu_additem(menu, "1", "1", 0);
    menu_addblank(menu);
    menu_additem(menu, "", "2", 0);
    menu_additem(menu, "3", "3", 0);
    menu_additem(menu, "4", "4", 0);
    menu_additem(menu, "6", "6", 0);
    menu_additem(menu, "7", "7", 0);
    menu_additem(menu, "8", "8", 0);
    menu_additem(menu, "9", "9", 0);
    menu_setprop(menu, MPROP_PERPAGE, 3);
    menu_setprop(menu, MPROP_EXIT, MEXIT_ALL);
    menu_setprop(menu, MPROP_NEXTNAME, "Next");
 
    menu_display(id, menu, 0);
}