I feel like I haven’t said anything particularly useful in a while, so I’m going to go over the architectural design and flow of my work-in-progress game engine, Shibboleth.
Our journey begins at what I call the Application Layer, which is nothing more than what the executable the end-user runs actually does, which isn’t much. To start, let me explain the expected folder structure the executable assumes.
<root folder> |- App64.exe (or App32 for 32-bit) |- Managers | |- A bunch DLLs in this folder. Follows executable naming scheme. |- States | |- states.json | |- A bunch DLLs in this folder. Follows executable naming scheme.
So, the application executable is expecting two folders to exist, Managers and States. Inside each of these folders are a bunch of DLLs. The States folder also contains a JSON file, more on that later. What the application then does is load all the managers from the DLLs found in the Managers folder and the state machine states from the DLLs found in the States folder. The states.json file describes the edges between the states in the state machine.
A manager, in my application framework, is a system that is global to the entire application. It will exist throughout the entirety of the application’s lifetime, and is guaranteed to have only one instance of it created. A DLL in the Managers folder has an expected C-style interface and each DLL can contain any number of managers.
Below is the expected interface for manager modules. For clarity, I’ve left off the markup for exposing the functions. You will have to specify extern “C” __declspec(export) on each function or the target platform’s equivalent.
The DLLs found in the States folder contain individual states of a state machine. Similar to the managers, each DLL has an expected C-style interface and can contain any number of states. The states.json file then describes what states are in the state machine, which states each state can transition to, which module (aka DLL) it should be found in, and what the starting state is.
Below is the expected interface for state modules. Again, you will have to specify extern “C” __declspec(export) on each function.
After the application has loaded all the managers and created the state machine, the update loop is just a simple:
That’s it. All the logic is contained within the DLLs, so changes to code only force small re-compile/re-links if their code is organized well. So, what does this buy us? Well, for one, the main executable pretty much never has to be rebuilt. This means changing the application is as simple as swapping out DLLs. The idea behind this was to make the engine as modular and data driven as possible. This structure allows flexibility in how modular you wish to make your code by allowing you to split it into as many or few DLLs as you desire. This also allows me to easily embed my engine into other programs, such as an editor.
This is the first of hopefully more posts about the architecture of Shibboleth. Until next time!