This guide explains how to use the Simple Multiplayer (SMP) for Javascript. The multiplayer system works like a relay in that it helps clients send messages to each other. Players are then able to interact with each other as messages are transmitted to the server and to other clients. The SMP service is provided at no cost. The only requirement is that the game also use at least 2 other Y8 Account user features. For example, a game can use multierplayer, login, high scores and/or achievements.

Important Limitations

  • There is no server side code available to developers. Players must be able to synchronize all shared data with each other. Turned based, racing, billards will work well with this but an MMO won’t.
  • There is no global storage system, individual player data can only be stored using online saves.
  • Room size is limited to 100 people per a lobby and 10 people per a game room. Game rooms can provide realtime data while a lobby is for connecting players and limited messages like a chat room.

Getting Started

Step 1 is to add a callback function after the Y8 Account JS SDK and Game API are loaded. All multiplayer data will go through the callback function. Call ID.Multiplayer.open with the appid, appsession, and your callback. The forth argument is optional. It’s used to pass your scope to the callback’s second argument.

var appid = 'YOUR APPID HERE';
var self = this;
window.idAsyncInit = function() {
  ID.Event.subscribe('id.init', function(){
    ID.GameAPI.init(appid, null, function(data, response){
      ID.Multiplayer.open(appid, data.appsession, mpCallback, self);
    });
  });

  ID.init({
    appId : appid
  });
};

(function(d, s, id){
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) {return;}
  js = d.createElement(s); js.id = id;
  js.src =  document.location.protocol == 'https:' ? "https://cdn.y8.com/api/sdk.js" : "http://cdn.y8.com/api/sdk.js";
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'id-jssdk'));

mpCallback(msg, scope){
  // multiplayer code goes here
}

Step 2 is to wait for the first message, it will have the start action. In the example, we join a room with a randomly generated name. If one already exists we join it. If no room is available, we create one and then join. Once in the room, we broadcast a hello message and wait for others to broadcast the same.

Note, the started varible is used because a room could be on a different server. If this is the case, SMP will automagically connect to the right server or node. Without this check, the code would run twice as the start action happens again on server switch.

var started = false; // keep track if we already started or are reconnecting to a new server
function mpCallback(msg){ // the callback function
  if (msg.action == 'start' && !started) { // do this on the first start
    started = true;
    ID.Multiplayer.roomList('lobby', null, null, 'lookup');
  }

  if (msg.section == 'rooms' && msg.action == 'list' && msg.pt == 'lookup') {
    if(msg.rooms.length > 0){ // if the room we want exists, join it
      ID.Multiplayer.roomJoin(msg.rooms[0].roomid, msg.rooms[0].webnode, 'inroom');
    }else{ // if not, create it
      ID.Multiplayer.roomCreate('lobby', null, null, 'nowjoin');
    }
  }

  if (msg.section == 'rooms' && msg.action == 'create' && msg.pt == 'nowjoin') {
    // if we created a room, we will want to also join it
    ID.Multiplayer.roomJoin(msg.room.roomid, msg.room.webnode, 'inroom');
  }

  if (msg.section == 'rooms' && msg.action == 'join' && msg.pt == 'inroom') {
    // finally let's announce that we have arrived.
    ID.Multiplayer.broadcast({say: 'hello room'});
  }

  if (msg.action == 'broadcast') {
    // listen for other clients
    console.log(msg.say);
  }

  if (msg.section == 'rooms' && msg.action == 'playerjoined') {
    console.log('playerjoined', msg);
  }

  if (msg.section == 'rooms' && msg.action == 'playerleft') {
    console.log('playerleft', msg);
  }

  console.log('-----');
}

Step 3 is to continue building your message chain. Use the pass-through argument to separate similar calls and to keep track of what step should follow next with your own game logic. Read the reference below to understand the functions available. Note, the Y8 Account JS SDK requires to be loaded from a domain! Once you have finished step 2 and uploaded your html file, open it in 2 browsers and check for the ‘Hello room’ message in the first loaded client.

Reference

Room List

ID.Multiplayer.roomList(type, roomid, custom, passthrough)

Will list game rooms based on the arguments provided.

  • type: (optional) A string equalling ‘lobby’ or ‘game’
  • roomid: (optional) A string of a specific room
  • custom: (optional) A object to sort room list. See room create.
  • passthrough: (optional) A string less than 50 characters to be passed back to the callback

Room Join

ID.Multiplayer.roomJoin(roomid, node, passthrough)

