GameMonkey Script

GameMonkey Script Forums
It is currently Mon Jan 21, 2019 12:13 am

All times are UTC




Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sat Jan 31, 2009 11:11 am 
Offline

Joined: Fri Jan 14, 2005 2:28 am
Posts: 439
Game Monkey Script Ex(Extended) is a repository of Game Monkey script modifications from myself and other community members that arise out of necessity and/or desire to extend GM functionality.

If anyone is interested, I have a sourceforce project I've been developing on a bit that has a modified version of GM with some user contributions and some of my own. If anyone is interested in using this variation I would love to hear feedback, bug reports, etc.

https://sourceforge.net/projects/gmscriptex/

Currently it has integrated the following
    Stack based Vector3 class, largely based on Shawns thread, though i dont have the broadcast bit in, as I don't like how it looks.
    Fork Implementation, by HiVision
    My gmBind2 library for easy binding of C++ objects(examples below)
    It will also be where I integrate the changes and my GM Debugger for extended remote debugging.

gmBind2

Based off the Squirrel binding library, and a lua binding library I played with, I've built a binding system for GM that makes it easy to bind classes, along with variables of that class, and member functions. In addition to template magic, this support involved some minor modifications to GM, specifically that function objects may reference functors instead of just C function pointers. This made it easier to build a binding library. Here's an example:

Code:
class BindClass
{
public:
   void FuncNoRetNoArg()  ...
   bool FuncBoolNoArg() ...
   std::string FuncStringNoArg() ...
   std::string FuncStringArg(std::string str) ...
   static int FuncRaw(gmThread *a_thread) ...

   static void Bind(gmMachine *a_machine)
   {
      gmBind2::Class<BindClass>("BindClass",a_machine)
         .constructor()
         .constructor((const char *)0,"Under")
         .func(&BindClass::FuncNoRetNoArg,"FuncNoRetNoArg")
         .func(&BindClass::FuncBoolNoArg,"FuncBoolNoArg")
         .func(&BindClass::FuncStringNoArg,"FuncStringNoArg")
         .func(&BindClass::FuncStringArg,"FuncStringArg")
         .func(&BindClass::FuncRaw,"FuncRaw")
         .var(&BindClass::TestInt,"TestInt")
         .var(&BindClass::TestFloat,"TestFloat")
         .var(&BindClass::TestVector,"TestVector")
         .var_readonly(&BindClass::TestFloatReadOnly,"TestFloatReadOnly")
         .var(&BindClass::TestStdString,"TestStdString")
         .var(&BindClass::TestGCTable,"TestGCTable")
         .var(&BindClass::TestGCFunction,"TestGCFunction")
         ;
   }
private:
   int                     TestInt;
   float                  TestFloat;
   float                  TestFloatReadOnly;
   float                  TestVector[3];
   std::string               TestStdString;
   gmGCRoot<gmTableObject>      TestGCTable;
   gmGCRoot<gmFunctionObject>   TestGCFunction;
};


Some things of note.
    It doesn't support function overloading, but it allows you to mix the raw style bindings so you can process arguments yourself, which effectively gets you varargs and overloading.
    The .constructor() must be used or the type will not be constructable from script.
    The .constructor() takes an optional name and optional table. Default is the class name.
    The .constructor() comes in 3 versions, .constructor(name,table), calls your objects default constructor, .constructor(RawFunction,name,table) calls a static raw binding where you can process arguments, and .constructorArgs(name,table) will call your objects constructor and pass the thread, allowing you to parse arguments in your actual object instance, such as BindObject::BindObject(gmThread *th)
    Up to 6 arguments can be auto detected by the templates. Easily extendable if you need.
    There are .func_operator, and .rawfunc_operators, which allow bindings to C++ operator functions.
    Pseudo inheritance is supported with the use of the .base function, see below.
    Supported types are bool,int,float,vector3,other types also bound with gmBind2,std::string,gmTableObject,gmFunctionObject,gmUserObject(including gmGCRoot variations of all 3)

