Skip to content

Commit

Permalink
Merge pull request #66 from colyseus/0.13.0
Browse files Browse the repository at this point in the history
0.13.0
  • Loading branch information
endel committed Apr 22, 2020
2 parents d8a92c6 + d1bd377 commit 3fae162
Show file tree
Hide file tree
Showing 11 changed files with 3,792 additions and 3,389 deletions.
6,972 changes: 3,636 additions & 3,336 deletions dist/colyseus.dev.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/colyseus.js

Large diffs are not rendered by default.

40 changes: 12 additions & 28 deletions dist/index.html
Expand Up @@ -11,8 +11,11 @@
<strong>Messages</strong><br>

<form id="form">
<input type="text" id="input" value="" />
<input type="submit" value="send" />
Room name: <input type="text" id="room_name" value="dummy" /> <br />

Send data: <input type="text" id="message_type" value="" placeholder="messageType" />
<input type="text" id="message_data" value="" placeholder="data" />
<input type="submit" value="send" />
</form>

<div id="messages"></div>
Expand All @@ -30,29 +33,10 @@
var client = new Colyseus.Client('ws://' + host + ':2567');
var room;

function request () {
var http = new XMLHttpRequest();
var params = '{"lorem":"ipsum","name":"binny"}';
http.open("POST", "http://localhost:2567/something", true);

// Send the proper header information along with the request
// http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
http.setRequestHeader("Content-type", "application/json");

// Call a function when the state changes.
http.onreadystatechange = function() {
if(http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
}

http.send(params);
}

function addListeners (room) {
console.log('joined!');
room.onMessage(function(message) {
console.log("message:", room.sessionId, message);
room.onMessage("*", (type, message) => {
console.log("received message:", type, "=>", message);
});

room.onLeave(function() {
Expand All @@ -65,7 +49,7 @@
}

function join () {
client.join('dummy', {}).then((r) => {
client.join(document.getElementById('room_name').value, { code: "one" }).then((r) => {
room = r;
addListeners(room);
}).catch(e => {
Expand All @@ -74,14 +58,14 @@
}

function create () {
client.create('dummy').then((r) => {
client.create(document.getElementById('room_name').value, { code: "one" }).then((r) => {
room = r
addListeners(room);
});
}

function joinOrCreate () {
client.joinOrCreate('dummy', {}).then((r) => {
client.joinOrCreate(document.getElementById('room_name').value, { code: "one" }).then((r) => {
room = r
addListeners(room);
});
Expand Down Expand Up @@ -116,10 +100,10 @@
document.getElementById('form').onsubmit = function(e) {
e.preventDefault()

room.send(document.getElementById('input').value);
room.send(document.getElementById('message_type').value, document.getElementById('message_data').value);

// room.send(document.getElementById('input').value);
document.getElementById('input').value = null
document.getElementById('message_data').value = null
}
</script>

Expand Down
12 changes: 7 additions & 5 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "colyseus.js",
"version": "0.12.2",
"version": "0.13.0",
"description": "Multiplayer Game Client for the Browser",
"keywords": [
"colyseus",
Expand All @@ -12,13 +12,15 @@
],
"repository": {
"type": "git",
"url": "git://github.com/gamestdio/colyseus.js.git"
"url": "git://github.com/colyseus/colyseus.js.git"
},
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"scripts": {
"test": "mocha test/*.ts --require ts-node/register",
"build": "webpack --env.production",
"build-dev": "webpack",
"build-all": "npm run build && npm run build-dev",
"watch": "tsc -w",
"prepublish": "tsc && npm run build",
"tslint": "tslint --project ."
Expand Down Expand Up @@ -56,10 +58,10 @@
"ts-loader": "^6.2.1",
"ts-node": "^6.0.3",
"tslint": "^5.9.1",
"typescript": "~3.5.0",
"typescript": "^3.8.3",
"uglify-js": "^2.6.1",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11",
"ws": "^6.1.2"
}
}
3 changes: 2 additions & 1 deletion src/Client.ts
@@ -1,5 +1,6 @@
import { post, get } from "httpie";

import { ServerError } from './errors/ServerError';
import { Room, RoomAvailable } from './Room';
import { Auth } from './Auth';
import { Push } from './Push';
Expand Down Expand Up @@ -63,7 +64,7 @@ export class Client {
room.connect(this.buildEndpoint(response.room, { sessionId: room.sessionId }));

return new Promise((resolve, reject) => {
const onError = (message) => reject(message);
const onError = (code, message) => reject(new ServerError(code, message));
room.onError.once(onError);

room.onJoin.once(() => {
Expand Down
9 changes: 7 additions & 2 deletions src/Connection.ts
Expand Up @@ -23,9 +23,14 @@ export class Connection extends WebSocketClient {
}
}

public send(data: any): void {
public send(data: ArrayBuffer | Array<number>): void {
if (this.ws.readyState === WebSocketClient.OPEN) {
return super.send( msgpack.encode(data) );
if (data instanceof ArrayBuffer) {
return super.send(data);

} else if (Array.isArray(data)) {
return super.send((new Uint8Array(data)).buffer);
}

} else {
// WebSocket not connected.
Expand Down
13 changes: 12 additions & 1 deletion src/Protocol.ts
Expand Up @@ -4,14 +4,25 @@ export enum Protocol {
// Room-related (10~19)
HANDSHAKE = 9,
JOIN_ROOM = 10,
JOIN_ERROR = 11,
ERROR = 11,
LEAVE_ROOM = 12,
ROOM_DATA = 13,
ROOM_STATE = 14,
ROOM_STATE_PATCH = 15,
ROOM_DATA_SCHEMA = 16,
}

export enum ErrorCode {
MATCHMAKE_NO_HANDLER = 4210,
MATCHMAKE_INVALID_CRITERIA = 4211,
MATCHMAKE_INVALID_ROOM_ID = 4212,
MATCHMAKE_UNHANDLED = 4213,
MATCHMAKE_EXPIRED = 4214,

AUTH_FAILED = 4215,
APPLICATION_ERROR = 4216,
}

export function utf8Read(view: number[], offset: number) {
const length = view[offset++];

Expand Down
114 changes: 101 additions & 13 deletions src/Room.ts
Expand Up @@ -11,6 +11,9 @@ import { SchemaSerializer } from '.';
import { SchemaConstructor } from './serializer/SchemaSerializer';
import { Context, Schema } from '@colyseus/schema';

import * as encode from '@colyseus/schema/lib/encoding/encode';
import * as decode from '@colyseus/schema/lib/encoding/decode';

export interface RoomAvailable<Metadata> {
roomId: string;
clients: number;
Expand All @@ -27,8 +30,7 @@ export class Room<State= any> {
// Public signals
public onJoin = createSignal();
public onStateChange = createSignal<(state: State) => void>();
public onMessage = createSignal<(data: any) => void>();
public onError = createSignal<(message: string) => void>();
public onError = createSignal<(code: number, message?: string) => void>();
public onLeave = createSignal<(code: number) => void>();

public connection: Connection;
Expand All @@ -41,6 +43,8 @@ export class Room<State= any> {
// TODO: remove me on 1.0.0
protected rootSchema: SchemaConstructor<State>;

protected onMessageHandlers: { [messageType: string]: (message: any) => void } = {};

constructor(name: string, rootSchema?: SchemaConstructor<State>) {
this.id = null;
this.name = name;
Expand All @@ -55,7 +59,7 @@ export class Room<State= any> {
this.serializer = new (getSerializer("fossil-delta"));
}

this.onError((message) => message && console.error(message));
this.onError((code, message) => console.error(`colyseus.js - onError => (${code}) ${message}`));
this.onLeave(() => this.removeAllListeners());
}

Expand All @@ -66,15 +70,15 @@ export class Room<State= any> {
this.connection.onclose = (e: CloseEvent) => {
if (!this.hasJoined) {
console.error(`Room connection was closed unexpectedly (${e.code}): ${e.reason}`);
this.onError.invoke(e.reason);
this.onError.invoke(e.code, e.reason);
return;
}

this.onLeave.invoke(e.code)
};
this.connection.onerror = (e: CloseEvent) => {
console.warn(`Room, onError (${e.code}): ${e.reason}`);
this.onError.invoke(e.reason);
this.onError.invoke(e.code, e.reason);
};
this.connection.open();
}
Expand All @@ -92,8 +96,49 @@ export class Room<State= any> {
}
}

public send(data): void {
this.connection.send([Protocol.ROOM_DATA, data]);
public onMessage<T = any>(
type: "*",
callback: (type: string | number | Schema, message: T) => void
)
public onMessage<T extends (typeof Schema & (new (...args: any[]) => any))>(
type: T,
callback: (message: InstanceType<T>) => void
)
public onMessage<T = any>(
type: string | number,
callback: (message: T) => void
)
public onMessage<T = any>(
type: '*' | string | number | typeof Schema,
callback: (...args: any[]) => void
) {
this.onMessageHandlers[this.getMessageHandlerKey(type)] = callback;
return this;
}

public send(type: string | number, message?: any): void {
const initialBytes: number[] = [Protocol.ROOM_DATA];

if (typeof(type) === "string") {
encode.string(initialBytes, type);

} else {
encode.number(initialBytes, type);
}

let arr: Uint8Array;

if (message !== undefined) {
const encoded = msgpack.encode(message);
arr = new Uint8Array(initialBytes.length + encoded.byteLength);
arr.set(new Uint8Array(initialBytes), 0);
arr.set(new Uint8Array(encoded), initialBytes.length);

} else {
arr = new Uint8Array(initialBytes);
}

this.connection.send(arr.buffer);
}

public get state (): State {
Expand All @@ -104,7 +149,7 @@ export class Room<State= any> {
// this method is useful only for FossilDeltaSerializer
public listen(segments: string, callback: Function, immediate?: boolean) {
if (this.serializerId === "schema") {
console.error(`'${this.serializerId}' serializer doesn't support .listen() method.`);
console.error(`'${this.serializerId}' serializer doesn't support .listen() method here.`);
return;

} else if (!this.serializerId) {
Expand All @@ -126,7 +171,6 @@ export class Room<State= any> {
}
this.onJoin.clear();
this.onStateChange.clear();
this.onMessage.clear();
this.onError.clear();
this.onLeave.clear();
}
Expand Down Expand Up @@ -162,8 +206,13 @@ export class Room<State= any> {
// acknowledge successfull JOIN_ROOM
this.connection.send([Protocol.JOIN_ROOM]);

} else if (code === Protocol.JOIN_ERROR) {
this.onError.invoke(utf8Read(bytes, 1));
} else if (code === Protocol.ERROR) {
const it: decode.Iterator = { offset: 1 };

const code = decode.number(bytes, it);
const message = decode.string(bytes, it);

this.onError.invoke(code, message);

} else if (code === Protocol.LEAVE_ROOM) {
this.leave();
Expand All @@ -175,7 +224,7 @@ export class Room<State= any> {
const message: Schema = new (type as any)();
message.decode(bytes, { offset: 2 });

this.onMessage.invoke(message);
this.dispatchMessage(type, message);

} else if (code === Protocol.ROOM_STATE) {
bytes.shift(); // drop `code` byte
Expand All @@ -186,7 +235,17 @@ export class Room<State= any> {
this.patch(bytes);

} else if (code === Protocol.ROOM_DATA) {
this.onMessage.invoke(msgpack.decode(event.data, 1));
const it: decode.Iterator = { offset: 1 };

const type = (decode.stringCheck(bytes, it))
? decode.string(bytes, it)
: decode.number(bytes, it);

const message = (bytes.length > it.offset)
? msgpack.decode(event.data, it.offset)
: undefined;

this.dispatchMessage(type, message);
}
}

Expand All @@ -200,4 +259,33 @@ export class Room<State= any> {
this.onStateChange.invoke(this.serializer.getState());
}

private dispatchMessage(type: string | number | typeof Schema, message: any) {
const messageType = this.getMessageHandlerKey(type);

if (this.onMessageHandlers[messageType]) {
this.onMessageHandlers[messageType](message);

} else if (this.onMessageHandlers['*']) {
(this.onMessageHandlers['*'] as any)(type, message);

} else {
console.warn(`onMessage not registered for type '${type}'.`);
}
}

private getMessageHandlerKey(type: string | number | typeof Schema): string {
switch (typeof(type)) {
// typeof Schema
case "function": return `$${(type as typeof Schema)._typeid}`;

// string
case "string": return type;

// number
case "number": return `i${type}`;

default: throw new Error("invalid message type.");
}
}

}
11 changes: 11 additions & 0 deletions src/errors/ServerError.ts
@@ -0,0 +1,11 @@

export class ServerError extends Error {
public code: number;

constructor(code: number, message: string) {
super(message);

this.name = "ServerError";
this.code = code;
}
}

0 comments on commit 3fae162

Please sign in to comment.