View Single Post
Author Message
Senior Member
Join Date: Sep 2015
Old 11-23-2016 , 13:27   AngelScript Mod Loader
Reply With Quote #1

For the past few days i've been working on a Metamod plugin that uses Angelscript as its scripting language. It's called the AngelScript Mod Loader, or ASMod for short.

Essentially this plugin is intended to be AMX Mod, only with Angelscript as its scripting language. There are multiple reasons why Angelscript is a good choice for this.

For starters, Angelscript binds directly to C++ code, which makes it more efficient to use than a language like Pawn, which uses cells. That requires wrapper functions in order to communicate between scripts and the C++ code it's accessing.

Second, it's object oriented. Types in C++ can be exposed to scripts exactly as they are. For example, entvars_t can be exposed as a reference counted type with properties; array properties are exposed using property getters and setters to provide a uniform representation of its members.

This makes using types more efficient and less error prone, since you can't pass the wrong enum value to a "pev" function by mistake.

And last but not least: since Sven Co-op also uses it, there is the potential to connect the two together and use the same script engine.

Which is exactly what i've done.

Using a few function addresses from Sven Co-op's server library, i was able to retrieve its script engine pointer. I then used this to add new API features to it.

As a test, i added my SQL API to it. This API was already integrated into HLEnhanced, though there are still a few things i want to take care of before calling it finished.

I modified one of my plugins to test it:
[email protected] g_pConnection = null;

void PluginInit()
    g_Module.ScriptInfo.SetAuthor( "Solokiller" );
    g_Module.ScriptInfo.SetContactInfo( "" );
    @g_pConnection = SQL::CreateMySQLConnectionWithDefaults();
    g_Game.AlertMessage( at_console, "SQL connection created: %1\n", g_pConnection !is null ? "yes" : "no" );
    g_pConnection.Query( "SELECT * FROM Test", MySQLQueryCompleted );

void MySQLQueryCompleted( [email protected] pQuery )
    g_Game.AlertMessage( at_console, "MySQL Query completed\n" );
This script compiles and runs just fine in Sven Co-op's plugin environment. My SQL code is accessed and ran as if it were a part of Sven Co-op's own code.

Memory allocation occurs on Sven Co-op's server library heap thanks to a few other functions that i retrieved from the library. This allows me to seamlessly integrate new features and make them function as though Sven Co-op's own code is responsible for it. I even added a new string type that converts to their string type as needed, using these memory allocation functions to ensure that the right heap is used.

Once i've reworked some of my utility code and added some features that i want added to the SQL API, i'll be able to not only provide a completely asynchronous MySQL and SQLite API, but also a new filesystem interface that allows write access, access to engine data that was not exposed in SC's API, like model data, as well as the ability to hook any engine or game interface function and overriding them if you so desire. Essentially, you'll be able to do what you can do in AMX Mod.

The entire Metamod plugin, as well as all of the modules that provide new features are all available in the Github repository, which can be found here:

I've only just gotten the essentials to work, so expect a lot of changes, but it's already off to a good start.
This plugin will eventually be able to function on its own as well, allowing you to use Angelscript in any GoldSource game that supports Metamod. Because of the module based approach to adding API functionality, individual games can have support for them implemented without any interference for other games, and shared code can be easily added.

In addition, i'll probably be adding Steamworks access to Metamod-P as well, which will come with new functions to hook into. For instance, a function that gets called when a player has been validated by Steam, which is something that has been requested by plugin authors to better handle the issue of players' Steam ID not being valid right away.

I'll be continuing work on this for a bit more to make it work standalone as well, which will also provide even more power to the Sven Co-op interop feature since it will let you write plugins that can access map script only features directly.
Things like custom entities are currently non-functional in plugins because (among other things) the access mask for those API features is set to map scripts only. All i have to do is provide the plugin module with an access mask that covers that mask and it will be able to access it.

All that said, this is just the beginning of this project. There is a lot to do before all of this becomes possible and stable, and efficiency is an important factor as well. And all of the new features would be useless without documentation to go with it. Fortunately i'll be able to provide that documentation on the wiki pages for the relevant projects.

Do keep in mind that it's a lot of work to build such an API, and when it comes to Sven Co-op the ability to access its data is dependent on them not changing how the implementation is structured.

If they choose to change it to break this plugin, then there is nothing i can do about it. However, considering how few updates their API has received in the past few months, i would strongly recommend supporting this plugin. I have provided an interface that games can export to more directly support this plugin:
All you have to do is implement it, and use interface.h's EXPOSE_SINGLE_INTERFACE macro to export the class so it can be retrieved by the plugin. It's trivially simple to implement:
class CASGameEnvironment final : public IASEnvironment
    asIScriptEngine* GetScriptEngine() override final { return gASManager()->GetScriptEngine(); }

    asALLOCFUNC_t GetAllocFunc() override final { return ::operator new; }

    asFREEFUNC_t GetFreeFunc() override final { return ::operator delete; }

    asALLOCFUNC_t GetArrayAllocFunc() override final { return ::operator new[]; }

    asFREEFUNC_t GetArrayFreeFunc() override final { return ::operator delete[]; }

    IASLogger* GetLogger() override final { return nullptr; }

This is specific to Sven Co-op, hence the gASManager reference. The logger is completely optional; it's there so games can control the logging behavior of the plugin and integrate it with their own logging features if needed. If it's null, it creates its own logger instead.

The interface might change in the future, but i doubt it's going to happen since this is all that's really needed to make an implementation work. If it does, and there is no way to make all users upgrade, i'll take advantage of CreateInterface's versioning feature and just make it fall back to loading this older version, assuming the changes aren't required to make it work properly. It's still in development, so i'd suggest waiting a bit.

I'd like to hear about people's opinions, any suggestions or feedback, ideas, etc. If you think i'm evil for doing this considering past events, you can say that too
Solokiller is offline