Architecture Vision

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Ok, so apparently I'm "Lead Software Architect". ;) Previously things have been fairly quiet so I have been dedicating time to realising the architecture rather than writing up reports on it (or just generally been slack), but things are picking up now. So in this thread I wanted to share details on the architectural vision and what this means for developers. Feel free to chip in with any questions, suggestions or such.

First, some general goals of the architecture:
  • Strong modding support. There are a number of aspects to this
    • Avoiding conflicts - things like not fixing blocks to specific ids and namespacing all assets
    • Allowing the introduction of new behaviour and assets outside of the engine (so no jar modification hacks needed)
    • Allow some modding without java coding - instead using data to drive things (prefabs, block definitions)
    • When coding is involved make things modular, easy to use, powerful and with a good system for plugging in to existing systems.
    • Resilience to errors and change. An exception in a mod should not bring down the entire game. If a mod is updated with components changed, existing saves should not break.
    • Reasonably secure. This is particularly important if we want quality of life features like automatically downloading plugins from servers - we need to ensure that those mods can't format harddrives or do other such things.
    • Clear, well document API (not done yet) - describing what can be used, and what can't in mods
  • Separation between engine concerns and game logic/assets. This ties into modding support, but the engine should be concerned with core features like rendering, audio and voxel world support. Game logic comes through modules, which means that the entire set of modules can be changed to achieve completely different game play.
The overall structure of the project is
  • A core engine (Terasology.jar + underlying jars)
  • A number of modules (Core, TestMod, Minerals, etc)
The core engine is focused on theses core capabilities
  • Module loading
  • Asset Management
  • Core Asset Types
  • Audio
  • Input
  • Network
  • Console (not the commands, but the console itself)
  • Physics
  • Rendering
  • GUI
  • Voxel World
  • Entity System
  • Event System
  • Persistence
  • Main Menu / World creation and loading
  • Maths
  • Engine state (driving everything)
  • Time
  • Particle System (currently not present)
It will have a small set of core assets needed to run, but most will live in modules.

The modules contain:
  • Assets
  • Gameplay related components/systems/events
  • Chunk Generators and simulation systems (this needs work)
  • Potentially other extensions, such as new asset handles, new type handlers (for supporting new types in components)
Modules will be able to depend on other modules in a hierarchical fashion, which will help inform module selection and also feed into compilation. We probably also need some general grouping mechanisms and separation around "gametypes" (a selection of modules that, together with a gametype descriptor, form an overarching gameplay. Only one of these can be selected per world) and "mods" (a selection of modules that add on top of a gametype, or even any gametype). A gametype would be composed of a couple of major options (like what prefab to use for players, and what prefab to use for the player character, systems to load and systems to block), while a mod would have to rely on more general points of interaction (like intercepting a PlayerSpawned event and modifying the player entity).

The entity system is a central mechanism in all of this. The strong persistence mechanisms we have around components and entities enable saving and loading, prefabs and network serialization. Being able to mix existing components with new components allows granular reuse. New systems can be added to change the behaviour of existing entities or react to existing events. And prefabs allow the creation of new objects, using new or existing assets, with a mashup of existing behaviours - providing codeless modding.

The engine will supply a set of components and events to link entities to the core capabilities - rendering, audio, physics and collision, input. It will also provide APIs for working with them - such as the Physics API for doing traces, or a Time API to get ingame and real times.

The voxel world is the other major pillar. The World API is provided for gameplay interaction, with single and batch block changes. Underlying this is all our chunk logic. This hides most of the implementation details, although some knowledge of whether chunks are loaded is needed. Individual blocks are also provided as temporary and occasionally persistent entities for interactions.

Assets are loaded through an asset manager, which ties into the module system to make available assets from the currently active modules. All assets are identified by a uri - this allows them to be persisted and restored when used in components. Supported AssetTypes are currently hard coded in the AssetType enumeration, but this can be refactored to allow extension. AssetLoaders provide the loading implementation.

Networking is under active development, and is described further in the incubator thread.

For rendering, we have the primary assets of mesh, skeletal mesh, animation, texture, shader and material. Materials provide the combination of the shader + shader params. The mesh and skeletal mesh components provide the ability to make an entity visible with these assets. World rendering is also handled in the engine itself.

The engine uses a combination of discovery through annotations/interface implementation to find components/systems/events, and reflection for instantiation and generic interaction with them - this drives serialization. The use of reflection is not ideal in some areas, and will be replaced with bytecode manip in the future to improve performance (particularly serialization as it will be used heavily in multiplayer).
 

Cervator

Org Co-Founder & Project Lead
Contributor
Design
Logistics
SpecOps
Thanks for the nice write-up, Immortius - I know it is an on-going struggle to adhere to the architecture (and I'm a prime actor with my eager merges - mea culpa, that's my personal struggle with balancing eager contributions and well-structured code) and information like this helps. The more complete we can make the Modding APIs and the more we can encapsulate the modules the easier it'll be to adopt.

What are your thoughts on adding hooks in the engine that modules can attach to right now? Can we currently implement the sort of event interception you mention as an example? The most common question I see is how to hook into doing something in a module at player initialization time or at world start-up. Not sure if we can do that yet, if we just need to add more events and allow them to be caught, or if we need to make that concept itself work first.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
What are your thoughts on adding hooks in the engine that modules can attach to right now? Can we currently implement the sort of event interception you mention as an example? The most common question I see is how to hook into doing something in a module at player initialization time or at world start-up. Not sure if we can do that yet, if we just need to add more events and allow them to be caught, or if we need to make that concept itself work first.
Those are good questions. We are missing some hooks, which need to be added, but others are present. Primarily it would be good to send "WorldStarted" events to the World entity at start up, then systems can listen for those to do initial entity creation and such (this can't really be done from a system's initialise method as other systems haven't been initialised yet, so won't be able to react to changes". This would be hooked into with something like:

Code:
@ReceiveEvent(component = WorldComponent.class)
public void onWorldStart(WorldStartedEvent event, EntityRef player) {
  /// Do whatever here, including instatiating prefabs
}
We should also have specific PlayerJoined and PlayerCharacterSpawned events (this is referring to the separation added in the multiplayer arc where a Player entity is the more abstract connection of a player to the game, and a PlayerCharacter entity is their physical body). However this can be simulated in a pinch by subscribing to the AddComponentEvent:

Code:
@ReceiveEvent(component = PlayerComponent.class)
public void onPlayerCreated(AddComponentEvent event, EntityRef player) {
  /// Do whatever here, including adding mod-specific components to the player
}
The Add/Changed/Removed Component events work a little different from other events, in that a ReceiveEvent method is only sent if one of the components in the component list is the one added/changed/removed. Other ReceiveEvent methods are invoked when the entity just includes the components in the list.
 
Top