Inheritance

Example:

Code:
gmBind2::Class<gcn::Widget>("Widget",_m)
      .func(&gcn::Widget::isVisible,            "IsVisible")
      .func(&gcn::Widget::setVisible,            "SetVisible")
      .func(&gcn::Widget::setWidth,            "SetWidth")
      .func(&gcn::Widget::getWidth,            "GetWidth")
      ;

   gmBind2::Class<gcn::Button>("Button",_m)
      .base<gcn::Widget>()
      .constructor(Button_Ctr,0,"Gui")
      .func(&gcn::Button::setCaption,            "SetCaption")
      .func(&gcn::Button::getCaption,            "GetCaption")
      ;


Essentially what .base<T>() does is configure the base type for the class, which is also a minor extension to GM by myself. Every type can optionally have a base type. The parent type must also have been bound using gmBind2. This example is a small piece of binding guichan to GM. I have pretty much the entire GUI library script bound using this 'inheritance', behavior of the types.

Basically what this allows is the following
    Sub classes recieve the type variables of their bases, going all the way up the chain.
    Argument deduction will resolve types, meaning it will pass a button to a function that expects a widget(since widget is a base of button).

Global Helper Object

There is also a simpler global object that can be used to simplify binding of global variables and functions.
Code:
gmBind2::Global(m_ScriptEngine, "COLOR")
      .var(COLOR::BLACK.rgba(), "BLACK")
      .var(COLOR::RED.rgba(), "RED")
      ;


This binds color variables under a global COLOR table, such as COLOR.BLACK, COLOR.RED, etc.

Function Caller Object
An experimental class I was toying with is an alternative to gmCall basically, though it probably needs fleshed out a bit more, especially for custom types as arguments, and a better way to handle the return value. Eventually this will be able to take any type registered with gmBind2.

Consider this script
Code:
global TestFunc = function(arg1,arg2,arg3,arg4,arg5)
{
   print("TestFunc",arg1,arg2,arg3,arg4,arg5);
   return "Test";
};
global InTable = {};
InTable.TestFunc = function(arg1,arg2,arg3,arg4,arg5)
{
   print("InTable.TestFunc",arg1,arg2,arg3,arg4,arg5);
   return "Test2";
};


The function helper object is used to call global functions, even if the function is nested in 1 or more layers of tables.

Code:
gmBind2::Function callFn(g_machine,"TestFunc");
gmVariable returnVal = callFn << 10 << 20 << 30 << gmBind2::Null() << 40 << gmBind2::Call();
and
gmBind2::Function callFn(g_machine,"InTable.TestFunc");
gmVariable returnVal = callFn << 1 << 2 << 3 << gmBind2::Null() << 4 << gmBind2::Call();


Last edited by DrEvil-OB on Fri Jun 05, 2009 1:20 am, edited 2 times in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Game Monkey Binding
PostPosted: Sat Jan 31, 2009 4:00 pm 
Offline

Joined: Thu Jan 01, 2004 4:31 pm
Posts: 307
Nice work :)


Top
 Profile  
Reply with quote  
 Post subject: Re: Game Monkey Binding
PostPosted: Tue Mar 03, 2009 11:17 pm 
Offline

Joined: Sun Mar 01, 2009 12:25 am
Posts: 2
This looks great! I'm trying to learn scripting and I tried Lua, GM, and Squirrel, and of the three GM was my favorite because of its size and speed, but I really wanted a binding system like LuaBind or SqPlus. This looks like it hit the nail on the head!! Can't wait to try it out. =D

edit: Um...am I missing something? The sourceforge page doesn't contain anything to download. :?:

edit edit: never mind. I realized I had to use SVN to get it. :oops:


Top
 Profile  
Reply with quote  
 Post subject: Re: Game Monkey Binding
PostPosted: Sun Mar 22, 2009 4:29 am 
Offline

