{"id":57511,"date":"2018-12-21T04:19:12","date_gmt":"2018-12-21T11:19:12","guid":{"rendered":"http:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/"},"modified":"2018-12-21T04:19:12","modified_gmt":"2018-12-21T11:19:12","slug":"writing-a-multiplayer-text-adventure-engine-in-node-js","status":"publish","type":"post","link":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/","title":{"rendered":"Writing A Multiplayer Text Adventure Engine In Node.js"},"content":{"rendered":"<link rel=\"canonical\" href=\"https:\/\/www.smashingmagazine.com\/2018\/12\/multiplayer-text-adventure-engine-node-js\/\"><title>Writing A Multiplayer Text Adventure Engine In Node.js<\/title><\/p>\n<article>\n<header>\n<h1>Writing A Multiplayer Text Adventure Engine In Node.js<\/h1>\n<address>Fernando Doglio<\/address>\n<p>                  <time datetime=\"2018-12-20T14:05:34+01:00\">2018-12-20T14:05:34+01:00<\/time><time datetime=\"2018-12-20T14:16:59+00:00\">2018-12-20T14:16:59+00:00<\/time><\/header>\n<p>Text adventures were one of the first forms of digital role-playing games out there, back when games had no graphics and all you had was your own imagination and the description you read on the black screen of your CRT monitor.<\/p>\n<p>If we want to get nostalgic, maybe the name <a href=\"https:\/\/en.wikipedia.org\/wiki\/Colossal_Cave_Adventure\">Colossal Cave Adventure<\/a> (or just Adventure, as it was originally named) rings a bell. That was the very first text adventure game ever made.<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/dd0874a7-eb14-4d00-bd88-f355dd49db49\/multiplayer-text-adventure-you-are-standing.jpg?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-you-are-standing.jpg 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/dd0874a7-eb14-4d00-bd88-f355dd49db49\/multiplayer-text-adventure-you-are-standing.jpg 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/dd0874a7-eb14-4d00-bd88-f355dd49db49\/multiplayer-text-adventure-you-are-standing.jpg 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/dd0874a7-eb14-4d00-bd88-f355dd49db49\/multiplayer-text-adventure-you-are-standing.jpg 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/dd0874a7-eb14-4d00-bd88-f355dd49db49\/multiplayer-text-adventure-you-are-standing.jpg 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-you-are-standing.jpg?w=900\" sizes=\"100vw\" alt=\"A picture of an actual text adventure from back in the day\"><\/a><figcaption>\n\t\t\tA picture of an actual text adventure from back in the day. (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/dd0874a7-eb14-4d00-bd88-f355dd49db49\/multiplayer-text-adventure-you-are-standing.jpg\">Large preview<\/a>)<br \/>\n\t\t<\/figcaption><\/figure>\n<p>The image above is how you\u2019d actually see the game, a far cry from our current top AAA adventure games. That being said, they were fun to play and would steal hundreds of hours of your time, as you sat in front of that text, alone, trying to figure out how to beat it.<\/p>\n<p>Understandably so, text adventures have been replaced over the years by games that present better visuals (although, one could argue that a lot of them have sacrificed story for graphics) and, especially in the past few years, the increasing ability to collaborate with other friends and play together. This particular feature is one that the original text adventures lacked, and one that I want to bring back in this article.<\/p>\n<aside data-audience=\"non-subscriber\" data-remove=\"true\">\n<div>\n<div>\n<p>\n              Our new book, in which Alla Kholmatova explores<br \/>\n              how to create <strong>effective and maintainable design systems<\/strong> to design great digital products. Meet <strong><a href=\"https:\/\/www.smashingmagazine.com\/printed-books\/design-systems\/\">Design Systems<\/a><\/strong>, with common traps, gotchas and the lessons Alla has learned over the years.\n          <\/p>\n<p>          <a href=\"https:\/\/smashed.by\/uxpaneldsbook\"><br \/>\n          <button><br \/>\n            Table of Contents\u00a0?<br \/>\n          <\/button><br \/>\n        <\/a>\n      <\/div>\n<div>\n        <a href=\"https:\/\/smashed.by\/uxpaneldsbook\"><\/p>\n<div>\n          <img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/design-systems-hardcover-book.png?w=900\"><\/div>\n<p>      <\/a>\n      <\/div>\n<\/p><\/div>\n<\/aside>\n<h3>Our Goal<\/h3>\n<p>The whole point of this endeavour, as you have probably guessed by now from the title of this article, is to create a text adventure engine that allows you to share the adventure with friends, enabling you to collaborate with them similarly to how you would during a Dungeons &#038; Dragons game (in which, just like with the good ol\u2019 text adventures, there are no graphics to look at).<\/p>\n<p>In creating the engine, the chat server and the client is quite a lot of work. In this article, I\u2019ll be showing you the design phase, explaining things like the architecture behind the engine, how the client will interact with the servers, and what the rules of this game will be.<\/p>\n<p>Just to give you some visual aid of what this is going to look like, here is my goal:<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/9624ffad-4e17-4383-9255-d5bff2f32531\/multiplayer-text-adventure-ui-wireframe.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-ui-wireframe.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/9624ffad-4e17-4383-9255-d5bff2f32531\/multiplayer-text-adventure-ui-wireframe.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/9624ffad-4e17-4383-9255-d5bff2f32531\/multiplayer-text-adventure-ui-wireframe.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/9624ffad-4e17-4383-9255-d5bff2f32531\/multiplayer-text-adventure-ui-wireframe.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/9624ffad-4e17-4383-9255-d5bff2f32531\/multiplayer-text-adventure-ui-wireframe.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-ui-wireframe.png?w=900\" sizes=\"100vw\" alt=\"General wireframe for the final UI of the game client\"><\/a><figcaption>\n\t\t\tGeneral wireframe for the final UI of the game client (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/9624ffad-4e17-4383-9255-d5bff2f32531\/multiplayer-text-adventure-ui-wireframe.png\">Large preview<\/a>)<br \/>\n\t\t<\/figcaption><\/figure>\n<p>That is our goal. Once we get there, you\u2019ll have screenshots instead of quick and dirty mockups. So, let\u2019s get down with the process. The first thing we\u2019ll cover is the design of the whole thing. Then, we\u2019ll cover the most relevant tools I\u2019ll be using to code this. Finally, I\u2019ll show you some of the most relevant bits of code (with a link to the full repository, of course).<\/p>\n<p>Hopefully, by the end, you\u2019ll find yourself creating new text adventures to try them out with friends!<\/p>\n<h3>Design Phase<\/h3>\n<p>For the design phase, I\u2019m going to cover our overall blueprint. I\u2019ll try my best not to bore you to death, but at the same time, I think it\u2019s important to show some of the behind-the-scenes stuff that needs to happen before laying down your first line of code.<\/p>\n<p>The four components I want to cover here with a decent amount of detail are:<\/p>\n<ul>\n<li><strong>The engine<\/strong><br \/>\nThis is going to be the main game server. The game rules will be implemented here, and it\u2019ll provide a technologically agnostic interface for any type of client to consume. We\u2019ll implement a terminal client, but you could do the same with a web browser client or any other type you\u2019d like.<\/li>\n<li><strong>The chat server<\/strong><br \/>\nBecause it\u2019s complex enough to have its own article, this service is also going to have its own module. The chat server will take care of letting players communicate with each other during the game.<\/li>\n<li><strong>The client<\/strong><br \/>\nAs stated earlier, this will be a terminal client, one that, ideally, will look similar to the mockup from earlier. It will make use of the services provided by both the engine and the chat server.<\/li>\n<li><strong>Games (JSON files)<\/strong><br \/>\nFinally, I\u2019ll go over the definition of the actual games. The whole point of this is to create an engine that can run any game, as long as your game file complies with the engine\u2019s requirements. So, even though this will not require coding, I\u2019ll explain how I\u2019ll structure the adventure files in order to write our own adventures in the future.<\/li>\n<\/ul>\n<h4>The Engine<\/h4>\n<p>The game engine, or game server, will be a REST API and will provide all of the required functionality.<\/p>\n<p>I went for a REST API simply because \u2014 for this type of game \u2014 the delay added by HTTP and its asynchronous nature will not cause any trouble. We will, however, have to go a different route for the chat server. But before we start defining endpoints for our API, we need to define what the engine will be capable of. So, let\u2019s get to it.<\/p>\n<table data-tablesaw-mode=\"swipe\" data-tablesaw-minimap>\n<thead>\n<tr>\n<th data-tablesaw-priority=\"persist\">Feature<\/th>\n<th>Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Join a game<\/td>\n<td>A player will be able to join a game by specifying the game\u2019s ID.<\/td>\n<\/tr>\n<tr>\n<td>Create a new game<\/td>\n<td>A player can also create a new game instance. The engine should return an ID, so that others can use it to join.<\/td>\n<\/tr>\n<tr>\n<td>Return scene<\/td>\n<td>This feature should return the current scene where the party is located. Basically, it\u2019ll return the description, with all of the associated information (possible actions, objects in it, etc.).<\/td>\n<\/tr>\n<tr>\n<td>Interact with scene<\/td>\n<td>This is going to be one of the most complex ones, because it will take a command from the client and perform that action \u2014 things like move, push, take, look, read, to name just a few.<\/td>\n<\/tr>\n<tr>\n<td>Check inventory<\/td>\n<td>Although this is a way to interact with the game, it does not directly relate to the scene. So, checking the inventory for each player will be considered a different action.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h5>A Word About Movement<\/h5>\n<p>We need a way to measure distances in the game because moving through the adventure is one of the core actions a player can take. We will be using this number as a measure of time, just to simplify the gameplay. Measuring time with an actual clock might not be the best, considering these type of games have turn-based actions, such as combat. Instead, we\u2019ll use distance to measure time (meaning that a distance of 8 will require more time to traverse than one of 2, thus allowing us to do things like add effects to players that last for a set amount of \u201cdistance points\u201d).<\/p>\n<p>Another important aspect to consider about movement is that we\u2019re not playing alone. For simplicity\u2019s sake, the engine will not let players split the party (although that could be an interesting improvement for the future). The initial version of this module will only let everyone move wherever the majority of the party decides. So, moving will have to be done by consensus, meaning that every move action will wait for the majority of the party to request it before taking place.<\/p>\n<div><\/div>\n<h5>Combat<\/h5>\n<p>Combat is another very important aspect of these types of games, and one that we\u2019ll have to consider adding to our engine; otherwise, we\u2019ll end up missing on some of the fun.<\/p>\n<p>This is not something that needs to be reinvented, to be honest. Turn-based party combat has been around for decades, so we\u2019ll just implement a version of that mechanic. We\u2019ll be mixing it up with the Dungeons &#038; Dragons concept of \u201cinitiative\u201d, rolling a random number in order to keep the combat a bit more dynamic.<\/p>\n<p>In other words, the order in which everyone involved in a fight gets to pick their action will be randomized, and that includes the enemies.<\/p>\n<p>Finally (although I\u2019ll go over this in more detail below), you\u2019ll have items that you can pick up with a set \u201cdamage\u201d number. These are the items you\u2019ll be able to use during combat; anything that doesn\u2019t have that property will cause 0 damage to your enemies. We\u2019ll probably add a message when you try to use those objects to fight, so that you know that what you\u2019re trying to do makes no sense.<\/p>\n<h5>Client-Server Interaction<\/h5>\n<p>Let\u2019s see now how a given client would interact with our server using the previously defined functionality (not thinking about endpoints yet, but we\u2019ll get there in a sec):<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/0ee944eb-9604-41b1-bea3-d067d2f84f27\/multiplayer-text-adventure-start-new-game.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-start-new-game.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/0ee944eb-9604-41b1-bea3-d067d2f84f27\/multiplayer-text-adventure-start-new-game.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/0ee944eb-9604-41b1-bea3-d067d2f84f27\/multiplayer-text-adventure-start-new-game.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/0ee944eb-9604-41b1-bea3-d067d2f84f27\/multiplayer-text-adventure-start-new-game.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/0ee944eb-9604-41b1-bea3-d067d2f84f27\/multiplayer-text-adventure-start-new-game.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-start-new-game.png?w=900\" sizes=\"100vw\" alt=\"\"><\/a><figcaption>\n\t\t\t(<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/0ee944eb-9604-41b1-bea3-d067d2f84f27\/multiplayer-text-adventure-start-new-game.png\">Large preview<\/a>)<br \/>\n\t\t<\/figcaption><\/figure>\n<p>The initial interaction between the client and the server (from the point of view of the server) is the start of a new game, and the steps for it are as follows:<\/p>\n<ol>\n<li><strong>Create a new game<\/strong>.<br \/>\nThe client requests the creation of a new game from the server.<\/li>\n<li><strong>Create chat room<\/strong>.<br \/>\nAlthough the name doesn\u2019t specify it, the server is not just creating a chatroom in the chat server, but also setting up everything it needs in order to allow a set of players to play through an adventure.<\/li>\n<li><strong>Return game\u2019s meta data<\/strong>.<br \/>\nOnce the game has been created by the server and the chat room is in place for the players, the client will need that information for subsequent requests. This will mostly be a set of IDs the clients can use to identify themselves and the current game they want to join (more on that in a second).<\/li>\n<li><strong>Manually share game ID<\/strong>.<br \/>\nThis step will have to be done by the players themselves. We could come up with some sort of sharing mechanism, but I will leave that on the wish list for future improvements.<\/li>\n<li><strong>Join the game<\/strong>.<br \/>\nThis one is pretty straightforward. Ince everyone has the game ID, they\u2019ll join the adventure using their client applications.<\/li>\n<li><strong>Join their chat room<\/strong>.<br \/>\nFinally, the players\u2019 client apps will use the game\u2019s metadata to join their adventure\u2019s chat room. This is the last step required pre-game. Once this is all done, then the players are ready to start adventuring!<\/li>\n<\/ol>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/a74b2f8b-4fe8-46f4-9c45-4787cc38868c\/multiplayer-text-adventure-flow.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-flow.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/a74b2f8b-4fe8-46f4-9c45-4787cc38868c\/multiplayer-text-adventure-flow.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/a74b2f8b-4fe8-46f4-9c45-4787cc38868c\/multiplayer-text-adventure-flow.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/a74b2f8b-4fe8-46f4-9c45-4787cc38868c\/multiplayer-text-adventure-flow.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/a74b2f8b-4fe8-46f4-9c45-4787cc38868c\/multiplayer-text-adventure-flow.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-flow.png?w=900\" sizes=\"100vw\" alt=\"Action order for an existing game\"><\/a><figcaption>\n\t\t\tAction order for an existing game (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/a74b2f8b-4fe8-46f4-9c45-4787cc38868c\/multiplayer-text-adventure-flow.png\">Large preview<\/a>)<br \/>\n\t\t<\/figcaption><\/figure>\n<p>Once the prerequisites have all been met, players can start playing the adventure, sharing their thoughts through the party chat, and advancing the story. The diagram above shows the four steps required for that.<\/p>\n<p>The following steps will run as part of the game loop, meaning that they will be repeated constantly until the game ends.<\/p>\n<ol>\n<li><strong>Request scene<\/strong>.<br \/>\nThe client app will request the metadata for the current scene. This is the first step in every iteration of the loop.<\/li>\n<li><strong>Return the meta data<\/strong>.<br \/>\nThe server will, in turn, send back the metadata for the current scene. This information will include things like a general description, the objects found inside it, and how they relate to each other.<\/li>\n<li><strong>Send command<\/strong>.<br \/>\nThis is where the fun begins. This is the main input from the player. It\u2019ll contain the action they want to perform and, optionally, the target of that action (for example, blow candle, grab rock, and so on).<\/li>\n<li><strong>Return the reaction to the command sent<\/strong>.<br \/>\nThis could simply be step two, but for clarity, I added it as an extra step. The main difference is that step two could be considered the beginning of this loop, whereas this one takes into account that you\u2019re already playing, and, thus, the server needs to understand who this action is going to affect (either a single player or all players).<\/li>\n<\/ol>\n<p>As an extra step, although not really part of the flow, the server will notify clients about status updates that are relevant to them.<\/p>\n<p>The reason for this extra recurring step is because of the updates a player can receive from the actions of other players. Recall the requirement for moving from one place to another; as I said before, once the majority of the players have chosen a direction, then all players will move (no input from all players is required).<\/p>\n<p>The interesting bit here is that HTTP (we\u2019ve already mentioned that the server is going to be a REST API) does not allow for this type of behavior. So, our options are:<\/p>\n<ol>\n<li>perform polling every X amount of seconds from the client,<\/li>\n<li>use some sort of notification system that works in parallel with the client-server connection.<\/li>\n<\/ol>\n<p>In my experience, I tend to prefer option 2. In fact, I would (and will for this article) use Redis for this kind of behavior.<\/p>\n<p>The following diagram demonstrates the dependencies between services.<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/d11f4a78-7cf0-4dfb-a595-7bec8bbfab55\/multiplayer-text-adventure-client-server.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-client-server.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/d11f4a78-7cf0-4dfb-a595-7bec8bbfab55\/multiplayer-text-adventure-client-server.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/d11f4a78-7cf0-4dfb-a595-7bec8bbfab55\/multiplayer-text-adventure-client-server.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/d11f4a78-7cf0-4dfb-a595-7bec8bbfab55\/multiplayer-text-adventure-client-server.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/d11f4a78-7cf0-4dfb-a595-7bec8bbfab55\/multiplayer-text-adventure-client-server.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-client-server.png?w=900\" sizes=\"100vw\" alt=\"Interactions between an client app and the game engine\"><\/a><figcaption>\n\t\t\tInteractions between an client app and the game engine (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/d11f4a78-7cf0-4dfb-a595-7bec8bbfab55\/multiplayer-text-adventure-client-server.png\">Large preview<\/a>)<br \/>\n\t\t<\/figcaption><\/figure>\n<h4>The Chat Server<\/h4>\n<p>I will leave the details of the design of this module for the development phase (which is not a part of this article). That being said, there are things we can decide.<\/p>\n<p>One thing we can define is the set of the restrictions for the server, which will simplify our work down the line. And if we play our cards right, we might end up with a service that provides a robust interface, thus allowing us to, eventually, extend or even change the implementation to provide fewer restrictions without affecting the game at all.<\/p>\n<ul>\n<li><strong>There will be only one room per party.<\/strong><br \/>\nWe will not let subgroups be created. This goes hand in hand with not letting the party split. Maybe once we implement that enhancement, allowing for subgroup and custom chat room creation would be a good idea.<\/li>\n<li><strong>There will be no private messages.<\/strong><br \/>\nThis is purely for simplification purposes, but having a group chat is already good enough; we don\u2019t need private messages right now. Remember that whenever you\u2019re working on your minimum viable product, try to avoid going down the rabbit hole of unnecessary features; it\u2019s a dangerous path and one that is hard to get out of.<\/li>\n<li><strong>We will not persist messages.<\/strong><br \/>\nIn other words, if you leave the party, you\u2019ll lose the messages. This will hugely simplify our task, because we won\u2019t have to deal with any type of data storage, nor will we have to waste time deciding on the best data structure to store and recover old messages. It\u2019ll all live in memory, and it will stay there for as long as the chat room is active. Once it\u2019s closed, we\u2019ll simply say goodbye to them!<\/li>\n<li><strong>Communication will be done over sockets<\/strong>.<br \/>\nSadly, our client will have to handle a double communication channel: a RESTful one for the game engine and a socket for the chat server. This might increase the complexity of the client a bit, but at the same time, it will use the best methods of communication for every module. (There is no real point in forcing REST on our chat server or forcing sockets on our game server. That approach would increase the complexity of the server-side code, which is the one also handling the business logic, so let\u2019s focus on that side for now.)<\/li>\n<\/ul>\n<p>That\u2019s it for the chat server. After all, it will not be complex, at least not initially. There is more to do when it\u2019s time to start coding it, but for this article, it is more than enough information.<\/p>\n<h4>The Client<\/h4>\n<p>This is the final module that requires coding, and it is going to be our dumbest one of the lot. As a rule of thumb, I prefer to have my clients dumb and my servers smart. That way, creating new clients for the server becomes much easier.<\/p>\n<p>Just so we\u2019re on the same page, here is the high-level architecture that we should end up with.<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/74cb1a65-235d-4410-85c3-640e48b2d668\/multiplayer-text-adventure-full-client-interaction.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-full-client-interaction.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/74cb1a65-235d-4410-85c3-640e48b2d668\/multiplayer-text-adventure-full-client-interaction.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/74cb1a65-235d-4410-85c3-640e48b2d668\/multiplayer-text-adventure-full-client-interaction.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/74cb1a65-235d-4410-85c3-640e48b2d668\/multiplayer-text-adventure-full-client-interaction.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/74cb1a65-235d-4410-85c3-640e48b2d668\/multiplayer-text-adventure-full-client-interaction.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-full-client-interaction.png?w=900\" sizes=\"100vw\" alt=\"Final high level architecture of the entire development\"><\/a><figcaption>\n\t\t\tFinal high level architecture of the entire development (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/74cb1a65-235d-4410-85c3-640e48b2d668\/multiplayer-text-adventure-full-client-interaction.png\">Large preview<\/a>)<br \/>\n\t\t<\/figcaption><\/figure>\n<p>Our simple ClI client will not implement anything very complex. In fact, the most complicated bit we\u2019ll have to tackle is the actual UI, because it\u2019s a text-based interface.<\/p>\n<p>That being said, the functionality that the client application will have to implement is as follows:<\/p>\n<ol>\n<li><strong>Create a new game<\/strong>.<br \/>\nBecause I want to keep things as simple as possible, this will only be done through the CLI interface. The actual UI will only be used after joining a game, which brings us to the next point.<\/li>\n<li><strong>Join an existing game<\/strong>.<br \/>\nGiven the game\u2019s code returned from the previous point, players can use it to join in. Again, this is something you should be able to do without a UI, so this functionality will be part of the process required to start using the text UI.<\/li>\n<li><strong>Parse game definition files<\/strong>.<br \/>\nWe\u2019ll discuss these in a bit, but the client should be able to understand these files in order to know what to show and know how to use that data.<\/li>\n<li><strong>Interact with the adventure.<\/strong><br \/>\nBasically, this gives the player the ability to interact with the environment described at any given time.<\/li>\n<li><strong>Maintain an inventory for each player<\/strong>.<br \/>\nEach instance of the client will contain an in-memory list of items. This list is going to be backed up.<\/li>\n<li><strong>Support chat<\/strong>.<br \/>\nThe client app needs to also connect to the chat server and log the user into the party\u2019s chat room.<\/li>\n<\/ol>\n<p>More on the client\u2019s internal structure and design later. In the meantime, let\u2019s finish the design stage with the last bit of preparation: the game files.<\/p>\n<h4>The Game: JSON Files<\/h4>\n<p>This is where it gets interesting because up to now, I\u2019ve covered basic microservices definitions. Some of them might speak REST, and others might work with sockets, but in essence, they\u2019re all the same: You define them, you code them, and they provide a service.<\/p>\n<p>For this particular component, I\u2019m not planning on coding anything, yet we need to design it. Basically, we\u2019re implementing a sort of protocol for defining our game, the scenes inside it and everything inside them.<\/p>\n<p>If you think about it, a text adventure is, at its core, basically a set of rooms connected to each other, and inside them are \u201cthings\u201d you can interact with, all tied together with a, hopefully, decent story. Now, our engine will not take care of that last part; that part will be up to you. But for the rest, there is hope.<\/p>\n<p>Now, going back to the set of interconnected rooms, that to me sounds like a graph, and if we also add the concept of distance or movement speed that I mentioned earlier, we have a weighted graph. And that is just a set of nodes that have a weight (or just a number \u2014 don\u2019t worry about what it\u2019s called) that represents that path between them. Here is a visual (I love learning by seeing, so just look at the image, OK?):<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/c89fe792-35c5-44e2-ba46-8fb5ffdcc1c2\/multiplayer-text-adventure-graph.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-graph.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/c89fe792-35c5-44e2-ba46-8fb5ffdcc1c2\/multiplayer-text-adventure-graph.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/c89fe792-35c5-44e2-ba46-8fb5ffdcc1c2\/multiplayer-text-adventure-graph.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/c89fe792-35c5-44e2-ba46-8fb5ffdcc1c2\/multiplayer-text-adventure-graph.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/c89fe792-35c5-44e2-ba46-8fb5ffdcc1c2\/multiplayer-text-adventure-graph.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-graph.png?w=900\" sizes=\"100vw\" alt=\"A weighted graph example\"><\/a><figcaption>\n\t\t\tA weighted graph example (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/c89fe792-35c5-44e2-ba46-8fb5ffdcc1c2\/multiplayer-text-adventure-graph.png\">Large preview<\/a>)<br \/>\n\t\t<\/figcaption><\/figure>\n<p>That\u2019s a weighted graph \u2014 that\u2019s it. And I\u2019m sure you\u2019ve already figured it out, but for the sake of completeness, let me show you how you would go about it once our engine is ready.<\/p>\n<p>Once you start setting up the adventure, you\u2019ll create your map (like you see on the left of the image below). And then you\u2019ll translate that into a weighted graph, as you can see on the right of the image. Our engine will be able to pick it up and let you walk through it in the right order.<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/12016d35-7186-43f6-8093-7272aa716122\/multiplayer-text-adventure-graph-rooms.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-graph-rooms.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/12016d35-7186-43f6-8093-7272aa716122\/multiplayer-text-adventure-graph-rooms.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/12016d35-7186-43f6-8093-7272aa716122\/multiplayer-text-adventure-graph-rooms.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/12016d35-7186-43f6-8093-7272aa716122\/multiplayer-text-adventure-graph-rooms.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/12016d35-7186-43f6-8093-7272aa716122\/multiplayer-text-adventure-graph-rooms.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-graph-rooms.png?w=900\" sizes=\"100vw\" alt=\"Example graph for a given dungeon\"><\/a><figcaption>\n\t\t\tExample graph for a given dungeon (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/12016d35-7186-43f6-8093-7272aa716122\/multiplayer-text-adventure-graph-rooms.png\">Large preview<\/a>)<br \/>\n\t\t<\/figcaption><\/figure>\n<p>With the weighted graph above, we can make sure players can\u2019t go from the entrance all the way to the left wing. They would have to go through the nodes in between those two, and doing so will consume time, which we can measure using the weight from the connections.<\/p>\n<p>Now, onto the \u201cfun\u201d part. Let\u2019s see how the graph would look like in JSON format. Bear with me here; this JSON will contain a lot of information, but I\u2019ll go through as much of it as I can:<\/p>\n<pre><code>{\n    \"graph\": [\n            { \"id\": \"entrance\", \"name\": \"Entrance\", \"north\": { \"node\": \"1stroom\", \"distance\": 1 } },\n     { \"id\": \"1st room\", \"name\": \"1st Room\", \"south\": {\"node\": \"entrance\", \"distance\": 1} , \"north\": { \"node\": \"bigroom\", \"distance\": 1} } ,\n     { \"id\": \"bigroom\",\n       \"name\": \"Big room\",\n       \"south\": { \"node\": \"1stroom\", \"distance\": 1},\n       \"north\": { \"node\": \"bossroom\", \"distance\": 2},\n       \"east\":  { \"node\": \"rightwing\", \"distance\": 3} ,\n       \"west\":  { \"node\": \"leftwing\", \"distance\": 3}\n     },\n     { \"id\": \"bossroom\", \"name\": \"Boss room\", \"south\": {\"node\": \"bigroom\", \"distance\": 2} }\n     { \"id\": \"leftwing\", \"name\": \"Left Wing\", \"east\": {\"node\": \"bigroom\", \"distance\": 3} }\n     { \"id\": \"rightwing\", \"name\": \"Right Wing\", \"west\": { \"node\": \"bigroom\", \"distance\": 3 } }\n    ],\n    \"game\": {\n     \"win-condition\": {\n       \"source\": \"finalboss\",\n       \"condition\": {\n         \"type\": \"comparison\",\n         \"left\": \"hp\",\n         \"right\": \"0\",\n         \"symbol\": \"\n                 { \"action\": \"throw\", \"target\": \"chair\"} \/\/throw <item>\n               ],\n               \"destination\": \"inventory\",\n               \"damage\": 2\n             }\n           ]\n         }\n       ]\n     },\n     \"bigroom\": {\n       \"description\": {\n         \"default\": \"You've reached the big room. On every wall are torches lighting every corner. The walls are painted white, and the ceiling is tall and filled with painted white stars on a black background. There is a gateway on either side and a big, wooden double door in front of you.\"\n       },\n       \"exits\": {\n         \"north\": { \"id\": \"bossdoor\",  \"name\": \"Big double door\", \"status\": \"locked\", \"details\": \"A aig, wooden double door. It seems like something big usually comes through here.\"}\n       },\n       \"items\": []\n     },\n     \"leftwing\": {\n       \"description\": {\n         \"default\": \"Another dark room. It doesn't look like it's that big, but you can't really tell what's inside. You do, however, smell rotten meat somewhere inside.\",\n         \"conditionals\": {\n           \"has light\":  \"You appear to have found the kitchen. There are tables full of meat everywhere, and a big knife sticking out of what appears to be the head of a cow.\"\n         }\n       },\n       \"items\": [\n         { \"id\": \"bigknife\", \"name\": \"Big knife\", \"destination\": \"inventory\", \"damage\": 10}\n       ]\n     },\n     \"rightwing\": {\n       \"description\": {\n         \"default\": \"This appear to be some sort of office. There is a wooden desk in the middle, torches lighting every wall, and a single key resting on top of the desk.\"\n       },\n       \"items\": [\n         {     \"id\": \"key\",\n           \"name\": \"Golden key\",\n           \"details\": \"A small golden key. What use could you have for it?\",\n           \"destination\": \"inventory\",\n           \"triggers\": [{\n             \"action\": \"use\", \/\/use <item> on north exit (contextual)\n             \"target\": {\n               \"room\": \"bigroom\",\n               \"exit\": \"north\"\n             },\n             \"effect\": {\n               \"statusUpdate\": \"unlocked\",\n               \"target\": {\n                 \"room\": \"bigroom\",\n                 \"exit\": \"north\"\n               }\n             }\n           }\n         ]\n         }\n       ]\n     },\n     \"bossroom\": {\n       \"description\": {\n         \"default\": \"You appear to have reached the end of the dungeon. There are no exits other than the one you just came in through. The only other thing that bothers you is the hulking giant looking like it's going to kill you, standing about 10 feet from you.\"\n       },\n       \"npcs\": [\n         {\n           \"id\": \"finalboss\",\n           \"name\": \"Hulking Ogre\",\n           \"details\": \"A huge, green, muscular giant with a single eye in the middle of his forehead. It doesn't just look bad, it also smells like hell.\",\n           \"stats\":  {\n             \"hp\": 10,\n             \"damage\": 3\n           }\n         }\n       ]\n     }\n    }\n}\n\n<\/item><\/item><\/code><\/pre>\n<p>I know it looks like a lot, but if you boil it down to a simple description of the game, you have a dungeon comprising six rooms, each one interconnected with others, as shown in the diagram above.<\/p>\n<p>Your task is to move through it and explore it. You\u2019ll find there are two different places where you can find a weapon (either in the kitchen or in the dark room, by breaking the chair). You will also be confronted with a locked door; so, once you find the key (located inside the office-like room), you\u2019ll be able to open it and fight the boss with whatever weapon you\u2019ve collected.<\/p>\n<p>You will either win by killing it or lose by getting killed by it.<\/p>\n<p>Let\u2019s now get into a more detailed overview of the entire JSON structure and its three sections.<\/p>\n<h5>Graph<\/h5>\n<p>This one will contain the relationship between the nodes. Basically, this section directly translates into the graph we looked at before.<\/p>\n<p>The structure for this section is pretty straightforward. It\u2019s a list of nodes, where every node comprises the following attributes:<\/p>\n<ul>\n<li>an ID that uniquely identifies the node among all others in the game;<\/li>\n<li>a name, which is basically a human-readable version of the ID;<\/li>\n<li>a set of links to the other nodes. This is evidenced by the existence of four possible keys: north\u201d, south, east, and west. We could eventually add further directions by adding combinations of these four. Every link contains the ID of the related node and the distance (or weight)     of that relation.<\/li>\n<\/ul>\n<h5>Game<\/h5>\n<p>This section will contain the general settings and conditions. In particular, in the example above, this section contains the win and lose conditions. In other words, with those two conditions, we\u2019ll let the engine know when the game can end.<\/p>\n<p>To keep things simple, I\u2019ve added just two conditions:<\/p>\n<ul>\n<li>you either win by killing the boss,<\/li>\n<li>or lose by getting killed.<\/li>\n<\/ul>\n<h5>Rooms<\/h5>\n<p>Here is where most of the 163 lines come from, and it is the most complex of the sections. This is where we\u2019ll describe all of the rooms in our adventure and everything inside them.<\/p>\n<p>There will be a key for every room, using the ID we defined before. And every room will have a description, a list of items, a list of exits (or doors) and a list of non-playable characters (NPCs). Out of those properties, the only one that should be mandatory is the description, because that one is required for the engine to let you know what you\u2019re seeing. The rest of them will only be there if there is something to show.<\/p>\n<p>Let\u2019s look into what these properties can do for our game.<\/p>\n<p><strong>The Description<\/strong><\/p>\n<p>This item is not as simple as one might think, because your view of a room can change depending on different circumstances. If, for example, you look at the description of the first room, you\u2019ll notice that, by default, you can\u2019t see anything, unless of course, you have a lit torch with you.<\/p>\n<p>So, picking up items and using them might trigger global conditions that will affect other parts of the game.<\/p>\n<p><strong>The Items<\/strong><\/p>\n<p>These represent all the things\u201d you can find inside a room. Every item shares the same ID and name that the nodes in the graph section had.<\/p>\n<p>They will also have a \u201cdestination\u201d property, which indicates where that item should be stored, once picked up. This is relevant because you will be able to have only one item in your hands, whereas you\u2019ll be able to have as many as you\u2019d like in your inventory.<\/p>\n<p>Finally, some of these items might trigger other actions or status updates, depending on what the player decides to do with them. One example of this are the lit torches from the entrance. If you grab one of them, you\u2019ll trigger a status update in the game, which in turn will make the game show you a different description of the next room.<\/p>\n<p>Items can also have \u201csubitems\u201d, which come into play once the original item gets destroyed (through the \u201cbreak\u201d action, for example). An item can be broken down into several ones, and that is defined in the \u201csubitems\u201d element.<\/p>\n<p>Essentially, this element is just an array of new items, one that also contains the set of actions that can trigger their creation. This basically opens up the possibility to create different subitems based on the actions you perform on the original item.<\/p>\n<p>Finally, some items will have a \u201cdamage\u201d property. So, if you use an item to hit an NPC, that value will be used to subtract life from them.<\/p>\n<p><strong>The Exits<\/strong><\/p>\n<p>This is simply a set of properties indicating the direction of the exit and the properties of it (a description, in case you want to inspect it, its name and, in some cases, its status).<\/p>\n<p>Exits are a separate entity from items because the engine will need to understand if you can actually traverse them based on their status. Exits that are locked will not let you go through them unless you work out how to change their status to unlocked.<\/p>\n<p><strong>The NPCs<\/strong><\/p>\n<p>Finally, NPCs will be part of another list. They are basically items with statistics that the engine will use to understand how each one should behave. The ones we\u2019ve defined in our example are \u201chp\u201d, which stands for health points, and \u201cdamage\u201d, which, just like the weapons, is the number that each hit will subtract from the player\u2019s health.<\/p>\n<p>That is it for the dungeon I created. It is a lot, yes, and in the future I might consider creating a level editor of sorts, to simplify the creation of the JSON files. But for now, that won\u2019t be necessary.<\/p>\n<p>In case you haven\u2019t realized it yet, the main benefit of having our game defined in a file like this is that we\u2019ll be able to switch JSON files like you did cartridges back in the Super Nintendo era. Just load up a new file and start a new adventure. Easy!<\/p>\n<h3>Closing Thoughts<\/h3>\n<p>Thanks for reading thus far. I hope you\u2019ve enjoyed the design process I go through to bring an idea to life. Remember, though, that I\u2019m making this up as I go, so we might realize later that something we defined today isn\u2019t going to work, in which case we\u2019ll have to backtrack and fix it.<\/p>\n<p>I\u2019m sure there are a ton of ways to improve the ideas presented here and to make one hell of an engine. But that would require a lot more words than I can put into an article without making it boring for everyone, so we\u2019ll leave it at that for now.<\/p>\n<div>\n  <img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/logo-red-14.png?w=900\" alt=\"Smashing Editorial\"><span>(rb, ra, al, il)<\/span>\n<\/div>\n<\/article>\n<p class=\"wpematico_credit\"><small>Powered by <a href=\"http:\/\/www.wpematico.com\" target=\"_blank\">WPeMatico<\/a><\/small><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Writing A Multiplayer Text Adventure Engine In Node.js Writing A Multiplayer Text Adventure Engine In Node.js Fernando Doglio 2018-12-20T14:05:34+01:002018-12-20T14:16:59+00:00 Text adventures were one of the first forms of digital role-playing games out there, back when games had no graphics and all you had was your own imagination and the description you read on the black&#8230;<a class=\"moretag\" href=\"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/\"> Read the full article&#8230;<\/a><\/p>\n","protected":false},"author":4,"featured_media":57512,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[371],"tags":[],"class_list":["post-57511","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-user-experience"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"Writing A Multiplayer Text Adventure Engine In Node.jsWriting A Multiplayer Text Adventure Engine In Node.js Fernando Doglio 2018-12-20T14:05:34+01:002018-12-20T14:16:59+00:00Text adventures were one of the first forms of digital role-playing games out there, back when games had no graphics and all you had was your own imagination and the description you read on the black screen of\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Guest Contribution\"\/>\n\t<meta name=\"keywords\" content=\"user experience\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Design for Immersive Technologies | User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Writing A Multiplayer Text Adventure Engine In Node.js | Design for Immersive Technologies\" \/>\n\t\t<meta property=\"og:description\" content=\"Writing A Multiplayer Text Adventure Engine In Node.jsWriting A Multiplayer Text Adventure Engine In Node.js Fernando Doglio 2018-12-20T14:05:34+01:002018-12-20T14:16:59+00:00Text adventures were one of the first forms of digital role-playing games out there, back when games had no graphics and all you had was your own imagination and the description you read on the black screen of\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2018-12-21T11:19:12+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2018-12-21T11:19:12+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Writing A Multiplayer Text Adventure Engine In Node.js | Design for Immersive Technologies\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Writing A Multiplayer Text Adventure Engine In Node.jsWriting A Multiplayer Text Adventure Engine In Node.js Fernando Doglio 2018-12-20T14:05:34+01:002018-12-20T14:16:59+00:00Text adventures were one of the first forms of digital role-playing games out there, back when games had no graphics and all you had was your own imagination and the description you read on the black screen of\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/12\\\/writing-a-multiplayer-text-adventure-engine-in-node-js\\\/#article\",\"name\":\"Writing A Multiplayer Text Adventure Engine In Node.js | Design for Immersive Technologies\",\"headline\":\"Writing A Multiplayer Text Adventure Engine In Node.js\",\"author\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/www.taterboy.com\\\/blog\\\/wp-content\\\/uploads\\\/2018\\\/12\\\/multiplayer-text-adventure-you-are-standing.jpg?fit=400%2C300&ssl=1\",\"width\":400,\"height\":300},\"datePublished\":\"2018-12-21T04:19:12-07:00\",\"dateModified\":\"2018-12-21T04:19:12-07:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/12\\\/writing-a-multiplayer-text-adventure-engine-in-node-js\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/12\\\/writing-a-multiplayer-text-adventure-engine-in-node-js\\\/#webpage\"},\"articleSection\":\"User Experience\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/12\\\/writing-a-multiplayer-text-adventure-engine-in-node-js\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.taterboy.com\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"name\":\"User Experience\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"position\":2,\"name\":\"User Experience\",\"item\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/12\\\/writing-a-multiplayer-text-adventure-engine-in-node-js\\\/#listItem\",\"name\":\"Writing A Multiplayer Text Adventure Engine In Node.js\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/12\\\/writing-a-multiplayer-text-adventure-engine-in-node-js\\\/#listItem\",\"position\":3,\"name\":\"Writing A Multiplayer Text Adventure Engine In Node.js\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"name\":\"User Experience\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\",\"name\":\"Design for Immersive Technologies\",\"description\":\"User Experience for Games, Virtual Reality (VR) and Augmented\\\/Mixed Reality (AR\\\/MR)\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/\",\"name\":\"Guest Contribution\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/12\\\/writing-a-multiplayer-text-adventure-engine-in-node-js\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/d2aad73eb0f48c67b141e9fc978a0e498969791ed623346e361d02d19775b0bb?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Guest Contribution\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/12\\\/writing-a-multiplayer-text-adventure-engine-in-node-js\\\/#webpage\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/12\\\/writing-a-multiplayer-text-adventure-engine-in-node-js\\\/\",\"name\":\"Writing A Multiplayer Text Adventure Engine In Node.js | Design for Immersive Technologies\",\"description\":\"Writing A Multiplayer Text Adventure Engine In Node.jsWriting A Multiplayer Text Adventure Engine In Node.js Fernando Doglio 2018-12-20T14:05:34+01:002018-12-20T14:16:59+00:00Text adventures were one of the first forms of digital role-playing games out there, back when games had no graphics and all you had was your own imagination and the description you read on the black screen of\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/12\\\/writing-a-multiplayer-text-adventure-engine-in-node-js\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/www.taterboy.com\\\/blog\\\/wp-content\\\/uploads\\\/2018\\\/12\\\/multiplayer-text-adventure-you-are-standing.jpg?fit=400%2C300&ssl=1\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/12\\\/writing-a-multiplayer-text-adventure-engine-in-node-js\\\/#mainImage\",\"width\":400,\"height\":300},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/12\\\/writing-a-multiplayer-text-adventure-engine-in-node-js\\\/#mainImage\"},\"datePublished\":\"2018-12-21T04:19:12-07:00\",\"dateModified\":\"2018-12-21T04:19:12-07:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/\",\"name\":\"Design for Immersive Technologies\",\"description\":\"User Experience for Games, Virtual Reality (VR) and Augmented\\\/Mixed Reality (AR\\\/MR)\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Writing A Multiplayer Text Adventure Engine In Node.js | Design for Immersive Technologies","description":"Writing A Multiplayer Text Adventure Engine In Node.jsWriting A Multiplayer Text Adventure Engine In Node.js Fernando Doglio 2018-12-20T14:05:34+01:002018-12-20T14:16:59+00:00Text adventures were one of the first forms of digital role-playing games out there, back when games had no graphics and all you had was your own imagination and the description you read on the black screen of","canonical_url":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/","robots":"max-image-preview:large","keywords":"user experience","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/#article","name":"Writing A Multiplayer Text Adventure Engine In Node.js | Design for Immersive Technologies","headline":"Writing A Multiplayer Text Adventure Engine In Node.js","author":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"publisher":{"@id":"https:\/\/www.taterboy.com\/blog\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-you-are-standing.jpg?fit=400%2C300&ssl=1","width":400,"height":300},"datePublished":"2018-12-21T04:19:12-07:00","dateModified":"2018-12-21T04:19:12-07:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/#webpage"},"isPartOf":{"@id":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/#webpage"},"articleSection":"User Experience"},{"@type":"BreadcrumbList","@id":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog#listItem","position":1,"name":"Home","item":"https:\/\/www.taterboy.com\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","name":"User Experience"}},{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","position":2,"name":"User Experience","item":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/#listItem","name":"Writing A Multiplayer Text Adventure Engine In Node.js"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/#listItem","position":3,"name":"Writing A Multiplayer Text Adventure Engine In Node.js","previousItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","name":"User Experience"}}]},{"@type":"Organization","@id":"https:\/\/www.taterboy.com\/blog\/#organization","name":"Design for Immersive Technologies","description":"User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","url":"https:\/\/www.taterboy.com\/blog\/"},{"@type":"Person","@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author","url":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/","name":"Guest Contribution","image":{"@type":"ImageObject","@id":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/d2aad73eb0f48c67b141e9fc978a0e498969791ed623346e361d02d19775b0bb?s=96&d=mm&r=g","width":96,"height":96,"caption":"Guest Contribution"}},{"@type":"WebPage","@id":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/#webpage","url":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/","name":"Writing A Multiplayer Text Adventure Engine In Node.js | Design for Immersive Technologies","description":"Writing A Multiplayer Text Adventure Engine In Node.jsWriting A Multiplayer Text Adventure Engine In Node.js Fernando Doglio 2018-12-20T14:05:34+01:002018-12-20T14:16:59+00:00Text adventures were one of the first forms of digital role-playing games out there, back when games had no graphics and all you had was your own imagination and the description you read on the black screen of","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/www.taterboy.com\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/#breadcrumblist"},"author":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"creator":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-you-are-standing.jpg?fit=400%2C300&ssl=1","@id":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/#mainImage","width":400,"height":300},"primaryImageOfPage":{"@id":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/#mainImage"},"datePublished":"2018-12-21T04:19:12-07:00","dateModified":"2018-12-21T04:19:12-07:00"},{"@type":"WebSite","@id":"https:\/\/www.taterboy.com\/blog\/#website","url":"https:\/\/www.taterboy.com\/blog\/","name":"Design for Immersive Technologies","description":"User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","inLanguage":"en-US","publisher":{"@id":"https:\/\/www.taterboy.com\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Design for Immersive Technologies | User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","og:type":"article","og:title":"Writing A Multiplayer Text Adventure Engine In Node.js | Design for Immersive Technologies","og:description":"Writing A Multiplayer Text Adventure Engine In Node.jsWriting A Multiplayer Text Adventure Engine In Node.js Fernando Doglio 2018-12-20T14:05:34+01:002018-12-20T14:16:59+00:00Text adventures were one of the first forms of digital role-playing games out there, back when games had no graphics and all you had was your own imagination and the description you read on the black screen of","og:url":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/","article:published_time":"2018-12-21T11:19:12+00:00","article:modified_time":"2018-12-21T11:19:12+00:00","twitter:card":"summary","twitter:title":"Writing A Multiplayer Text Adventure Engine In Node.js | Design for Immersive Technologies","twitter:description":"Writing A Multiplayer Text Adventure Engine In Node.jsWriting A Multiplayer Text Adventure Engine In Node.js Fernando Doglio 2018-12-20T14:05:34+01:002018-12-20T14:16:59+00:00Text adventures were one of the first forms of digital role-playing games out there, back when games had no graphics and all you had was your own imagination and the description you read on the black screen of"},"aioseo_meta_data":{"post_id":"57511","title":null,"description":null,"keywords":null,"keyphrases":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_custom_url":null,"og_image_custom_fields":null,"og_custom_image_width":null,"og_custom_image_height":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"location":null,"local_seo":null,"created":"2021-02-07 23:46:23","updated":"2026-08-22 11:01:05","focus_keyword":null,"additional_keywords":null,"truseo_locale":null,"primary_term":null,"og_image_url":null,"og_image_width":null,"og_image_height":null,"twitter_image_url":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"limit_modified_date":false,"ai":null,"breadcrumb_settings":null,"seo_analyzer_scan_date":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.taterboy.com\/blog\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/\" title=\"User Experience\">User Experience<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tWriting A Multiplayer Text Adventure Engine In Node.js\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/www.taterboy.com\/blog"},{"label":"User Experience","link":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/"},{"label":"Writing A Multiplayer Text Adventure Engine In Node.js","link":"https:\/\/www.taterboy.com\/blog\/2018\/12\/writing-a-multiplayer-text-adventure-engine-in-node-js\/"}],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8wzr5-eXB","jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/12\/multiplayer-text-adventure-you-are-standing.jpg?fit=400%2C300&ssl=1","_links":{"self":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts\/57511","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/comments?post=57511"}],"version-history":[{"count":0,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts\/57511\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/media\/57512"}],"wp:attachment":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/media?parent=57511"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/categories?post=57511"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/tags?post=57511"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}