View on GitHub


Resources for the SFB1102 A7 project

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:

The Minecraft server plugin and the broker and architect server are available on GitHub.

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();

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).

Messaging interface

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 (as) by calling ther Hello method. When a user connects to the Minecraft server (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 (Scenario, GameId).

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 (Scenario, GameId) tuple and a TextMessage stream at the same time.

Now everything is set up. ms now calls HandleStatusInformation, HandleBlockPlaced and 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 calls.)

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.