Headless support

Mike Kienenberger

Active Member
Contributor
Architecture
GUI
I hope I'm not intruding where I'm not wanted, but I grabbed a copy of msteiger/Terasology, branch headless, merged in MovingBlocks/Terasology/develop, and started working on it.

I took the basic idea of Environment that msteiger developed, copied it out of Engine-tests into engine, and then squeezed TerasologyEngine code into an LwjglEnvironment subclass.

The refactored code leaves no references to lwjgl in TerasologyEngine, is highly customizable, and still works. :)

https://github.com/mkienenb/Terasology/tree/headless

The next step is to create a HeadlessEnvironment, pull non-display code out of LwjglEnvironment, and probably make LwjglEnvironment inherit from that. Then start seeing what breaks.

I'm also interested in seeing whether a non-3d Environment is possible. I think it might well be with the latest NUI API changes.
 

Mike Kienenberger

Active Member
Contributor
Architecture
GUI
My headless branch is now runnable without errors. Start it with the -headless option. One obvious missing piece is starting in multiplayer without going through the screens, or some other interface for starting it with the right settings

All of the small pieces (assets, nui, gui, shader, possibly others) used to remove the run-time dependency on lwjgl have been submitted as PRs, and I think most of them are already merged. The rest require discussion of whether the Environment / Display pattern is a reasonable way to proceed.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
On my part... I was envisioning a system where the major subsystems (Rendering, Audio, etc) had an interface + implementations, so you would have a HeadlessRenderer and NullAudioManager. These would take care of registering their asset types and producing any implementation-dependent classes like the Canvas implementation. So maybe that is just a slight refactoring to what you have?
 

Mike Kienenberger

Active Member
Contributor
Architecture
GUI
I was also considering a rendering subsystem, although I hadn't really thought it out beyond "we need to separate out all of the classes related to a specific rendering system like lwjgl so that they are plug-able and optional". Because it was going to involve a lot of package changes, I was holding off on suggesting it for the time being. EDIT: On retrospect, this doesn't require a lot of package changes as the classes returned by a major sub-system don't have to change packages from where they are right now. That part can be deferred for now.

Environment right now represents not only the major subsystems, but also the setup order -- currently it's more of a collection of major-sub-systems configuration information + a partial provider of subsystem pluggability. So the environment(s) really needs to be broken out into various sub-systems, as well as separated out into a startup ordering system, along with a way to pick-and-choose which major-subsystems are being used.

We should identify an ordering or dependency between the various pieces that start up, and not just a default order of setup*() methods in a setup method. As much as possible, a major-subsystem-provider should not need to know what that order is, although it might need to provide a dependency for its own classes. Adding the dependencies will require a lot of extra meta-information, but I think it will reduce maintenance and major-sub-system implementations if it's explicitly described. In a perfect world, there is no setup() method, but instead the startup process itself determines the correct order to run each of the pieces.

Maybe this can be some kind of annotation (and I'm probably mangling the annotation syntax) like
Code:
@startup (provides=TextureAssetType, dependsOn=AssetManager, Display)
public void setupTextureAssetType()
 
@startup ({
    provides=Network, dependsOn=Config,Time),
    provides=WorldGenerator, dependsOn=ModuleManager}, etc)
public void setupManagers() {}
The dependsOn part should possibly be defined in the systems themselves, although maybe it's sufficient to put them on the setup methods.

I kind of know how should work conceptually, but I haven't worked out all of the details yet, so anyone please comment if you have thoughts in this area.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Correct me if I'm wrong, but we already have engine + state for most setup order. Environment doubles up on this. I don't think we need to reinvent this wheel. Ultimately it would be nice if the tests (the ones that require a running environment anyway) should be able to just plug in the Headless subsystems into engine, run engine with a StateTesting and test from there. And similarly for a headless server - run the engine with the headless subsystems and then inject a StateHeadlessServer (or straight to StateLoading).

Although handling the tests through the engine is tricky, because it enters into a game loop. Would need to refactor it a little, or possibly plug it into arquillian.

We can still make the subsystems themselves more generic, and maybe that is where some ordering information comes into play. But I don't think we should over do it for what probably only involves half a dozen subsystems at best (RenderingSubsystem, AudioSubsystem, InputSubsystem... maybe NetworkSubsystem? Nothing else springs immediately to mind). I don't think there is any dependencies between those.

Subsystems themselves may look something like:

Code:
interface EngineSubsystem {
 
    void initialize();
 
    void shutdown();
 
    void update(float delta);
 
}
There might be a slight complexity to support switching subsystems at runtime (for transforming into a dedicated server for instance).
 

Mike Kienenberger

