Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
115 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Introduction to GameMonkey Script Part 2
Embedding GameMonkey


Executing a Script from File

In order to overcome the need for hard-coded scripts, it is beneficial to load the scripted data from a file. However, you may notice that the gmMachine object has no member function for loading scripts from a file resource. To some, this omission may seem to be a startling oversight, but it becomes understandable when you appreciate the context that the GameMonkey Script environment is intended for - games! Games often have many weird and wonderful methods of loading file resources; some choose straight file IO, others use a zip-like format, others have encryption and some even use a network layer to receive the game content. In this context, a single LoadScript member function is fairly pointless as the majority of games embedding GM would have little use for it.

The basic method of loading a script from file is as follows:

  1. Locate the file resource
  2. Open the file resource
  3. Obtain the size of the data to load
  4. Allocate enough memory to load the data plus one byte for the zero terminator
  5. Load the data from file into the allocated memory
  6. Close the file
  7. Compile the script from the loaded data
  8. Free up the allocated memory containing the script

It is worth bearing in mind that steps 3, 4 and 5 can be combined into a single step because we can use ifstream and string from the C++ standard library, however I have chosen to separate out the steps should you be using a non-C++ library such as PhysicsFS or the legacy C FILE functions. With this in mind, it is simple to write our own simple implementation based on the C++ standard library.

#include <fstream>
#include <string>
#include <iterator>

int gmLoadAndExecuteScript( gmMachine &a_machine, const char *a_filename )
{
    std::ifstream file(a_filename);
    if (!file)
        return GM_EXCEPTION;
	
    std::string fileString = std::string(std::istreambuf_iterator<char>(file),
                                         std::istreambuf_iterator<char>());
    file.close();
    return a_machine.ExecuteString(fileString.c_str());
}

Example: loading_scripts_1.cpp

If you now integrate this code with the simple example code shown previously, you should now be able to load and execute scripts from a text file. GameMonkey scripts usually have a *.gm, extension, but this is obviously not required.

#include "gmThread.h"

int main()
{
	gmMachine gm;

	// Execute a simple script file
	gmLoadAndExecuteScript ( gm, "scripts/hello.gm" );

	return 0;
}

Basic Error Checking

Now that your scripts are getting more complex you may begin to experience accidental syntax errors which would cause the script compilation to fail. It is extremely important that you handle at least the basic errors that come from bad script compilation. The gmMachine object maintains an internal log of the last errors in the compilation process which will help you catch these errors should they occur.

The basic way of checking an error is to catch the return from the ExecuteString member function. If the value is zero, there is no error and you can proceed as normal. However if the value is non-zero you have an error and should handle it somehow. Simply knowing that there's an error is useless in helping you track it down, so GameMonkey gives you access to the error log for your own use. You can access the compilation log using the GetLog member function of the gmMachine object. From the retrieved gmLog object you can access the GetEntry function, which allows you to iterate through the errors the log contains, popping them from the list when you're done.

#include "gmThread.h"

void handleErrors( gmMachine &a_machine )
{
    gmLog &log = a_machine.GetLog();
    // Get the first error
    bool firstError = true;
    const char *err = log.GetEntry( firstError );
    while ( err )
    {
        std::cout << "Compilation error: -" << err << std::endl;
        err = log.GetEntry( firstError );
    }
}

Example: error_handling.cpp

In the code above you will notice the use of the firstError variable; this is used internally by GM Script to control the one-way iteration over the log entries. You must be set the variable to true for the first call to the function; it will be set to false on subsequent calls.

int main()
{
    const char *myScript = "fruits = table ( \"apple\", \"pear\", \"orange\" ); "
                           "foreach ( frt in fruits ) { print(frt);	} ";

    gmMachine gm;

    // Execute a simple script
    int ret = gm.ExecuteString( myScript );
    if ( ret != 0 )
    {
        // There were errors in the script, exit gracefully
        handleErrors( gm );
        return 1;
    } 

    return 0;
}

Example: error_handling.cpp

Now that you know how to handle script compilation errors I will set you the task of updating the gmLoadAndExecuteScript function by adding better error handling and reporting compilation errors to the user.





More on Script Execution


Contents
  Basic Embedding Concepts
  Executing a String as a Script
  Executing a Script from File
  More on Script Execution
  gmVariable Object
  Calling a Scripted Function
  Creating a host-bound function
  Creating a simple type
  Constructor with Parameters
  Operator Overrides
  SetDot Operator
  Garbage Collection

  Source code
  Printable version
  Discuss this article

The Series
  Language Introduction
  Embedding GameMonkey