Controller

The Controller component of SolarisEngine is a JavaScript program built on top of Mineflayer. It connects via TCP to the controller bots of other players. Through communication and the high-level API of Mineflayer it makes the bots engage in collaborative gameplay. The controller is built around episode types. An episode type represents a specific multiplayer scenario that the bots engage in. To ensure diversity and good coverage of various game mechanics, it has a collection of 14 training episode types. And to ensure a proper evaluation of the core multiplayer mechanics, it has a collection of separate 7 eval episode types. All episode types currently support only two players.

Program Lifecycle

Throughout the life of the controller program, it establishes a connection with the server and creates a mineflayer.Bot() instance just once at startup. After that, it reuses the same bot instance to collect as many episodes as specified in the --episodes_num CLI arg. The entry point to the controller is the episodes-loop.getOnSpawnFn() function, which Mineflayer calls when the bot has connected to the server. The function runs in a loop, sampling random episodes, executing them, and sending actions to the separate action_recorder process to be saved as json files on disk.

To ensure the data collection doesn’t get interrupted by the player dying, the controller gives the players infinite resistance, water breathing, and no fall damage via RCON at the program startup.

Episodes Loop

Controllers of players share the same random generator, sharedBotRng that they use to sample the same episode type randomly on every loop iteration. To ensure that the episode starts in a clean state, and in a new terrain, the controller teleports the players to a new random location and resets their inventories before starting to record the episode.

The episode loop has an error-handling mechanism that catches any error that might occur during episode execution and notifies other players about it. They collectively abort the current episode and progress to the next one.

Episode Progression

All episode types inherit episode-handlers.base-episode.BaseEpisode that provides them with the basic episode lifecycle: setupEpisode, entryPoint, tearDownEpisode, and stop-phase coordination. An episode consists of multiple phases. At the beginning and end of a phase, all players wait for each other and exchange arbitrary values needed for the phase progression. This phasing mechanism, combined with the sharedBotRng ensures the bots progress through the episode in synchronization. All episode types are instances of a concrete game scenario that runs from start to finish. They are built on top of primitives that provide reusable API like building, digging, fighting, or moving.