Will list game rooms based on the arguments provided.

  • roomid: A string of a specific room
  • node: A node returned from room list or room create that identifies which server the room is in
  • passthrough: (optional) A string less than 50 characters to be passed back to the callback

Room Create

ID.Multiplayer.roomCreate(type, roomid, custom, passthrough, options)

Create a new room. Note, this does not automatically join the room. Also, if setting the roomid manually, the roomid could be in use by another application. This is a known bug.

  • type: (optional)(default: ‘lobby’) A string equalling ‘lobby’ or ‘game’
  • roomid: (optional)(default: ‘random string’) A string to identitfy the room by.
  • custom: (optional) A object to store custom data or to provide extra sorting options
  • passthrough: (optional) A string less than 50 characters to be passed back to the callback
  • options: (optional) An object containing one or more key value pairs
    • isOpen: Boolean value of false will prevent other players from joining
    • isVisible: Boolean value of false will prevent a room from being list
    • maxPlayers: An integer of max players allowed in a room

Room Update

ID.Multiplayer.roomUpdate(custom, passthrough, options)

Update the custom varible and/or options of a room. This should be used infrequently and not for realtime updates.

  • custom: (optional) A object to store custom data or to provide extra sorting options
  • passthrough: (optional) A string less than 50 characters to be passed back to the callback
  • options: (optional) An object containing one or more key value pairs
    • isOpen: Boolean value of false will prevent other players from joining
    • isVisible: Boolean value of false will prevent a room from being list

Room Leave

ID.Multiplayer.roomLeave(passthrough)

The player will exit the room they are in

  • passthrough: (optional) A string less than 50 characters to be passed back to the callback

Room Broadcast

ID.Multiplayer.broadcast(message)

Send a message to everyone in a room except to the person sending it.

  • message: An object containing data to be shared with a room

Room Broadcast All

ID.Multiplayer.broadcastAll(message)

Send a message to everyone in a room and return the message to the sender via the callback.

  • message: An object containing data to be shared with a room

Room Send To

ID.Multiplayer.sendTo(userid, message)

Send a message to single person is the same room as the sender.

  • playerid: A string to identify a player by. It can be found in room list
  • message: An object containing data to be shared with another player

User Meta Data

ID.Multiplayer.userMeta(meta, passthrough)

User data visible to other players in a room. Remember to set this on each start as joining a room could cause the player to switch servers.

  • meta: Data to be stored along with the player
  • passthrough: (optional) A string less than 50 characters to be passed back to the callback

Ping

ID.Multiplayer.ping(passthrough)

Will return a action of ‘pong’ to be used with a timer to calculate latency.

  • passthrough: (optional) A string less than 50 characters to be passed back to the callback

debugLevel

ID.Multiplayer.debugLevel = Integer

Will limit what is output to the console. Useful when you need to hide some output for performance or production use.

  • Integer: Using 2 will output everything. 1 output everything except broadcast messages. 0 will be silent except for errors.

Disconnects

Players will be disconnected after 15 minutes of inactivity. Players could also be disconnected because of networking problems or server maintenance. When a disconnect is detected, a callback with section connection and action disconnect will be sent to the game. If the returned errorcode property is 4000 it was a timeout. Otherwise, if the errorcode is 1, the connection was lost for an unknown reason. Games need to trigger a message for the player and return them to a main menu or instruct them to reload the game.

Troubleshooting

Multiplayer logic will become a mess quickly. Keep the code clean and concise to avoid trouble later. When you struggle to understand what is going on, carefully read the output to track what is happening during the logic chain. Use the pass-through arguments to force your chain to follow the path you need.

As noted earlier, there is more than one server or node. When a room is created, it will be put on the least congested server. Be prepared to see output showing this server switching. Also, remember to expect this behavior to avoid getting stuck in a loop.

About room names, note that name conflicts can happen between different games. If you want to name a room manually, prefix the name with a unique value to avoid possible conflicts. Otherwise, leave the room name as null and an id will be generated automatically.

Final Notes for Success

Communication

Getting a player into a game room and playing involves a number of steps. Remember to tell the player what is happening along the way. Tell them when they are connecting, waiting for another player, and any other part of your games multiplayer logic.

Player Onboarding

Getting players connected will be a big challenge. To help, we have made the Matchmaking system. You should use it for auto joining games because it has features to prevent 2 players from being stuck in seperate rooms waiting for antoher player.

Stay Simple

Your multiplayer code should be very simple with very little game logic mixed in. Use functions to other classes to keep your In and Out code separated. Also, keep the data you send using SMP small. For example {rotation: 5.123345345} could be shortened to {r: 5}, this helps keep the data flowing quickly.