Introduction to GameMonkey Script Part 2
Embedding GameMonkey
Executing a Script from FileIn 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:
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()); }
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 CheckingNow 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 ); } }
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; }
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. |