Colyseus Arena Room Create - Error Loading Scripts: utf8Read Invalid Byte 81

I have been following the Colyseus Multiplayer Server tutorial and have run into a roadblock when creating a new room. Please see the image below which contains a screenshot of the error thrown from my PlayCanvas app and the logs from the Colyseus Arena server. It does seem as if things are working correctly on the Arena server-side, as both the ‘create’ and ‘join’ functions print their logs without errors. However, the asynchronous function call client.joinOrCreate is throwing an error when the callback resolves. Namely, the error is

Uncaught Error: Invalid byte 81
    at utf8Read (Protocol.ts:64:11)
    at Room.onMessageCallback (Room.ts:167:33)

I have been trying to troubleshoot this issue, however I can’t even really tell if the issue is being generated in the external scripts provided by Arena, the PlayCanvas framework, the code I have written in both PlayCanvas and node.js, or some combination of all of the above. The utf8Read seems to be under-the-hood in PlayCanvas, but Room.onMessage is definitely a Colyseus function. Now if I do something like provide an incorrect room name, the try/catch statement executes and something like Network Controller Error: Name 'bad-name' not found in server is printed to the console, but when I provide the correct name I get the Uncaught Error shown below. So the function call and try statement seem to generally work on both the client-side and server-side, but something is going wrong assigning the function return to the local variable? I’m not sure. Here is the code snippet from my app. I’ve changed it a few times and had the same result so I’ve reverted the code to use the async function suggested by the tutorial, and the Colyseus server code is the barebones npm init colyseus-app with a couple added message handlers:

var NetworkController = pc.createScript('networkController');

NetworkController.attributes.add('interval', { type: 'number' });

NetworkController.prototype.initialize = function () {
    this.session = {
        client: null,
        room: null,
        roomName: 'my_room'
    };
    this.session.client = new Colyseus.Client('wss:ycixrc.colyseus.dev');
};

NetworkController.prototype.update = function (dt) {
    let keys = this.app.keyboard;
    if (keys.wasPressed(pc.KEY_1)) { this.sessionManager('join-or-create'); }
    if (this.session.room) {
        if (keys.wasPressed(pc.KEY_2)) { this.sessionManager('update-interval'); }
        if (keys.wasPressed(pc.KEY_3)) { this.sessionManager('toggle-on'); }
        if (keys.wasPressed(pc.KEY_4)) { this.sessionManager('toggle-off'); }
        if (keys.wasPressed(pc.KEY_5)) { this.sessionManager('leave'); }
        this.session.room.state.onChange = (changes) => {
            console.log('=============================');
            changes.forEach(change => {
                console.log('-------------------------------');
                console.log(change.field);
                console.log(change.value);
                console.log(change.previousValue);
                console.log('-------------------------------');
            });
        };
    }
};

NetworkController.prototype.sessionManager = async function (action) {
    try {
        switch (action) {
            case 'join-or-create':
                console.log('here before create');
                this.session.room = await this.session.client.joinOrCreate(this.session.roomName);
                console.log('here after create');
                console.log(this.session.room);
                break;
            case 'update-interval':
                console.log('here before update');
                this.session.room.send('updateInterval', { newInterval: this.interval });
                console.log('here after update');
                break;
            case 'toggle-on':
                console.log('here before toggle on');
                this.session.room.send('toggleRun', { run: true });
                console.log('here after toggle on');
                break;
            case 'toggle-off':
                console.log('here before toggle off');
                this.session.room.send('toggleRun', { run: false });
                console.log('here after toggle off');
                break;
            case 'leave':
                console.log('here before leave');
                this.session.room.leave();
                console.log('here after leave');
                break;
        }
    } catch (err) {
        console.log(`Network Controller Error: ${err.message}`);
    }
};

Hi @bdb. The project you link to is private. Can you make it public if you would like others on the forum to investigate, please?

@will Sorry about that! I have set the project to public. I am actively developing on the project however, so I published the version with this scenario intact.

This does look more like an issue with the external scripts. It looks like the client.joinOrCreate call gets into the code below, and the last line is what throws the error:

 protected onMessageCallback(event: MessageEvent) {
        const bytes = Array.from(new Uint8Array(event.data))
        const code = bytes[0];

        if (code === Protocol.JOIN_ROOM) {
            let offset = 1;

            const reconnectionToken = utf8Read(bytes, offset);
            offset += utf8Length(reconnectionToken);

            this.serializerId = utf8Read(bytes, offset);

So I would guess the bytes array is not being populated properly. I have separated the joinOrCreate into separate calls to create and join. This results in the following:

  1. Press 1 to create new room. Room is created and joined as per the server logs. Error thrown in client

  2. Press 2 to join the created room. Server logs show new join ID. Same error thrown in client when assigning the returned ‘room’ object to a local variable

  3. I can join/create repeatedly and the server logs seem to respond properly, but every call ends with the same client error and the local room variable is not populated

@endel Would you be able to help here?

1 Like

Hi there!

The tutorial we’ve made is using Colyseus 0.15@preview, which is not supported on Arena yet, they’re going to support the new 0.15 very soon, perhaps we should’ve done the tutorial using 0.14 :sweat_smile:

You can downgrade to version 0.14 in order to use Arena - the only noticeable API changes between versions are the schema callbacks though, documented here: 0.15 - Colyseus & Arena Cloud Documentation


The “external script” for 0.14 inside PlayCanvas is:

https://unpkg.com/colyseus.js@0.14.13/dist/colyseus.js

For the server-side you can run this command to update all modules to 0.14:

npm install @colyseus/loadtest@latest --save
npm install @colyseus/testing@latest --save
npm install @colyseus/arena@latest --save
npm install @colyseus/monitor@latest --save
npm install colyseus@latest --save

(Sorry for the delay to answer, I’m taking a few days on vacation!)

4 Likes

Thank you for the reply @endel - and please don’t worry about any delay, I appreciate your quick feedback. I had noticed the version was a preview and tried using the 0.14 external script link - which failed but I didn’t think to make other changes on the server side. I’ll give this all a try later today.

I am having the same issue.

The issue is caused when the game setState function is called on the server side. If we remove that then state serializer error is called. I have not been able to fix it yet. Is there a possible solution.

@Muhammad_Hassan_Ali Have you gone through the fixes posted by @endel above?

Thx. Solved it. You have to use the same version of external script in Playcanvas as the one you are developing on node server on. The error occurs as @endel said that has something to do with version. Just keep the version same.

Hello,

There is the same issue by using this process : Real-time Multiplayer with Colyseus | Babylon.js Documentation

There is a demo here : https://playground.babylonjs.com/#3K64VU#2

@endel any idea,solution?

I tried with version 0.14 but room.state.players is undefined.

Ok Found it!

The server was 0.14 and the client was 0.15. So, it’s important to clone and start this server version to run with the babylon tutorial

2 Likes