Active Member
Contributor
Architecture
GUI
I was originally referring to the dependencies between all terasology systems, and not just the the major sub-systems. It was rather difficult to determine which systems, be they major sub-systems, or internal systems, were dependent upon each other. Perhaps that is beyond the needed scope of this issue. I went back and re-evaluated how much of this is needed for just the major sub-systems.

Having reviewed the refactoring of the initalization code out of TerasologyEngine to LwjglEnvironment, I can say that we will need at least two points of initialization and possibly three. Some parts of the graphic subsystem have to run before internal systems. Some have to run later, and some still later still.

Config has to be available first.

Configuring logging for lwjgl, LWJGLHelper.initNativeLibs(), and TimeLwjgl have to be done early on before much of anything else.

Then some more internal systems: ModuleManager, AssetManager, for sure, and various other kinds of internal systems which may or may not need to be initialized.

Then more display initialization.

Then more asset / sound initialization -- it's possible that this can be done later. I suspect openal sound may also need the double-initialization.

Then more display control initialization -- it's possible that this could have been done with the second stage of initialization. In fact, it's probably likely

So maybe this can be reduced to only two areas of initialization. I tried to avoid changing the order that the engine set up systems in most cases for this first draft, and I was trying to keep with the order listed in Environment as well since I thought this was the direction we wanted to take, but I can start testing to see whether it matters what order the sound/graphics/controls code is executed other than the two-stage part.

I think this can be done with a slight refactoring of TerasologyEngine which will remove the need to have the Environment class involved with setup ordering since that doesn't appear to be how we want to define initialization ordering.

On a barely-related note, maybe we should move setupModuleManager() into a separate class. This rule-set is frequently being adjusted, and the Engine class is huge already.

I'd also like to suggest moving the entire init() configuration setup into a separate class, even if we preserve the exact methods and method calls. When I did that using Environment, that dropped engine down from 800 lines to 300 lines, making it much more readable.

I haven't done anything with the state half of this engine+state equation yet, so I'm not yet sure how this will factor into things.
 

Mike Kienenberger

Active Member
Contributor
Architecture
GUI
[Moving this from our original private conversation]

Along with headless support, my goal is to also provide a 2d version of Terasology.

Why a 2D version?

Sometimes it is just an easier way to see the entire world for development.

For myself, my machine is right on the edge of being able to run Terasology. Ie, it is slow. Being able to do some development in a 2d mode would greatly improve my work flow. It would also let me, as a developer on a slow machine, see how systems run on a machine which is able to execute updates much faster than I am -- for example, the various issues with movement and behavior where Synopia's machine runs the updates at 100-to-200ms, and I'm in the 1000ms range. It's also fun. :) And it also proves that the engine architecture is able to support alternate view systems (ie, not lwjgl), even if 2d isn't exactly high on other people's list.

I'm all for a proper 2D environment. Maybe we can create something based on the 2D view in Cities? It uses a conversion function (a hashmap really) to derive a color (hue) from a Block. The height of the top layer defines the color saturation. The result is then rendered into a BufferedImage. Camera movements such as panning, zooming, etc. is also available.
My plan was to use the actual block tiles, and have it displayed and controlled like Dwarf Fortress does, but there's no reason why we can't make it an option to do both.

@mkienenb: I browsed through your code and it looks fine to me. I thought of suggesting Mockito to generate (empty) stub implementation for some classes, but maybe it's not worth having a dependency to a *testing* library. Let me know, if I can help you out (and where ;))
I wanted to get something available yesterday, but other things came up. Today is probably too busy. Maybe tomorrow, I will have a revised PR that will enable modular sub-systems for graphics, sound, and input. Once we have those, maybe you can revisit the engine-tests and rework them to use the new system?
 

Cervator

Org Co-Founder & Project Lead
Contributor
Design
Logistics
SpecOps
I remember we had a "slicing" prototype prepared quite a while ago that would show the world as of a particular y height but cut out everything above (it was a fixed camera thing but still 3d). Maybe a 2D tile view like that for the true DF style would be more handy when you want an alternate camera interface :)
 

Mike Kienenberger

Active Member
Contributor
Architecture
GUI
The 2d first draft is available on my github account if anyone wants to take a look.

https://github.com/mkienenb/Terasology/tree/2d-subsystem

I pulled a few pieces of the Cities module into the in-game world-rendering just to have something to quickly look at, but it's not nearly as interesting as msteiger's actual city 2d view rendering.

This also revealed a few more pieces that needed changing to support headless operation.
 

Mike Kienenberger

Active Member
Contributor
Architecture
GUI
I posted a few things in the NUI thread that should have been here instead. Moving the discussion....

I don't think individual systems need to be registered automatically based on differing implementations, so lwjgl/awt should not be options (they can be registered manually by the renderer). The actual options are a combination of whether there is a display (headless or not - or client/listen server/singleplayer or not), whether the machine is authoritive (server/singleplayer or not) and whether the machine is a remote client or not. The AWT implementation may count as headless for this purpose, because it doesn't render the normal way.
Yes, I'm running AWT as headless-equivalent.

