Complex Features on a Limited Schedule
I've been planning the Tech series for over 10 years now and as much as I've thought about gameplay, I've also thought about making the project practical for a one-man show (at least in the beginning). Although Tech Arena's features have been boiled-down to a bare minumum, it's still a great deal to accomplish. Component development, trading/economy, managing resources, etc. have all been major problems looming over the project. However, I worked out an easy solution that has greatly simplified the development of the game's more complex features.
After reading "Programming in Lua" by Roberto Ierusalimschy, I realized that the Lua VM, itself, had some powerful, yet elegantly simple, features: easy integration with DLL components, debug support, environments for seperate scripts, metatables which could be used to restrict and grant access to data, garbage collection, weak tables, easily serializing and deserializing persistant data. It's such a simple and powerful virtual machine, that I thought, like other "machines", it should have an operating system written for it... and boom went the dynamite!
Why integrate Lua into an application when you can integrate your application into Lua?
The Solution: TechOS
Using the game's already existing GUI, input system, rendering system and other major components, I set out to write a kernel for the VM that would treat scripts as applications which could be integrated together to drive the game's interfaces and features. The actual game is, itself, a Lua application (although it's mainly for initialization, firing up threads and responding to input). Even vehicle components can be treated as seperate application instances. There are quite a few things to consider.
- Applications must be encapsulated (your cockpit code can not access my targeting system code)
- Must allow some form of multitasking but also allow for strictly single instance apps or even one-off scripts
- Applications must easily integrate with eachother
- Some applications are more restricted (vehicle components can not access OS functions)
- Must be some control over the order in wich things execute
- Apps must easily be able to save state
- Must be extensible (DLLs... Lua already does most of the leg work here)
The OS maintains tables of all currently running scripts. When an application is run, an environment table is created and that table gets an access metatable assigned to it. The access metatable contains read-only references to all functions, objects and tables for that access level. For instance, OS level apps are assigned the global table (_G), giving it full access.
Persistant tables are also maintained by the OS. An application can call Sys.Persist() on a table which then returns the current state of that table (restores if previously called). This is somewhat similar to the Windows Registry except that only that application has access to its data.
Multitasking, Instances, Integration and Priority
These four issues are all handled very simply. First, event-driven multitasking is used rather than actual multithreading. This gives control over order of execution and avoids the nightmare of using multiple VM's across seperate threads.
Applications (and vehicle components) typically have OnCreate(), OnUpdate() and OnDestroy() functions that are called from the OS. "Global" code and data executes when loaded and affects all instances of the app. The three event functions are passed an instance table to hold all instance data which is maintained by the OS. Without these functions, the application is treated as a one-off script and is terminated upon completion. Single instance apps implement OnCreateOnce() instead of OnCreate(). Rather than opening another instance, the OS will focus the currently running instance.
When apps are opened from other apps, they may pass parameters which are received by the OnCreate function. This also allows apps to share tables for integration by passing those tables as parameters.
Some exciting possibilities come to mind now that this system is (mostly) in place. Rather than creating only vehicle components, an industrious user could possibly write applications for the OS and extend the games interface and functionality. Although DLL's currently are loaded as OS level functions only, I plan to allow other access levels to be extended. This leaves the development of the game wide open for community participation. I've even considered backing up DLL's that have name collisions with currently loaded libraries so you have an option, in-game, of which to use. Don't like the graphics engine? Write a new render.dll. It's a crazy idea and would require that all players in a network game run the same DLLs. Still, it would be easy to implement at this point and probably worth a try.
All of this took about a month to write and was definitely time well spent. The in-game IDE, which was one of the more daunting items on my task list, was implemented in under two weeks and ended up with many more features than I originally planned. I've been considering releasing TechOS seperate from the game for those interested though it probably won't be worth much until I document the core API.
Well, that's enough insane rambling for one day. Back to being productive! ;-)