Vice Underdogs

Scripting => Script Showroom => Topic started by: stormeus on July 17, 2012, 06:31:37 am

Title: Hooking events in script releases
Post by: stormeus on July 17, 2012, 06:31:37 am
Just sharing a method I thought would be helpful to those who hadn't known about it before:

Squirrel compiles all scripts into optimized bytecode that can either be stored in-memory or flushed to disk and then read in memory later. When the compiler parses a script, it processes and (in dofile's case) runs everything inside a makeshift function called main().

While most people don't think about it, you can basically do this in any file you want:
Code: [Select]
function onServerStart()
{
    print( "Server started" );
}

// This would actually work
print( "myfile.nut parsed" );

In addition to that, functions are declared as variables, unlike Pawn. In fact, the Squirrel manual even makes mention of the ability to create "local functions," which I won't go into detail on since that's beside the point here. Essentially, creating a function such as function onServerStart() is syntactically the same as doing this:
Code: [Select]
onServerStart <- function()
{
    print( "Server started" );
}

Because of that, you can overwrite a function like so:
Code: [Select]
function onServerStart() { print( "You shouldn't be able to see this." ); }

onServerStart = function()
{
    print( "im in ur base, stealin ur funcshunz" );
}

// Or
function onOverwrittenServerStart()
{
    print( "1 2 3 4" );
}

// Comment this line out to test the first example
onServerStart = onOverwrittenServerStart;

Writing over a function doesn't necessarily remove it from memory, however. As long as a reference is kept to the original function, the original function can still be called. You can keep a reference by simply storing it in a global variable.
Code: [Select]
function onServerStart() { print( "aaaa" ); }

oldServerStart <- onServerStart;
onServerStart = function() { print( "bbbb" ); }

You can then call the old function since it still has a valid reference.
Code: [Select]
function onServerStart() { print( "aaaa" ); }

oldServerStart <- onServerStart;
onServerStart = function() { oldServerStart(); print( "bbbb" ); }

You can now use the above method to hook a script's events without having to manually edit scripts, functions, and commands. You can instead do something like this to add to someone's onPlayerCommand function without really having to worry about manual editing:
Code: [Select]
function onPlayerCommand( player, command, arguments )
{
    print( "hook-free" );
}

// In another file, probably
oldOnPlayerCommand <- null;

// Hook function
function myFunction( player, command, arguments )
{
    // Call old onPlayerCommand if needed
    if( oldOnPlayerCommand )
        oldOnPlayerCommand( player, command, arguments );

    // I dunno
    print( "nah" );
}

// Check if onPlayerCommand is defined
if( "onPlayerCommand" in getroottable() )
{
    // Store the old function
    oldOnPlayerCommand = onPlayerCommand;

    // Hook it
    onPlayerCommand = myFunction;
}
else
{
    // Define it
    onPlayerCommand <- myFunction;
}
Title: Re: Hooking events in script releases
Post by: morphine on July 17, 2012, 07:59:10 am
For the last part (hooking command functions specifically), I've always found it easier to do it this way:

Code: [Select]
function onPlayerOtherCommand( player, command, arguments )
{
    command = command.tolower();

    if( command == "noob" )
    {
        Message( "You are a noob." );
        return 1;
    }
}


function onPlayerCommand( player, command, arguments )
{
    command = command.tolower();

    if( command == "something" )
    {

      // idk
    }
    else if( onPlayerOtherCommand( player, command, arguments ) == 1 ) return 1;
}

Don't know if this is on-topic much but whatever. :)
Title: Re: Hooking events in script releases
Post by: Skirmant on July 17, 2012, 10:16:57 am
Interesting, but not insanely useful.
For most scripts a single function call is insufficient to trigger specific events. And editing unlike hooking is a constant solution :P
Title: Re: Hooking events in script releases
Post by: Thijn on July 17, 2012, 02:02:14 pm
Its interesting for sure. But like Skirmant said, i'm not really sure how to use this.
Like Morphine I mainly use different function names and call them all in the main event.
Title: Re: Hooking events in script releases
Post by: Charley on July 28, 2012, 09:32:31 am
I dunno I think this can be extremely useful. It would be good to have a lag free version of a script where each part/system is separated into its own file, instead of just being able to separate certain functions into their own file.
Title: Re: Hooking events in script releases
Post by: stormeus on July 28, 2012, 10:33:27 am
The basic idea is to be able to dynamically alter a server by simply loading a release script/snippet instead of manually editing files in order to do so. Rather than edit your onPlayerCommand function in order to add a command someone released, if the script author released their script this way, all you would have to do is run dofile( "myscript.nut" ) after you load all of your files and callbacks. Plus, it makes for good insight on how the Squirrel interpreter works.