Implementing a "Kick Out" Feature in a Multiplayer Game Lobby

The main goal of this module is to be able to kick players from the private lobby. Only the host should be able to kick other players from the lobby. Let's look at how it can be accomplished in a simple multiplayer game.

This is a guide for making a simple character kick system in the multiplayer games inside the lobby which you can see in a lot of multiplayer games today. Let us explore how we can implement a simple character kick system for multiplayer games.

Overview

The main goal of this module is to be able to kick players from the private lobby. Only the host should be able to kick other players from the lobby. Let's look at how it can be accomplished in a simple multiplayer game.

Before diving into the details, we assume you have already set up a new project with some sort of socket connection and already know the basics of the event system used by Socket.io. If not, it's a good idea to learn the basics of Socket.io in Unity and set up a dummy server before moving forward. And obviously, you have made some kind of lobby for players to join.

The Architecture

We need to make a simple database schema that holds room information that contains players as well as another DB that holds user information. I have a User schema and Room Schema as follows:

Database Schema

const mongoose = require("mongoose");
const Schema = mongoose.Schema;
let UserSchema = new Schema({
    userId: {
        type: String,
        unique: true
    },
    socketId: {
        type: String,
        unique: true
    },
    roomName: {
        type: String,
        default: null
    },
    isPlaying: {
        type: String,
        default: false
    },
    ...
});
module.exports = mongoose.model("Users", UserSchema);
Creating User Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
let roomSchema = new Schema({
    roomName: {
        type: String,
        default: () => {
            return randomRoomName();
        },
    },
    players: {
        type: [String],
        default: []
    },
    ...
});
Creating Room Schema

Before kicking players we need to at least have them in a room. In my case, whoever joins the room is pushed in the stack "players" in Room schema.
The player creating the room will always have his name first in the list of players and others who join follow thereafter. Meaning from this fact alone we can determine who is the host of this room. The host will always be the first index player on the list. But what if the host leaves the room and other "not host" players are remaining in the room? Well whenever the host leaves the room, his info is removed from the list meaning the player at index after him will have a new first index in the list. Meaning, the host will always be at index 0.

Events

Next, we need to write a controller that listens for events emitted by the client when the host tries to kick a player. Let's call the event "KickUser". The event will be fired by a game client, such as a Socket.io client in Unity, whenever a player kicks a player. Have a look at this example.

const redis = require("socket.io-redis");
const User = require("../Model/User");
const Room = require("../Model/Room");
const config = require("../Config/Config")

// socket manager
const SocketManager = function(http) {
    io = require("socket.io").listen(http, config.socketConfig);
    io.adapter(redis(config.redisConfig));
    SocketListeners(io);
};


// socket listeners
function SocketListeners(io) {
    // kickId is the name of the joined player that is present in players list
    socket.on("Kick", async (kickId) => {
        console.log("Requested Kick Id is : " + kickId);
        await User.findOne({
            socketId: socket.id
        }).then(async (user) => {
            if (user) {
                await Room.findOne({
                    roomName: user.roomName
                }).then(async (room) => {
                    // check if host is calling for validation
                    if (room.players[0] == user.userId) {
                        io.in(user.roomName).emit("Kicked", kickId);
                    }
                })
            } else
                io.in(user.roomName).emit("err", "Kick Failed");
        })
    });


    // on leaving the room
    socket.on("leave", async function() {
    	// clear user roomName info form "User"
        // remove player informatioin from the players list in room
        // after clearing everything save updated room info
        // finally roomUpdated info to all the clients in given room
        
        io.in(room.roomName).emit("room", JSON.stringify(room));
    });

}
Server-side socket

Let's break it down from both server and client perspectives:

Server-side ideation:

  1. The server receives requests from clients to remove a certain user from the room
  2. The server then first finds the user and room of the user that is requesting a kick.
  3. The server validates if the requestor is the host and if it is sending the "Kicked" event to all the clients in the room.
  4. The client then receives kicked response and checks the kicked player if true, kicked client calls the event "leave" in the server
  5. The server now clears kicked player information and send a roomUpdated event to all clients

Client-side ideation:

  1. host player requests to kick user
  2. the server validates the information and sends a kicked event to all the clients in the room
  3. client check if the kicked user is him, and if so is true he will emit "leave" in server
  4. kicked user will get out of the lobby
  5. other users that are still in the room will get room updated event and will update the player information in UI accordingly.

By following these simple steps you are now able to make a player kick system. If you need any help, feel free to comment down. Good day!