Filed under: embedding javascript in game

What's new : Laboratory2D Beta 2 on the horizon

Hello again! This is where I spam about all the cool things I have been doing. To reward your visit, here is one of the earlier designs I had worked through when coming up with the logo for the engine.

The huge refactor

It has been a long and crazy time with the most recent additions to Laboratory2D. I started tearing out the insides of the code to try and decouple the scripting from the core C++ engine so that I could start binding spider monkey. It took a really long time, somehow over the last 8 months i had managed to litter v8 into the core components of the engine. Not in a terrible way, i managed to remove 99% of it over some simple copy pasting - but the event system and the whole sub script system were heavily bound to v8. This had to be fixed.

The escalation

Well as always, while shifting around some code I found some other ugly code. I refactored it. I found some interesting bugs, fixed and refactored. I changed all the layouts of code to be uniform again, I cleaned out all the random code that had yet to be finished or tested and stuck it in another branch. I finally got around to cleaning up the interfaces and systems and I am proud to be at the end of this stretch with a much cleaner code base on the c++ side, a lot less hackish code that i did about a year ago when learning the systems involved - and most of all - a clear and driving direction for the core itself.

The new features

Again a crazy list I won't post here. I finalised the font support for full bitmap font support, with normal scaling, rotation and coloring. Camera systems got large improvements like being draggable and zoomable with a single flag, sprites got a whole new set of handy features, a few more examples were polished and a couple other useful modules have been included in the core (like a handy edit box, with selection, a history and custom properties). I also finalised the networking support for scripting so that a handful of lines of code later, you can have a chat going over the internet.

What about this Beta 2

Well, beta 2 will be the first mostly public release of the engine. Hopefully it will be soon but work and life and other things tend to get in between - so i won't say a time. Just soon. As of now, Beta 2 is almost 100% complete according to my original goals it is merely a matter of getting the documentation and the examples in line with the monolithic changes I have made over the last month or two. That gives me the next short while to finalise the android, iPhone and browser embedding features that I have been working on in the mean time. As with the post mentioning the way I am doing releases - Beta 2 phase will be fixing all the issues reported by the beta users as well as making even better examples and demos. Sometimes, i can't help myself so I jump ahead and get started on the demos to keep me motivated. Expect to see a lot of interesting things as beta 2 rolls out! 

The original reason for Laboratory2D is still alive

As a matter of interest, the project that started Lab2D is actually in progress again. Now that the core is stable and the scripting is mature it is a totally different world to work with - we are moving forward at a rapid pace again which is exciting. The scripting opens up opportunities for team members to help even more than before and to really get involved where the C++ before had just isolated me from having the game i wanted. As I roll out releases for this game, keep an eye out on other forums and indie gaming channels for trailers and gameplay teasers and most likely a playable release. For those who are not aware of it, it is an uplink style game (inspired by) and it is something i have wanted to make for many years. 

 

How to obtain the beta now

Its free, It's javascript driven, its fun (and portable).

email fuzzy at owned.co.za

tweet @FuzzYspo0N

Comment below

 

The inner workings of 2d game tech laboratory 2 : Scripting

Keep in mind readers, this is very rough around the edges. Proper detailed documentation to all subsystems (with examples) are always sought after. Im going with the easy to read, and then deeper knowledge. Higher level makes understanding so much nicer for me. Any suggestions on this one? Its kinda tl;dr but the blocks separate it enough to pick out what you want to know.

 

v8 c++ Crash Course

