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:
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:
onServerStart <- function()
{
print( "Server started" );
}
Because of that, you can overwrite a function like so:
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.
function onServerStart() { print( "aaaa" ); }
oldServerStart <- onServerStart;
onServerStart = function() { print( "bbbb" ); }
You can then call the old function since it still has a valid reference.
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:
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;
}