Filed under: v8 javascript
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
Well, one thing is for certain I am grateful to be posting this and not something about giving up or moving on. Let me explain.
The whole hindsight thing.
I chose javascript for many reasons. I also chose v8 because it is extremely easy to integrate. It really is. It is also really fast, and easy to build on other platforms.
See when I started building the technology behind this game, my intention was not to get onto as many platforms as possible, at least not with the core codebase. I built the UI system in such a way that i can connect multiple 'viewers' to see that state of the game I am making. That means a native iPhone app, or a web based php system, all of them have the exact same access to become clients of the game, with little or no porting of the core code. The game mainly runs online, so a connection would be required anyway. Your client is actually hosted on a dedicated server, and your 'viewer' will show you what the client is doing.
As time progressed I found myself really enjoying the base technology, and more so enjoying using javascript to make games. I also ended up moving towards a normal usable 2D engine for other people to use (and quite a few are). I added tilemaps, physics, sprite systems, animations etc. What I realised then is some sort of potential for the cross platform factor to become far more of a reality. Port the code to the platforms as native code, and all my games can be portable (as well as anyone else who wanted to use the technology). I had assumed ( about 9 months after i had started ) that v8 was portable to the platforms I was porting to at the time. Mac? Easy. Linux? Easy. Android? Already there. iPhone? Compiled for arm, success!
What I did when building the iPhone version of Lab2D was something monumentally dumb though -
Make sure you do not test ported code on the simulator and be satisfied.
Why V8 engine does not run on iDevices at all.
This is something that took a while to find the REAL reason. Some complained about the SDK Agreements, not really an issue if you are not downloading the scripts. Some complained about the lack of ARM support but of course V8 grew on that pretty quickly for Android.
iOS has a restriction that basically makes V8 not work. Here is a list of the things V8 can and cannot do on iPhone.
CAN - Compile against device SDK's (4.1 in this case). CAN - Run on the device, the arm code and v8 code works as expected (note, this is just the V8 code) CANNOT - Execute any javascript via the JIT compiler.
Why is that? Some said the ARM JIT of V8 emits the wrong kind of assembly for ARM processors and uses a simulator of sorts on android? No, that is not the reason. The reason is - Writable memory cannot be flagged as executable on iOS.
This is simple to understand. A JIT will compile code into memory, and execute it there. This, cannot happen on the iOS platform. Seeing as V8 is, just a JIT compiler, it makes sense why Invoke/Execute crashes on device and not on the simulator.
And then came GameMonkey
At first I thought I had lost out. I was really blown away at what a stupid mistake I had made by not testing earlier. Secondly, the other problem I realised is that V8 does not compile or run on PowerPC Architecture ( there is a port, but not usable yet ). PowerPC powers XBox360, PS3, Wii. That means the chances of me using V8 on those are so slim its not even funny. It also means that MIPS was not there yet either (think PSP).
The options I had included rewriting the entire engine in another scripting language. Rebinding the core engine to scripts can take half a day, but rewriting all of the game engine script code will take some time. And a lot of it relies on the language features to get things done efficiently, meaning a lot more code than I had in JS. It also meant losing the whole purpose of why i wanted to use javascript etc etc. I looked around, I have used many scripting engines before none of which I would choose for this, but I was linked to GameMonkey by the awesome Dylan Cuthbert from Q-Games. While there was a certain trade off, I was more interested in it because of its JS like syntax and other cool features. I started experimenting with the scripting engine and tweaking the VM here and there. What I did notice is that i could actually add the features of JS that I need to the GameMonkey engine, and continue forward with a language that was definitely portable ( as gamemonkey can be seen on all major consoles and platforms ). Instead of porting everything fully, I kept digging around for more possibilities. Most obviously, I would be looking for a javascript engine that was portable that I could use to add support for on the platforms that need it.
And then came SpiderMonkey
Now that I added some support for game monkey, I only added what was needed to test portablity and viability of using the engine. What came next was seeing someone else had used spidermonkey on iPhone before. I also noticed that the post was made in 2008. A long time for an engine to progress. And to be honest, there was no real competition with spider monkey and game monkey in terms of performance. Spider monkey was dog slow not that long ago. Luckily, they have made progress in leaps and bounds with performance and I didn't want to prematurely optimise anything without knowing if it was slow as an embedded engine. So, i grabbed the spidermonkey source code, I made a new static library project and I built the code using a hybrid of their make files and XCode. I hooked up the output library file into a test empty OpenGLES example , and I copy pasted the getting started tutorial into the main.mm file. 2 minutes later, I deployed to device and had spidermonkey functioning 100% on iPhone. On device this time. This was a huge sigh of relief. I can still move forward as planned, without losing much pace except for adding bindings for spider monkey to the core (only about 60 bindings total it seems).
And then there was the engine code
Obviously, I realised that i need to be testing far more if I plan to support other hardware and platforms. I need libraries and code that will definitely work on specialised hardware. I can't afford to spend more time on something I might not be able to use somewhere. It sucks. And If i plan to be using this code on consoles it needs a lot of refactoring for efficiency. When I started the engine code It was more than a year ago now, slowly over that time I had learnt far more about V8, about C++ and C practices, I had started moving to use C based libraries intentionally as they are the most portable and compatible and easily built, I started removing code that was legacy and tidying up all the weird experiments that I had coded in and around the engine code ( like a UV map editor in the UI system! ). I also learnt far more about writing efficient code, only after the code worked in the first place. The whole get it done, get it working, get it faster, get it clean was working wonders in terms of productivity with the little time I had to give the engine, but it added some ugly code along the way.
Inbetween all this, I have read a huge amount on the topic of portable code, portable games and understanding the architectures of consoles, mobile platforms and game engines. If you haven't - be sure to get http://www.gameenginebook.com and also check out http://www.altdevblogaday.org . Both of these links are incredibly helpful when looking to write console or portable code in general. Especially efficient code, memory management and those things that PC doesn't hate on so much.
And then there was a refactor
This weekend I started a pretty large scale change over the entire core codebase. Switching out libraries for proven portable ones, adding a scripting interface that allows multiple scripting engines to exist at the same time, wrapping more of the engine in spidermonkey and gamemonkey, moving away from code with known bad allocation patterns and starting to add custom memory allocation systems in the places where it would count. Portability for me always meant Windows, Linux, Mac for the most part. Now that there is more of a usable toolset here the value of having this available to independent developers on other consoles and platforms is obvious. Plus, it teaches me immense amounts about writing time critical code in an efficient manner.
And then there is a conclusion
iOS - spider monkey (javascript)
Windows, Mac, Linux, Android - V8 (javascript)
PS2, PS3, PSP, XBox360, Wii - GameMonkey
My plan is to move game monkey language closer to JS in all the ways possible, so that less porting between gm and js files will occur, but since I have yet to even test the engine code on any of these platforms ( due to the cost of obtaining a developer kit ) but until then, it is not much concern. Until then the focus will be on getting the engine embedded in browsers, and onto android in full. That is still a lot of options, and still all using javascript. When I do get onto those platforms with testing, and performance tweaking it might mean something different...
But overall, this side project hobby engine is actually so much fun. It is fun to face these challenges, fun to write and learn more about portable programming practices and most importantly - it is really fun to work with the engine to make my own games (which really, was the intention overall).
For now, the engine is of course still usable, still looking for testers, and the source will be available as soon as the refactor is complete. The code will most likely be hosted publically on github along with all the games and engine side code, there is nothing really magical going on there!
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.
To celebrate the months wasted trying to find a way to make gameswf work for me, i am going to post the test build of the game here. There is not a lot to the UI yet, just a login screen and a "login complete" screen. After that, there is the escape key to exit.
For this test, the username is devcore , and so is the password. Iv attached some images below for post sake.
The tech so far
Currently im using v8 javascript engine for scripting,
PhoenixCore for rendering Link
And awesomium for the UI stuff Link
The rest of the story
Is still to come. I will post later today sometime maybe, the ideas behind the networking code on the client tool development side,
and i hope that there will be some discussion behind that. So if you can/want to please check back in a while for the networking part.
Candy
[caption id="attachment_110" align="aligncenter" width="431" caption="FLCore"]
[/caption]
[caption id="attachment_110" align="aligncenter" width="450" caption="FLCore"]
[/caption]
Notes :
Requires VC (vs2008) runtime libraries. Google these, if you dont have them. If you cant run the exe, open the html instead for now.
The Download
Self Extractor (3.8 MB)
Link
|