A formatting note - I have tried to clean the code a little to work with this width of page. The original code with less dumbassery is linked in the header file here for reference. Get the example code from here : LINK I know i needed one. When i had chosen v8 as my weapon of choice i found it to be incredibly easy to grasp. What i didnt expect is for the simplicity to grab me around the ankles and dangle me off the edge of confusion cliff. Before i realised i was scratching my head with a few good c++ books open wondering what on earth i had done wrong that was giving v8 exceptions from a single line of code. What i would advise, is try to avoid shortcuts. I was using CProxyV8, i had seen v8Juice and both of them are really neat, but every single error i ran into was related to me not understanding what it is v8 was actually asking for and most times it was because of the use of the wrappers. Finding my own way I learnt a few things along the way - using v8 from the ground up is actually easier to understand. The first thing that helps is to look at the v8 namespace comments as they usually reveal something important, if its related to the JS side, or if its related to the c++ side.Once i saw that i was attempting to use a JS object as a c++ class, yea things started to make more sense. What i needed from v8. Now while the point and line class example is so heavily leaned on, it still didnt really help me to figure out the route i needed to take to expose certain classes to the script environment. The included sample http process and the other samples also , did things in a very straightforward and limited way. Below i will present an empty class from my codebase, with the code i used to expose and gracefully use the script side easily. I needed to wrap and unwrap a class. In order to expose a class to JS, i wanted to use an EXISTING C++ class, and just pass a pointer/handle to the scripts. That way i can also control what part of my game/scripts they can actually overwrite and change, and what instances of class they can manipulate. For example, i dont want multiple instances of the core, or any subsystem of the core. I just want a script side global(used loosely here) that the script writer can access (and which also protects him from accidentally overwriting one of my objects). A template based helper function I wrote 2 functions that are free to use , and will wrap and unwrap a class accordingly. The usage of the code provided here is shown lower down, so it might not make sense here. Wrapping involves creating a JS object from a c++ class, and maintaining a handle to manipulate it, and unwrapping involves unwrapping an object to a c++ pointer again, normally from the v8arguments handle that a function passes in on being called. example use : ExposeClass< somenamespace::someClass >( somev8Context, someClassInstance, v8::String::New("someclass"), v8::ReadOnly);

//http://blog.owned.co.za v8 Javascript Helper Function ,
//Give this function the c++ object (an instance) to wrap, 
//and it returns a JS object to expose things under.
//Note theres 2 functions here , 
//the top one is used by the ExposeClass function

template 
v8::Handle WrapClass(T* y)
{
        // Handle scope for temporary handles, 
        v8::HandleScope handle_scope;
        v8::Persistent  class_template_;
        
        v8::Handle rt = v8::ObjectTemplate::New();
        
        //The raw template is the ObjectTemplate
        // (that can be exposed to script too)
        //but is maintained internally.
        rt->SetInternalFieldCount(1);
        
        //Create the actual template object, 
        class_template_ = v8::Persistent::New(rt);
        
        //Create the new handle to return, and set its template type 
        v8::Handle result = class_template_->NewInstance();
        v8::Handle class_ptr
        class_ptr = v8::External::New(static_cast(y)); 
        
        //Point the 0 index Field to the c++ pointer for unwrapping later
        result->SetInternalField(0, class_ptr);
        
        //Return the value, releasing the other handles on its way.
        return handle_scope.Close(result);
} 


template
v8::Handle ExposeClass(v8::Persistent context, 
                                   T* y, 
                                   v8::Handle exposedName, 
                                   v8::PropertyAttribute props)
{
        v8::HandleScope handle_scope;
        
         v8::Handle obj = Core::Script::WrapClass(y);
        context->Global()->Set(exposedName, obj, props);
        
        return handle_scope.Close(obj);
}