Joined: Fri Jan 14, 2005 2:28 am
Posts: 439
Here's an update on what has been added since my last post.
  • added tableClear function to actually clear a table contents without allocating a new table
  • changed foreach functionality so that it can be implemented for user types, updated tableObjects to use it
  • added support to gmBinder2 for binding bitfields
  • fixed gmMachine::Lookup function to parse indexed strings as well, eg gmVariable v = machine->Lookup("sometable[9].someproperty");
  • bunch of miscellaneous fixes to gmBinder2
  • added asynchronous call support to gmBind2::Function


Top
 Profile  
Reply with quote  
 Post subject: Re: Game Monkey Binding
PostPosted: Tue May 19, 2009 7:05 pm 
Offline

Joined: Sun Jul 29, 2007 11:52 am
Posts: 33
Location: Canberra, Australia
Just had a look over this, damn GM has came along since I last used it :)

Quite like gmBind2, looks very simple to use, sneaky, never thought of doing binding that way (code wise). Shall be a pleasure to implement in my new engine!

Dare I ask, is there a way to say an object is owned by C++? I.e, passing a pointer of an object into GM, or do you not intend for it to work like that?

Another question, is it possible to implement inheritance from a C++ class? No worries if not, I don't plan to do it, just interested :)


Top
 Profile  
Reply with quote  
 Post subject: Re: Game Monkey Binding
PostPosted: Tue May 19, 2009 7:45 pm 
Offline

Joined: Fri Jan 14, 2005 2:28 am
Posts: 439
There is a bool to the "gmBind2::Class<Foo>::WrapObject" function you can set to true to mark the object as natively owned, which means references to the object being GC'd wont actually delete the object.

The one thing I wish GM had a better solution for is strong/weak references, like squirrel. Currently for my C++ owned object, they hold a gmGCRoot<gmUserObject> that holds a reference to their script object, and I use that any time I call a function I need to pass a reference to the object into. Also in my c++ bound object, I call gmBind2::Class<Foo>::Nullify(i think), to zero out the gmUserobject user * when the object is destroyed, so that any remaining script references to that class will be nullified and no bad pointers are accessed. The gmBind2 functions I believe check for that and will throw a script exception if anything attempts to access anything on the (now nullified), reference to the deleted object.


Top
 Profile  
Reply with quote  
 Post subject: Re: Game Monkey Binding
PostPosted: Fri May 29, 2009 11:40 pm 
Offline

Joined: Tue Feb 10, 2009 5:54 pm
Posts: 1
I'm all set to try this out. Just one snaffu though... this change:
Code:
struct gmFunctionEntry
{
  const char * m_name;
  gmCFunction m_function;
  const void * m_userData;                        ///< Optional user ptr or value to assist binding
};
// ->
struct gmFunctionEntry
{
   const char   *m_name;
   gmCFunction   m_function;
   gmObjFunctor   *m_functor;                        ///< Optional user ptr or value to assist binding
};

Caused a small headache for me... I've been 'safely' ignoring the m_userData member (ie having local gmFunctionEntry's, with m_userData being unassigned). Took me a while to figure out why the functor had crazy values in it. All good now though.


Top
 Profile  
Reply with quote  
 Post subject: Re: Game Monkey Binding
PostPosted: Thu Jun 04, 2009 6:21 pm 
Offline

Joined: Tue Sep 02, 2008 3:58 pm
Posts: 2
So far using this is working great. Passing in bound types (when needing a pointer) as parameters seems to work automatically! I'm having a problem using a 'const char *' as a param. Any suggestions?

example

this function:
bool Load(const char* podBasename);

bound like this:
.func(&Scene::Load,"Load")

is causing this compile error
1>c:\documents and settings\bbelt\code\beat\sim4\gamemonkey\3rdparty\gmbinder2\gmbinder2_class.h(195) : error C2825: 'ClassT': must be a class or namespace when followed by '::'


Top
 Profile  
