Game Box MMO Game

Updated at 1685687945000
For MMO game, Game Box provides some components for logic:
  1. MMOVirtualWorld: Manages all MMO rooms and schedule to update state the rooms and room's players.
  2. SyncPositionRoomUpdatedHandler: To sync room's players to clients.

1. MMOVirtualWorld

Actually, a MMOVirtualWorld manages a list of MMORoomGroup, every room will be evenly distributed into every MMORoomGroup like this:

    public void addRoom(MMORoom room) {
        int roomGroupIndex = (int) (room.getId() % roomGroupCount);
        MMORoomGroup roomGroup = roomGroups[roomGroupIndex];
        roomGroup.addRoom(room);
    }

We need add a room like that to be able to use multi-threading but still ensure every room state's update will be run on only one thread to avoid concurrent issues. To create a MMOVirtualWorld you can do like this:

    @EzySingleton
    public MMOVirtualWorld mmoVirtualWorld() {
        logger.info("Initialize MMO Virtual World");
        return MMOVirtualWorld.builder().build();
    }

For more details, please take a look: MMOVirtualWorldConfig

2. MMORoomGroup

A MMORoomGroup will do 2 main tasks:

  • Manages MMORooms
  • Schedule update MMORooms and MMOPlayers state

The scheduler will work like this:

    private void loop() {
        this.active = true;
        List roomBuffer = new ArrayList<>();
        while (active) {
            try {
                long start = System.currentTimeMillis();
                this.updateRooms(roomBuffer);
                long end = System.currentTimeMillis();
                long timeElapsed = end - start;
                if (timeElapsed < timeTickMillis) {
                    //noinspection BusyWait
                    Thread.sleep(timeTickMillis - timeElapsed);
                }
            } catch (Exception e) {
                logger.error("Room group loop error: ", e);
            }
        }
    }

You can see, the sleep time will depend on rooms update elapsed time. So, if we have more rooms, the CPU usage will be higher.

3. MMORoom

A MMORoom manages a list of MMOPlayers. When the update method is called, it will calculate the distance between players to update near by players list of every players in the room like this:
    public void update() {
        List playerList = playerManager.getPlayerList();
        playerList.forEach(player -> {
            player.clearNearByPlayers();
            playerList.forEach(other -> {
                double distance = player.getPosition().distance(other.getPosition());
                if (distance <= this.distanceOfInterest) {
                    player.addNearbyPlayer(other);
                } else {
                    player.removeNearByPlayer(other);
                }
            });
        });
        notifyUpdatedHandlers();
    }

You can see, in the end of the method, the room will notify to it's all listeners, and we can add the SyncPositionRoomUpdatedHandler to the listener list.

4. SyncPositionRoomUpdatedHandler

After the room update, we will have a list of updated players. In fact, we must sync the position to clients to allow the clients update and display by the state. Because the update schedule is very quick (in millisecond) so is better to use UDP like this:

    public void onRoomUpdated(MMORoom room, List players) {
        players.forEach(player -> {
            // Check if player's position or rotation is updated
            if (player.isStateChanged()) {
                responseFactory.newArrayResponse()
                    .udpTransport()
                    .command(Commands.SYNC_POSITION)
                    .param(player.getName())
                    .param(player.getPosition().toArray())
                    .param(player.getRotation().toArray())
                    .param(player.getClientTimeTick())
                    .usernames(player.getNearbyPlayerNames())
                    .execute();
                player.setStateChanged(false);
            }
        });
    }

To create a MMO game, you can take a look EzySmashers. It's using EzyFox, Game Box for server and Unity for client.