And for unwrapping - (usually inside CALLED functions from script. use : someNamespace::someClass* sc = UnwrapClass< someNamespace::someClass >( myJSObjToUnwrap );

//http://blog.owned.co.za v8 Javascript Helper Function ,
//Give this function the v8 JS object to unwrap, and the class to unwrap to.
                
template 
T* UnwrapClass(v8::Handle obj) 
{
        v8::Handle field;
        field = v8::Handle::Cast(obj->GetInternalField(0));
        void* ptr = field->Value();
        return static_cast(ptr);
}

How i use the above templates Nobody is claiming magic in the code above, it might even be wrong and or, doing something badly. I just found it to work - any comments and fixes will be noted and adjusted if you comment. Now, the usage, at least MY usage of the above code was to expose c++ classes with existing c++ functions to the script context. Step 1 - A Class to expose, with some members id like in scripts. First, i have a c++ class declared, with some static functions that i would like to expose.

class cCore
{

public:

        //This is a simple example, 
        //do not copy this as a real class.
        cCore(){};
        ~cCore() {};

        //We need a persistant handle to the core in scripts, a non persistant
        //v8::Handle will get lost in the handle scopes, 
        //and throw some weird exceptions to hunt down. 
        //Wrap class and unwrap class return 
        //NON persistant handles, so store your own 
        //persistant ones if you want to 
        //use it outside of the first handle scope.

        //core script object,  "core" in scripts.
        v8::Persistent cso_core;                                    

        //We expose some functions in our class to the scripts from c++, 
        //By wrapping an object (cso_core above) and the attaching functions
        //to that object. Those functions, when called from javascript - 
        //end up here.

        //The functions are self explanatory.

        //Print some text to the console/ui console
        static v8::Handle xecho(const v8::Arguments& args);         
        //execute a javascript file       
        static v8::Handle xexec(const v8::Arguments& args);         
        //exit all systems and shutdown       
        static v8::Handle xexit(const v8::Arguments& args);                
                                        
}; //end class declaration

Step 2 - Creating the Javascript Object , of my class.

//Just for clarity sake, so you can see my workflow.
void cScriptSystem::startup()
{
        //Create a script engine for the core
        v8::HandleScope handle_scope;

        //Create a context for the system to use 
        //(i have just one at this point)
        coreContext = v8::Context::New(NULL, global);

        //Enter the main context, making it active.
        v8::Context::Scope context_scope(coreContext);

        exposeCore(); //see below
};
                
//And the code to expose the class, 
void cScriptSystem::exposeCore()
{
        v8::HandleScope handlescope;

        //This will be reused to expose multiple 
        //classes, short name for easy code.
        v8::Handle cc;
                        
        //get the JS version of the C++ object, 
        //note that core is simply Core::cCore* core; 
        cc = ExposeClass< Core::cCore >(coreContext, core,  
                                         v8::String::New("core"), 
                                         v8::ReadOnly);
                        
        //set the persistant handle for use outside of this handlescospe
        core->cso_core = v8::Persistent::New(cc);

        //Give the functions to the object. 
        //This makes the functions come out as 
        //core.echo(), core.exec(), core.exit() in scripts.
        Expose(core->cso_core, v8::String::New("echo") , 
               v8::InvocationCallback(Core::cCore::xecho));
        Expose(core->cso_core, v8::String::New("exec") , 
               v8::InvocationCallback(Core::cCore::xexec));
        Expose(core->cso_core, v8::String::New("exit") , 
               v8::InvocationCallback(Core::cCore::xexit));

};

Step 3 - Actually handling the function calls on the c++ side. This is where the unwrapClass comes in... If you noticed the v8::Arguments& args on the functions, it hands in the class that the function is being called upon. So if i called core.exit() , the object that it will pass me is the core object (our C++ object, in JS form). See the code for clarity,

v8::Handle cCore::xexit(const v8::Arguments& args)
{
        //args.Holder() is the object that the function was invoked upon. 
        //This means it could be exposed to 2 classes, 
        //under different types so be sure
        //that you dont just randomly unwrap into the wrong container type.
                        
        Core::cCore* core = UnwrapClass( args.Holder() );
                                
        //The code inside the normal exit function is identical, 
        //so i can just call the normal c++ code directly.                
        core->startShutdown();
                        
        //This is to return void, basically.
        return v8::Undefined();
}

Step 4 - The missing function. I left out a function along the way, you may have noticed. The function is called Expose,

void Expose(v8::Handle intoObject, v8::Handle namev8String, v8::InvocationCallback funcID)
{
    v8::HandleScope handle_scope;

    v8::Handle thisFunc;
    thisFunc = v8::FunctionTemplate::New(funcID);
    intoObject->Set(namev8String, thisFunc->GetFunction());
}

Conclusion I hope this helps clarify the v8 logic, and helps users figure out how easy it actually is to automate things with templates and macros. Theres still many ways to simplify the code above but for simplicity - take it as it is. In closing, v8 offers some super easy to use casting functions. Ill give some examples as i found these to be really nice but be wary, do exception handling, type checking and proper management of values handed to c++ from v8. It will save you some time looking through v8 exception callstacks if you manually handle possible exceptions.

//you can now use it std::string(*somestdstring)
v8::String::Utf8Value        somestdstring ( args[0] );  
                
        //you can now use it *somecstring (same as const char* somecstring;)
v8::String::Utf8Value        somecstring ( args[1] );        

        //normal double, 
double somedouble = args[2]->ToNumber()->Value();
        //booleans
bool somebool = args[3]->ToBoolean()->BooleanValue(); 
        //and integers.
int someint = args[4]->ToInt32()->Int32Value();        
                
//And one last thing, giving javascript meaningful return values. 
//This should be more than enough to let you figure out how simple
//v8 makes things for you on the c++ side.
                
        return handle_scope.Close(v8::Number::New(somedouble));
        return handle_scope.Close(v8::Int32::New(someint));
        return handle_scope.Close(v8::Boolean::New(somebool));

Out.

Posterous theme by Cory Watilo | Mod by FuzzYspo0N