Reply with quote  
 Post subject: Re: Game Monkey Binding
PostPosted: Thu Jun 04, 2009 7:01 pm 
Offline

Joined: Fri Jan 14, 2005 2:28 am
Posts: 439
Thanks, Yes I built it so that as long as you have bound the types with gmBind2 you can freely bind functions that use those types as parameters or return them(pointers and references work I believe).

I might be able to add a template specialization for const char *. I want to say that when I tried it that it broke other usages since the compiler tends to be not too great at figuring out the proper template specializations when the parameter is a pointer, so it was erroring on other pointer parameters when const char * was specialized, so I left out const char * in favor of using std::string for string parameters. I'll see if I can try it again soon. In the meantime if you want to work around you can do an overload binding.

Code:
bool Load2(const std::string &podBasename)
{
    // call your existing function
    return Load2(podBasename.c_str());
}


Top
 Profile  
Reply with quote  
PostPosted: Thu Jun 11, 2009 5:57 pm 
Offline

Joined: Tue Sep 02, 2008 3:58 pm
Posts: 2
Thanks, for me using std:string instead is no problem at all.

I did find a gotcha with bound member vars and inheritance. If you bind a var in the base class,
when you try to access it from a subclass it looks for it in that objects table. It works fine if the
var is bound by the subclass.


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 04, 2009 5:55 am 
Offline

Joined: Fri Jan 14, 2005 2:28 am
Posts: 439
  • Added Schema Library to repository
  • Added optional 'documentation' support to gmBinder2. *see below
  • Added a helper class to gmBinder called TableConstructor that simplifies building a large nested table. *see below
  • Bunch of other fixes and stuff. See svn log for details.

Code:
//Last parameter allows an optional 'documentation' string, that can later be queried with gmBind2::Class<Weapon>::GetPropertyTable where it will build a table that contains information on the properties of the user object.
gmBind2::Class<Weapon>("Weapon",_m)
   //.constructor()
   .var(getName, setName,         "Name","string","Name of the weapon.")
   .var(&Weapon::m_WeaponID,      "WeaponId","int","Numeric Id of the weapon.")
   .var(&Weapon::m_WeaponAliasID,   "WeaponAliasId")
   .var(&Weapon::m_MinUseTime,      "MinUseTime","float","Weapon must be used for a minimum amount of time when chosen.")
   .var(getPrimaryFire, NULL,      "PrimaryFire","firemode","Access to primary fire mode.")
   .var(getSecondaryFire, NULL,   "SecondaryFire","firemode","Access to secondary fire mode.")
   ;

// gmBind2::Class<Weapon>::GetPropertyTable will build a table like this. My intent is to have this simple output be the input to various script parsers that could ultimately create various files needed to keep autocompletion, syntax highlighting, etc up to date with various text editors our users work with.
/*Weapon =
{
   [0] =
   {
      Name = "Name",
      Type = "string",
      Comment = "Name of the weapon.",
   },
   [1] =
   {
      Name = "WeaponId",
      Type = "int",
      Comment = "Numeric Id of the weapon.",
   },
   [2] =
   {
      Name = "WeaponAliasId",
      Type = "int",
      Comment = "",
   },
   [3] =
   {
      Name = "MinUseTime",
      Type = "float",
      Comment = "Weapon must be used for a minimum amount of time when chosen.",
   },
   [4] =
   {
      Name = "PrimaryFire",
      Type = "firemode",
      Comment = "Access to primary fire mode.",
   },
   [5] =
   {
      Name = "SecondaryFire",
      Type = "firemode",
      Comment = "Access to secondary fire mode.",
   },
},*/



Code:
//Tableconstructor example. Paired with the 'documentation' support of gmBinder, this builds a hierarchy of documentation for various user types and dumps it to a file.
gmBind2::TableConstructor tc(m_ScriptEngine);
tc.Push("Weapon");
   gmBind2::Class<Weapon>::GetPropertyTable(m_ScriptEngine,tc.Top());
