The MC-Saar-Instruct platform
MC-Saar-Instruct is an experimentation platform leveraging Minecraft for instruction giving tasks. Participants can log into the server with a standard Minecraft client. We provide three components:
- a Minecraft server plugin
- a broker organizing and logging all experiments
- an architect server, the component giving instructions.
If you use this work, please cite our publication on MC-Saar-Instruct. It is also a good starting point to read about MC-Saar-Instruct.
How experiments with MC-Saar-Instruct work
When a player logs into the Minecraft server, the server connects to the broker. The broker decides which instruction giving system (architect) the player should be paired to an the scenario that should be played. Both the Minecraft server and the architect are told which scenario to play and a bi-directional message channel between MC server and architect is opened.
The MC Server sends updates for every change in the world (blocks placed and destroyed) as well as all text messages typed by the player and regular updates for the position and orientation of the player (every 100ms). The architect can send text messages and they are shown to the player in Minecraft. The architect is responsible for changing the status of the game (e.g., to “sucessully completed”) and checking whether the goal of the target is met.
The broker logs all messages between architect and player into a database and provides a web front-end. Upon task completion, the broker takes over the message channel and runs a questionnaire with the player, which is again logged to the database.
The architecture of MC-Saar-Instruct
The overall system is divided into three components (see above). There is only one Minecraft server running and only one broker. The architects are hosted by architect servers, one server for each kind of architect. MC server, broker and architect servers can all run on different machines and the architects managed by the architect server could all run on their own machine as well if necessary.
The overall architecture looks like this:
In our reference implementation, each architect is only instantiated for the duration of a single game and the Architect Server forwards the RPC calls made by the broker unchanged. The architects only need to implement an interface consisting four functions:
void handleStatusInformation(StatusM); void handleBlockPlaced(BlockPlacedM); void handleBlockDestroyed(BlockDestroyedM); String getArchitectInformation();
handleBlockPlacedis called when a block is placed,
handle BlockDestroyedwhen a block is destroyed,
handleStatusInformationfor every update of the position and orientation of the player,
getArchitectInformationto obtain the name of the architect.
The Architect Server managing the architects can be re-used without changes and an architect can be implemented in 80 lines of code in Java, see the DummyArchitect we ship (it contains more lines because it articially spins up threads to simulate processing).
This is all information you only need if you want to rewrite parts of the infrastructure. It is not needed to write new architects.
Messaging between the components is handled with grpc, a high-performance, type-safe Remote Procedure Call library. grpc handle all low-level aspects of the communication and new architect servers can be written in any language supported by grpc.
On startup, the broker (
br) connects to all ArchitectServers (
by calling ther
Hello method. When a user connects to the Minecraft
ms), it sends a message to the broker:
ms -> br startGame(IPAddress, PlayerName): (Scenario, GameId)
The broker selects a scenario, creates a new ID for this game and connects to an ArchitectServer:
br -> as StartGame(Scenario, GameId): None
Upon completion, the (still running)
startGame method call returns
The Client then obtains the message channel:
ms -> br GetMessageChannel (GameId): stream (TextMessage)
The broker calls the same method on the ArchitectServer
br -> as GetMessageChannel (GameId): stream (TextMessage)
and returns that message channel to the minecraft server. the ArchitectServer uses that channel to push instructions to the user.
The reason for setting up the message channel happens in a separate
call and not as part of
StartGame is that grpc cannot return several
values for a single call; we cannot obtain a
tuple and a
TextMessage stream at the same time.
Now everything is set up.
ms now calls
HandleBlockDestroyed whenever it is
applicable. The broker forwards these calls by calling the same
methods on the ArchitectServer. (The ArchitectServer forwards these
calls to the correct architect, but this is handled by direct method
The architect is the component responsible for deciding when a game
changes state (e.g. the task was succsessfully completed). This is
indicated by a flag in
TextMessage. The broker notices this and
takes appropriate action such as starting the post-game questionnaire.
When a player disconnects, the Minecraft server calls
ms -> br EndGame (GameId)
which is again forwarded to the server:
br -> as EndGame (GameId)
The architect server shuts down the corresponding architect (in our implementation at least) and the broker also clears that game from its data structures and logs this event to the database.
The last method is
EndAllGames() from the ArchitectServer. It
terminates all games; this method is meant for an orderly shutdown
initiated by the broker.