I was thinking more of what happens down the road if we decide to support libgdx as well as lwjgl. Systems like BlockParticleEmitterSystem contain lwjgl code currently. I picked this one as an example because it was the first one I hit before adding isHeadless(), but there are others. If we are never going to support anything other than one 3d rendering system at any given point in development, then it doesn't matter. But if we are going to support both lwjgl and libgdx, then we need a way to pick the correct block particle emitter system, or provide an appropriate renderer for the emitter system. Either these systems need to be removed from automatic registration, or there needs to be some way to determine which one to pick in the system itself, such as a @registerSystem flag. Another way to do this would be to get the emitter system renderer from a RenderingSubsystemFactory. I needed to create and register this class to provide a WorldRenderer appropriate to the current rendering subsystem, and it seems like this might be the way to go for other renderer-specific classes which cannot be determined at initialization.
 

Cervator

Org Co-Founder & Project Lead
Contributor
Design
Logistics
SpecOps
Here's one bit that might then relate to that - currently the PC Facade is responsible for starting the PC application. I know the headless fun is all happening in engine, but the idea of Facades was that they'd be responsible for setting up / configuring the active front-end for the game, so on Android there'd be an Android Facade that starts the game instead of the PC facade.

Could the config of which flavor of lwjgl / libgdx etc be defined in the Facade? Right now it is just a single tiny class, a wrapper really. It is up in the air where we take it from there :)

Oh, and Architect badge :D
 

Mike Kienenberger

Active Member
Contributor
Architecture
GUI
Cervator,

You're exactly right. We're configuring all the headless/lwjgl/awt goodness in the PC facade.

Here's a link if you want to see how it's done so far. Right now, in the same file, for headless I am also specifying an alternate GameState, which is not reflected in the example below.

https://github.com/mkienenb/Terasology/blob/7e39ca43f683f35034a79b5c94fe85449508c82e/facades/PC/src/main/java/org/terasology/engine/Terasology.java

As you mentioned, this is only one possible approach. Another way would be to have a separate main java class for each type, but at present there's not a lot of advantages to doing that yet.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
I think any system that has implementation specific rendering code should be registered manually by the renderer. This also fits in with potentially breaking them out into separate libraries.

This also goes for systems that result from any engine subsystem.

One possibility when it comes to alternate subsystems would be having specifying the type in config - this would only be for the standard 'headed' options though. But this would allow the different implementations to plug in easily.

The awt mode is a more peculiar beast, so it make more sense for it to be in a facade I think.

Headless should be built in though to support headless instances and possibly switching to headless at runtime.
 

Mike Kienenberger

Active Member
Contributor
Architecture
GUI
Update! Cervator and I connected to the same headless server a few minutes ago! I've also started this headless server from a remote ssh connection. So we're mostly there at this point. Now it's just the death of a thousand papercuts to get it all committed and cleaned up. :)

PR for headless support is right here (although most of the individual pieces are separated out for easier review). Note that this version uses your last config settings to generate a new game each time you run it.

Use the -headless option when you start Terasology.java (not sure how that is done from gradlew yet).

https://github.com/mkienenb/Terasology/tree/PR-headless-support

2d awt support updated here. Still doesn't do much once the game starts. But it's a pretty cool demo of how easily NUI can be ported to a different environment. I still have a few minor graphics artifacts to sort out for the bitmap drawing and clipping.

https://github.com/mkienenb/Terasology/tree/2d-subsystem

Edit:

Headless support is now in terasology/develop.

./gradlew startServer will start in headless mode.

Thanks to Cervator for setting up startServer and to Immortius for reviewing a great many PRs to get us to this point!

The 2d awt version now has working in-game mouse, keyboard, and NUI HUD support. You still need to pull it from my branch at this point. No support for mesh items (blocks) drawing. Need to change the world renderer to do something more interesting now.
 

Mike Kienenberger

Active Member
Contributor
Architecture
GUI
At this point, we probably should create a separate thread for the AWT facade.
In any case, the AWT Facade is available now -- no more keeping a separate engine branch.

./gradlew fetchFacadeAWT should pull it in, although there's a PR to change it to pull from Terasology to MovingBlocks.

Fixed console output (although still need msteiger enhanced text support), added simplistic mesh drawing support.
NUI support has reached the "good enough" stage except for the missing blinking text insertion cursor.
 

Cervator

Org Co-Founder & Project Lead
Contributor
Design
Logistics
SpecOps
Updates applied, fully functional now including in IntelliJ :)

Nice work as always, now get to making an AWT Incubator already! :D

Would it be an idea to do another incubator for headless / subsystem maintenance instead of this dev portal thread?
 
Top