Enhancing a room with items
Where we learn about how to add items to your room.
Overview
This adventure teaches you about adding virtual items to your room in Game On, taking you through understanding the required parts of the protocol and some ideas on how to handle commands. You’ll learn how to have items show up in your Room Description and how to support simple commands that interact with them.
Why Virtual Items?
Game On presents itself to users as a text-based adventure game. Within text adventures, the concepts of items and inventory are common.
The classic /take sword
and /kill dragon with sword
are great examples of how to make a room feel more like an interactive
experience and less like a technology demonstration.
For example, when you visit RecRoom in Game On (/sos
, /teleport RecRoom
), you see how it uses items to construct a puzzle that
keep users in the room entertained. Items in rooms are used in many ways, you could have them represent remote
services, or just be a signpost with instructions for how to interact with your room.
In this adventure, we add a Button
to the room and have it respond when the user pushes it.
Prerequisites
This adventure doesn’t require any prerequisites and may be attempted directly after completing the Java Sample Room.
Walkthrough
Protocol
Game On Rooms support the concepts of 'items' via the websocket protocol. This means there is a difference between sending a Room Description that has the items embedded in the description text..
And a description that leaves them out and declares them to the room via the protocol..
To understand how to do this, we need to look at the Game On Websocket Protocol..
The particular message we are interested in is the 'location' message, which carries the Room Description and other information back to Game On. It is this "other information" that we may use to tell the game about items in the room.
The location message is documented like this:
player,<playerId>,{
"type": "location",
"name": "Room name",
"fullName": "Room's descriptive full name",
"description": "Lots of text about what the room looks like",
"exits": {
"shortDirection" : "currentDescription for Player",
"N" : "a dark entranceway"
},
"commands": {
"/custom" : "Description of what command does"
},
"roomInventory": ["itemA","itemB"]
}
At the end is the "roomInventory" field which may be used to pass an Array of Strings that represent the items in the room. When the game renders the list in the web interface, it looks similar to this:
Room's descriptive full name
----------------------------
Lots of text about what the room looks like
You notice:
o itemA
o itemB
The itemA
, and itemB
have been called out by the the game’s web interface, because they were present in the roomInventory
part
of the location message from the Room.
Java Sample Room
Within the sample room, the location
message is built from details in the RoomDescription object, by the
RoomImplementation object, using the Message object’s utility method createLocationMessage
.
Here we see the method createLocationMessage
building up the response to send..
public static Message createLocationMessage(String userId, RoomDescription roomDescription) {
JsonObjectBuilder payload = Json.createObjectBuilder();
payload.add(TYPE, "location");
payload.add("name", roomDescription.getName());
payload.add("fullName", roomDescription.getFullName());
payload.add("description", roomDescription.getDescription());
// convert map of commands into JsonObject
JsonObject commands = roomDescription.getCommands();
if ( !commands.isEmpty()) {
payload.add("commands", commands);
}
// Convert list of items into json array
JsonArray inventory = roomDescription.getInventory();
if ( !inventory.isEmpty()) {
payload.add("roomInventory", inventory);
}
return new Message(Target.player, userId, payload.build().toString());
}
We see that the location
message is built as JSON and the items are being read from the RoomDescription
object passed as an argument.
The RoomDescription object offers the methods
public void addItem(String itemName);
public void removeItem(String itemName);
public JsonArray getInventory();
Which would allow a Room implementation to add/remove items from the room, and thus from the description returned to the user.
Over in the RoomImplementation we see the simple processCommand
method, that parses the input from the user
and carries out the appropriate action. In this case, we’re interested in the /look
command, which should trigger a location
response.
Sure enough, there within the switch statement, we see a location message being built & returned to the user.
case "/look":
case "/examine":
// See RoomCommandsTest#testHandle*Look*
// Treat look and examine the same (though you could make them do different things)
if ( remainder == null || remainder.contains("room") ) {
// This is looking at or examining the entire room. Send the player location message,
// which includes the room description and inventory
endpoint.sendMessage(session, Message.createLocationMessage(userId, roomDescription));
} else {
endpoint.sendMessage(session,
Message.createSpecificEvent(userId, LOOK_UNKNOWN));
}
break;
If we wanted to add additional behavior, perhaps to support /examine itemName
, this is where we could add it. Either as an extension
to the switch block handling /examine
and /look
, or via an entirely new command. If the item were a button, we might like to add
/push button
as a command and send an appropriate response.
Let’s look at adding that button now.
Adding our own item.
Firstly, find the postConstruct
method in the roomImplementation, and before the last log statement, add..
roomDescription.addItem("button");
Then locate the switch statement within the processCommand
method. Add a little code so that the 'else' block in the /look
and /examine
case, that used to look like:
is updated to look like:
Finally, lets add a little code to handle the /push
command for our button.
Go back to that postConstruct
method, and below your addItem("button")
line add:
roomDescription.addCommand("/push","Pushes an item, like, a button?");
That causes the room description to add our custom command to the location
response, so any user doing /help
in the room will
see /push
described as a command.
Now, back in the switch statement within the processCommand
method, add a new switch block that looks like…
case "/push":
// Handle the push command, response depends if user pushes button, or anything else.
if ( remainder.contains("button") ) {
endpoint.sendMessage(session,
Message.createBroadcastEvent(username+" pushes the button. Nothing Happens. Surprising.",
userId, "You push the big red button."));
} else {
endpoint.sendMessage(session, Message.createSpecificEvent(userId, "What do you want to push?));
}
break;
That bit is invoked when the first word of the input is /push
with remainder
set to whatever the rest of the command was.
If the user did /push button
or /push the button
etc, we’ll send them a message saying they pushed the button and send
everyone else a message saying Nothing Happened. If the user only does /push
by itself, we prompt them they should probably
say what they want to push.
Suggested extensions
- Add a novelty 'mystical fortune telling ball' that gives random fortunes when shaken.
- The parsing approach here is crude, consider how you could design a framework to support multiple items, each offering their own commands, and help text, and having an effect.
- Could you add/remove an item to the room dynamically at runtime? (remember the caching in RoomDescription)
- Perhaps via new
/additem
and/removeitem
commands? - Perhaps an object that appears based on the name of the player joining the room ?
- Perhaps via new
Conclusion
Items and commands are important parts of the Game On protocol and are designed to improve the end user experience with your room. You should now have a general understanding of the steps required to add items and handle them with commands.
Suggested further adventures
- Caching adventure - Learn about stateful items.
- JAX RS - Learn about invoking REST endpoint using JAX RS