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


Creating a host-bound function

Now that you know how to call a scripted function from native code, you may be wondering how you export your own functions to GameMonkey Script. The reasons for wanting to do so are understandable; some people may want the speed of native code (although GM is fast, it's still slower than native code), others may want to expose aspects of their game or engine to the script. Whatever your reasons, you need to know how to export your own functions.

Unlike some scripting languages (for example, AngelScript), functions must be wrapped for use by the VM. The GM Script VM doesn't have the ability to translate the parameters from the script into a form understandable by your native compiled code - one reason being that function calls and their parameters aren't known until runtime, unlike in C++ where everything is checked during compilation. The process within the wrapped a function usually involves the following steps:

  1. Parsing the parameters passed from the scripting VM
  2. Converting them into a usable form for C++ code
  3. Performing the main action of the function
  4. Converting any return values to a suitable format for the VM
  5. Returning control back to script

Here's the code I will be examining for a simple native version of myMultiply:

int GM_CDECL gm_myMultiply( gmThread *a_thread )
{
    // Check the number of parameters passed is correct
    if (a_thread->GetNumParams() != 2 )
        return GM_EXCEPTION;
        
    // Local vars to hold data from params
    int a_x = 0;
    int a_y = 0;
	
    // Check params are valid types
    if (a_thread->ParamType(0) != GM_INT)
        return GM_EXCEPTION;
    if (a_thread->ParamType(1) != GM_INT)
        return GM_EXCEPTION;
	
    // Get data from params
    a_x = a_thread->Param(0).m_value.m_int;
    a_x = a_thread->Param(1).m_value.m_int;
	
    // perform calculation
    int ret = a_x * a_y;
	
    // return value
    a_thread->PushInt( ret );
	
    return GM_OK;
}

Like its scripted counterpart, this function takes two parameters and returns the sum of their values. The 'wrapped' function first checks that the number of parameters it has been passed is correct; if not we return a scripted exception, next it checks to see that the parameters are the correct type (in this case, int). It then reads the values of these parameters from the thread - you will notice that the parameters are held again in the gmVariable structure. After the parameter data has been collected and converted you can now perform the main body of the function; in this example it is a simple multiplication but in your games it is likely that you will call another function in your engine.

You will notice that a lot of the code to check parameters is repetitive and can be cumbersome to write. Fortunately for us, the GameMonkey authors have provided several macros to aid our task.

int GM_CDECL gm_myMultiply( gmThread *a_thread )
{
	GM_CHECK_NUM_PARAMS(2);
	GM_CHECK_INT_PARAM( a_x, 0 );
	GM_CHECK_INT_PARAM( a_y, 1 );
	
	int ret = a_x * a_y;
	
	a_thread->PushInt( ret );
	
	return GM_OK;
}

The GM_CHECK_NUM_PARAMS macro is fairly self-explanatory; it checks the number of parameters from the thread and will return a GM_EXCEPTION if the number of supplied parameters is lower than those you want. It should be noted, however, that if you pass more parameters than you are requesting then you will not get this error. One assumes that this feature is provided to allow for variable argument functions to be used with the macro.

The GM_CHECK_*_PARAM macros are similar, but they also declare the variables and fill them for you. These macros make the code much simpler to read while still performing the same function. However it must be noted that as these are macros they require specific variable names to work. In the example above, your thread pointer must be stored in the variable a_thread, this naming convention is used throughout GameMonkey Script and I have stuck to it in these articles.





Creating a simple type


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