Game Development Community

dev|Pro Game Development Curriculum

Edit script files and functions during run-time

by Phillip O'Shea · 08/20/2008 (6:22 am) · 6 comments

Open up your "console/consoleFunctions.cc" file and locate the console function "exec". You'll need to add a callback for this function to notify us of a newly executed script file.

ConsoleFunction(exec, ... )
{
    ...
    
    // ScriptUpdate Resource {
    if (ret)
        Con::executef(2, "onExec", scriptFileName);
    // }
    
    delete [] script;
    execDepth--;
    return ret;
}

Next, open up your "console/fileSystemFunctions.cpp" file and paste this new function in there somewhere (I put mine after the "fileSize" function).

ConsoleFunction(fileTime, S32, 2, 2, "(fileName)\n"
                                    "@param fileName The name of the file to check.\n" 
                                    "@return Returns the time of the last modification")
{
    argc;

    // Grab the full file address
    Con::expandScriptFilename(scriptFilenameBuffer, sizeof(scriptFilenameBuffer), argv[1]);
     
    // Return the time
    FileTime pModifyTime;
    if (Platform::getFileTimes(scriptFilenameBuffer, NULL, &pModifyTime))
        return (S32)pModifyTime.v1;
    
    return 0;
}

Finally, you need to add this package to your project. I placed mine in the root "main.cs" file (the first file to be called when the project is executed).

package ScriptUpdate
{
    function onExec(%fileName)
    {
        for (%i = 0; %i < $ScriptUpdate::Count; %i++)
        {
            if (getField($ScriptUpdate::Info[%i], 0) $= %fileName)
            {
                $ScriptUpdate::Info[%i] = trim(%fileName TAB fileTime(%fileName));
                return;
            }
        }
        
        $ScriptUpdate::Info[$ScriptUpdate::Count] = trim(%fileName TAB fileTime(%fileName));
        $ScriptUpdate::Count += 1;
    }
    
    function updateScripts(%val)
    {
        if (%val)
        {
            echo ("Updating Scripts...");
            
            for (%i = 0; %i < $ScriptUpdate::Count; %i++)
            {
                %fileName = getField($ScriptUpdate::Info[%i], 0);
                %fileTime = getField($ScriptUpdate::Info[%i], 1);
                
                if (fileTime(%fileName) != %fileTime)
                    exec (%fileName);
            }
        }
    }
};

//
activatePackage( ScriptUpdate );

Bind the function to a key for ease of use.

moveMap.bind( "keyboard", "ctrl l", "updateScripts" );

That is it!

When you load your game, it will store all of the files that you execute and the last time they were modified. If you then call the function "updateScripts()" during the game, it will re-execute any files that have been modified.

This little idea was inspired by the Plastic Tweaker resource from Plastic Games. I strongly recommend that you check it out!

About the author

Head of Violent Tulip, a small independent software development company working in Wollongong, Australia. Go to http://www.violent-tulip.com/ to see our latest offerings.


#1
08/20/2008 (7:36 am)
Sounds good
#2
08/20/2008 (9:18 am)
Philip, this is a very neat inspiration! This resource would have fit perfectly all the other gems PG posted.

I am very curious about the Plastic Tweaker, but I'll only check it out once it's out of beta and selling. Until then, this resource will be a great aid. Thank you!
#3
08/20/2008 (2:02 pm)
Sounds very cool, one thing though: Shouldn't updateScripts() be updateScripts(%val)?
#4
08/20/2008 (2:04 pm)
Thanks Nathan!
#5
08/20/2008 (2:35 pm)
Brilliant! Thanks a ton.
#6
08/21/2008 (1:57 am)
Thank you, a very helpful resource. I had a few issues I'm not sure are common and I've yet to test them on windows, but to get this working in Linux TGE 1.5.2 I had to make a couple changes:

1. First, all file changes were made in consoleFunctions.cc, there was no console/fileSystemFunctions.cpp for me?
2. The pModifyTime is only a struct for win32 it appears, so using the v1 field for that was an issue, I wrapped the return in an ifdef for that

ConsoleFunction(fileTime, S32, 2, 2, "(fileName)\n"
                                    "@param fileName The name of the file to check.\n"
                                    "@return Returns the time of the last modification")
{
    argc;

    // Grab the full file address
    Con::expandScriptFilename(scriptFilenameBuffer, sizeof(scriptFilenameBuffer), argv[1]);

    // Return the time
    FileTime pModifyTime;
    if (Platform::getFileTimes(scriptFilenameBuffer, NULL, &pModifyTime))
    {
#ifdef TORQUE_OS_WIN32
        return (S32)pModifyTime.v1;
#else
        return (S32)pModifyTime;
#endif
    }
    return 0;
}

3. The fileTime comparison wasn't working for me until I used !$= instead of != . Not sure if that's due to how Linux returns the values.
4. I had to actually make sure there was a fileName, or we end up trying to execute a nonexistent file

if (%fileName !$= "")
                {
	                if (fileTime(%fileName) !$= %fileTime)
	                {
	                    exec (%fileName);
	                }
                }

That was it, everything is working great now, will build a Windows build tomorrow to see if it messed anything.