GameMaker
The GameMaker extension is in beta, and may not be stable. Please report any issues you find.
The GameMaker SDK is built on top of the shared Colyseus Native SDK, which provides cross-platform support for Colyseus across different engines. The work on Native SDK is still in progress, so expect some breaking changes as we go.
Platforms
- Windows (x86_64)
- macOS (arm64, x86_64)
- Linux (x86_64)
- iOS (arm64)
- Android (arm64, arm32, x86_64)
- HTML5 (WASM)
Installation
- Download the latest
.yympsfrom GitHub Releases - In GameMaker, go to Tools → Import Local Package
- Select the
.yympsfile, select all resources, and click Import
The import adds the Colyseus_SDK extension and two scripts, Colyseus and ColyseusPredict, to your project. Keep both: the first calls into the second, so a project with only one of them does not compile.
Project Setup
The SDK uses an event queue pattern to integrate with GameMaker’s frame loop. You need an Object with Create, Step, and Clean Up events to manage the connection lifecycle.
You must call colyseus_process() once per frame in the Step event to poll and dispatch all queued events.
Create the client from the Step event, on the first frame colyseus_is_ready() returns true. On HTML5 the WASM module finishes instantiating after the game has started, so a Create event runs too early; native is ready from the first frame, so the same code works everywhere.
Quick Example
This example shows how to connect to a room, listen for state changes, send messages, and leave the room.
/// Create Event: nothing is connected yet
client = 0;
net_room = 0;/// Step Event: connect once the extension is ready, then poll every frame
if (client == 0) {
if (!colyseus_is_ready()) exit;
client = colyseus_client_create("http://localhost:2567");
net_room = colyseus_client_join_or_create(client, "my_room", "{}");
net_setup_handlers(net_room); // see below
}
colyseus_process();
if (colyseus_room_is_connected(net_room)) {
var _dx = keyboard_check(vk_right) - keyboard_check(vk_left);
var _dy = keyboard_check(vk_down) - keyboard_check(vk_up);
if (_dx != 0 || _dy != 0) {
x += _dx * 4;
y += _dy * 4;
colyseus_send(net_room, "move", { x: x, y: y });
}
}/// Room event handlers. Called from the object's Step event, so the
/// function literals below are bound to that instance and can store
/// instance variables (player_objs, callbacks).
function net_setup_handlers(net_room) {
colyseus_on_join(net_room, function(_room) {
show_debug_message("Joined room: " + colyseus_room_get_id(_room));
show_debug_message("Session ID: " + colyseus_room_get_session_id(_room));
// Store as instance variables (no "var") so nested callbacks can access them
player_objs = {};
callbacks = colyseus_callbacks_create(_room);
// Listen to root state properties (instance can be omitted for root)
colyseus_listen(callbacks, "currentTurn", function(value, prev) {
show_debug_message("Turn changed: " + string(prev) + " -> " + string(value));
});
// Listen for players added to the "players" map
colyseus_on_add(callbacks, "players", function(instance, key) {
show_debug_message("Player joined: " + key);
var _px = colyseus_schema_get(instance, "x");
var _py = colyseus_schema_get(instance, "y");
var _player_obj = instance_create_depth(_px, _py, 0, obj_player);
player_objs[$ key] = _player_obj;
// GML anonymous functions can access instance variables (like "callbacks"),
// but NOT outer local variables (declared with "var").
// Use method() to pass local variables into nested callbacks:
colyseus_listen(callbacks, instance, "x", method({ obj: _player_obj }, function(v, p) {
obj.x = v;
}));
colyseus_listen(callbacks, instance, "y", method({ obj: _player_obj }, function(v, p) {
obj.y = v;
}));
});
// Listen for players removed from the map
colyseus_on_remove(callbacks, "players", method({ objs: player_objs }, function(instance, key) {
show_debug_message("Player left: " + key);
if (variable_struct_exists(objs, key)) {
instance_destroy(objs[$ key]);
delete objs[$ key];
}
}));
});
colyseus_on_error(net_room, function(code, msg) {
show_debug_message("Room error [" + string(code) + "]: " + msg);
});
colyseus_on_leave(net_room, function(code, reason) {
show_debug_message("Left room [" + string(code) + "]: " + reason);
});
colyseus_on_message(net_room, function(_room, _type, _data) {
// _data is auto-decoded: struct for maps, string/number/bool for primitives
show_debug_message("Message [" + _type + "]: " + json_stringify(_data));
});
}/// Clean Up Event: free resources
if (net_room != 0) {
colyseus_room_leave(net_room);
colyseus_room_free(net_room); // also frees associated callbacks
}
if (client != 0) {
colyseus_client_free(client);
}SDK API
Client
| Function | Description |
|---|---|
colyseus_client_create(endpoint) | Create a client. Returns a client handle. |
colyseus_client_free(client) | Free client resources. |
colyseus_client_join_or_create(client, room_name, options_json) | Join or create a room. Returns a room handle. |
colyseus_client_join(client, room_name, options_json) | Join an existing room. |
colyseus_client_create_room(client, room_name, options_json) | Create a new room. |
colyseus_client_join_by_id(client, room_id, options_json) | Join a room by its ID. |
colyseus_client_reconnect(client, reconnection_token) | Re-take a seat with a token saved from colyseus_room_get_reconnection_token(), for example after the process was killed. The server must allowReconnection(). |
Room
| Function | Description |
|---|---|
colyseus_room_is_connected(room) | Returns 1 if connected, 0 otherwise. |
colyseus_room_get_id(room) | Get the room’s unique ID. |
colyseus_room_get_session_id(room) | Get your session ID in the room. |
colyseus_room_get_name(room) | Get the room type name. |
colyseus_room_leave(room) | Leave the room gracefully. |
colyseus_room_free(room) | Free room resources (also frees callbacks). |
colyseus_room_get_reconnection_token(room) | The "roomId:token" string for colyseus_client_reconnect(); empty until joined. |
colyseus_room_is_reconnecting(room) | Returns 1 while an automatic reconnection is in progress. |
colyseus_room_set_reconnection_options(room, enabled, max_retries, min_delay_ms, max_delay_ms, min_uptime_ms, delay_ms, max_enqueued_messages) | Tune automatic reconnection; pass -1 to keep a value. |
Room Events (GML helpers)
Register callbacks for room lifecycle events. These are dispatched by colyseus_process().
| Function | Callback signature |
|---|---|
colyseus_on_join(room, handler) | handler(room) |
colyseus_on_state_change(room, handler) | handler(room) |
colyseus_on_message(room, handler) | handler(room, type, data) (data is auto-decoded) |
colyseus_on_error(room, handler) | handler(code, message) |
colyseus_on_leave(room, handler) | handler(code, reason) |
colyseus_on_drop(room, handler) | handler(code, reason), once when the connection drops with a recoverable code and automatic reconnection starts |
colyseus_on_reconnect(room, handler) | handler(), when the reconnection succeeds; messages sent while dropped have been flushed by then |
Sending Messages
| Function | Description |
|---|---|
colyseus_send(room, type, data) | Send a message. data is a struct (sent as a map of strings, numbers and booleans), or a bare string, number or boolean. |
colyseus_room_send(room, type, data) | Send raw encoded bytes with a string type. |
colyseus_room_send_bytes(room, type, data, length) | Send raw bytes. |
colyseus_room_send_int(room, type, data) | Send raw encoded bytes with a numeric type. |
For advanced message construction:
| Function | Description |
|---|---|
colyseus_message_create_map() | Create a message builder. Returns a message handle. |
colyseus_message_put_str(msg, key, value) | Add a string field. |
colyseus_message_put_number(msg, key, value) | Add a number field. |
colyseus_message_put_bool(msg, key, value) | Add a boolean field (1/0). |
colyseus_room_send_message(room, type, msg) | Send and free the constructed message. |
colyseus_message_free(msg) | Free a message (only needed if not sent). |
State & Schema
Schema instances arrive as GML structs: colyseus_room_get_state(room) returns the root state with every field (and nested ref) as a member, and the structs a callback receives are the same objects. The structs are refreshed after every patch, so dot access is current as of the last colyseus_process(). Any function below also accepts a struct where it says instance.
| Function | Description |
|---|---|
colyseus_room_get_state(room) | The root state as a struct (undefined until the first state arrives). |
colyseus_schema_get(instance, field) | Read one field: a string, a number (booleans read as 1/0), a struct for refs, undefined if the field does not exist. |
colyseus_schema_get_field_type(instance, field) | The field type as a number (COLYSEUS_TYPE_*), -1 if the field does not exist. |
colyseus_map_get(instance, field, key) | An entry of a MapSchema by key: a struct for schema entries, the value for primitive-valued maps, undefined when absent. |
State Change Callbacks (GML helpers)
Create a callbacks manager to listen for state mutations. Omit the instance argument to listen on the root state.
| Function | Callback signature |
|---|---|
colyseus_callbacks_create(room) | Create a callbacks manager. Returns a callbacks handle. |
colyseus_listen(callbacks, [instance], property, handler) | handler(value, previous_value) |
colyseus_on_add(callbacks, [instance], property, handler) | handler(instance, key) |
colyseus_on_remove(callbacks, [instance], property, handler) | handler(instance, key) |
colyseus_on_change(callbacks, [instance], property, handler) | handler(key, value) for any change inside a collection field |
colyseus_on_change(callbacks, instance, handler) | handler() after any field of that instance changes |
colyseus_callbacks_remove_handle(callbacks, handle) | Stop listening for a specific callback (each registration returns its handle). |
colyseus_callbacks_free(callbacks) | Free callbacks (auto-freed when room is freed). |
Event Processing
| Function | Description |
|---|---|
colyseus_process() | Call once per frame. Polls the event queue and dispatches registered handlers. |
Running the test server locally
To run the test server locally, you will need to run the following commands in your terminal:
git clone https://github.com/colyseus/sdks-test-server
cd sdks-test-server
npm install
npm startYou can see the source code for the test server here.
You should be able to see the server running at http://localhost:2567, and the example project will be able to connect to it.
Debugging
Breakpoints
If you set a breakpoint while the WebSocket connection is open, the connection closes automatically after 3 seconds due to inactivity. To prevent the WebSocket connection from dropping, use pingInterval: 0 during development:
import { defineServer } from "colyseus";
import { WebSocketTransport } from "@colyseus/ws-transport";
const server = defineServer({
// ...
transport: new WebSocketTransport({
pingInterval: 0,
}),
// ...
});Make sure to have a pingInterval higher than 0 on production. The default pingInterval value is 3000.
Next Steps
- Client SDK API Reference - Full API documentation for all methods, events, and room operations
- State Synchronization - Understanding Schema definitions
- Room API - Server-side room implementation
GameMaker
Construct 3
Cocos Creator