tc.Pop();
tc.Push("FireMode");
   gmBind2::Class<Weapon::WeaponFireMode>::GetPropertyTable(m_ScriptEngine,tc.Top());
tc.Pop();
tc.Push("MapGoal");
   gmBind2::Class<MapGoal>::GetPropertyTable(m_ScriptEngine,tc.Top());
tc.Pop();   
File f;
if(f.OpenForWrite("user/docs.gm",File::Text))
   gmUtility::DumpTable(m_ScriptEngine,f,"Docs",tc.Root(),gmUtility::DUMP_RECURSE);


Top
 Profile  
Reply with quote  
PostPosted: Sun Nov 15, 2009 8:16 am 
Offline

Joined: Fri Jul 24, 2009 1:09 pm
Posts: 27
I've just checked out the GM extended code. I'm curious how to get rid of the vector3 code and all of it's references, as I don't need it and it's giving me missing file errors. I tried changing it here (in gmConfig.h):
Code:
#define GM_USE_VECTOR3_STACK       0         // Support for stack based vector3 type

But I get these errors:
Quote:
Error 1 error C2065: 'Vec3' : undeclared identifier c:\Dev\GMEX\trunk\gmsrc_ex\src\binds\gmStringLib.cpp 1037
Error 2 error C2146: syntax error : missing ';' before identifier 'v' c:\Dev\GMEX\trunk\gmsrc_ex\src\binds\gmStringLib.cpp 1037
Error 3 error C2065: 'v' : undeclared identifier c:\Dev\GMEX\trunk\gmsrc_ex\src\binds\gmStringLib.cpp 1037
Error 4 error C2228: left of '.x' must have class/struct/union c:\Dev\GMEX\trunk\gmsrc_ex\src\binds\gmStringLib.cpp 1038
Error 5 error C2228: left of '.y' must have class/struct/union c:\Dev\GMEX\trunk\gmsrc_ex\src\binds\gmStringLib.cpp 1038
Error 6 error C2228: left of '.z' must have class/struct/union c:\Dev\GMEX\trunk\gmsrc_ex\src\binds\gmStringLib.cpp 1038
Error 7 error C2039: 'PushVector' : is not a member of 'gmThread' c:\Dev\GMEX\trunk\gmsrc_ex\src\binds\gmStringLib.cpp 1040
etc...


Cheers.


Top
 Profile  
Reply with quote  
PostPosted: Sun Nov 15, 2009 10:15 pm 
Offline

Joined: Sat Oct 10, 2009 5:30 pm
Posts: 4
This is awesome! Although, it tends to make Intellisense a little mad sometimes :lol:

Also, can this be used for binding a single function as well? Or only classes?

I tried doing:
Code:
void testFunction2()
{
   superdevice->getLogger()->log(L"It works!2");
}

//[...]
void GameMonkeyTest()
{
 gmBind2::RegisterFunction<gmCFunction>(gm,(gmCFunction)testFunction2,"testFunction2");
}

But that makes GameMonkey create a function that has one param that is null, and giving it null causes an exception on line 141 of gmbinder2.h .

Also, on line 812 of gmbinder2.h
Code:
gmFunctionEntry fn = {0,0,0,0};

needs to be
Code:
gmFunctionEntry fn = {0,0,0};


~DtD


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 17, 2009 3:25 pm 
Offline

Joined: Fri Jan 14, 2005 2:28 am
Posts: 439
gmBind2::Global can be used to bind global variables and functions

Thanks, I've committed the struct initialization fix also.

Mitch, I've committed compile fixes for when GM_USE_VECTOR3_STACK is set to 0. Thanks for letting me know about that one too.


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 25, 2009 9:33 pm 
Offline

Joined: Sat Oct 10, 2009 5:30 pm
Posts: 4
Is there any good way to bind constants? (Specifically enums) I tried using gmBind2::Global but it didn't work.

~DtD


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group