Following classic RPC principles, the best way to think about cmnRPC is as a networked version of script_execute - todo
This is a "cheat sheet" for "cmnRPC" extension by YellowAfterlife.
Click on sections to expand/collapse them.
Quick display controls: Categories
· Sections
· Everything
·
Initial setup: Add the following to a persistent controller object's Step event:
Add the following to a persistent controller object's Async - Network event:
Additional setup for Steam API: Add the following to a persistent controller object's Async - Steam event:
The basics:
The following relate to networking core in cmnRPC.
Fully resets the extension state.
This is done automatically in host/join.
Attempts to host a server on the given port.
Custom adapters may interpret the port differently.
Calls rpc_net_on_host once the result is known.
Attempts to join a server at the given endpoint.
Custom adapters may interpret the url+port differently.
Calls rpc_net_on_join once the result is known.
Should be ran in step event of some controller object.
The current network adapter.
You should generally avoid changing this mid-session.
See custom adapters and built-in adapters for more information.
Game version. The server will reject connections with mismatched game version.
Your own display name.
This is communicated to the host upon joining and is what is obtained through
rpc_net_on_arrival / rpc_user_get_name.
Maximum number of connected clients when hosting.
Sets whether the game can be joined.
It is common to "lock" the game if you don't allow mid-session joining.
Custom adapters may interpret this differently to show/hide lobby from lists.
Read-only; holds the current value set by rpc_net_set_allow_join.
This is called whenever cmnRPC needs to log debug information.
By default this will call show_debug_message.
Determines what information cmnRPC will log via rpc_log.
Levels are as following:
The following read-only variables let you easily fetch some information about cmnRPC state.
Indicates that we are in a game (hosting or joined).
This is set to true as soon as calling host/join functions.
Indicates whether we are the server.
This is set to true when not hosting/joining to simplify the logic.
Indicates that we are a client and currently trying to connect to a server.
Indicates whether we are the server and currently trying to host a game.
Your own UID.
UID of the server-player.
If you are the server, this is equal to rpc_net_local_uid.
The following are global variables that you can assign scripts to
and the extension will call them at according times.
Here, "status" is a rpc_status_* value.
Is called once it is known whether you've successfully hosted a server.
Is called once it is known whether you've successfully hosted a server.
Is called by clients when connection to server is lost. Reason is provided.
Is called by clients and server when someone leaves the game.
This will also be called when someone times out.
Is called when a client joins - both for server and other clients.
Calls the given script for everyone, including you.
For example,
would show "<player name>: <text>" for everyone.
Calls the given script for everyone except you:
Calls the given script for given player.
If the UID happens to be your own, this executes for yourself without sending anywhere.
For example,
Inside RPC scripts, this holds the UID of whomever that sent the message.
The following let you manage connections with remote players.
Returns the number of other users in the session.
Returns the UID of the user at given position (0...count-1).
Returns the display name for the user with given UID,
as set via rpc_net_local_name.
Returns "" if UID is invalid / user could not be found.
May vary by platform, such as:
Note: you don't know remotes of other players as client nor your own.
Attempts to kick the user with specified UID, returns whether successful.
The function will fail if:
Status codes:
All is well!
May be returned by custom adapters, but not by default.
May be returned by custom adapters.
Returned by default TCP/UDP adapters if server cannot be bound to port.
Connection timeout.
You have tried to join a server but your version doesn't match.
The player is leaving voluntarily.
You've been kicked.
The session has already started and thus cannot be joined.
Connection closed but reason is not known
Helpers:
Returns a string name for given RPC status code.
The following let you hint argument types when passing them to call functions
to pack values slightly tighter.
This is a little less efficient than rpc_script_args
(as it'll still use a prefix byte for most types),
but is sufficient for most cases.
A boolean. Fits into one byte as you'd expect.
An integer in -8..+7 range.
An integer in 0..255 range.
An integer in -2048..+2047 range.
An integer in 0..65535 range.
An integer in -524280..+524279 range.
Takes up 3 bytes.
An integer in -2147483648..+2147483647
An integer in 0..4294967295
A 64-bit integer
A 32-bit float
A 64-bit float
Your normal null-terminated string
[GMS2 only] Compresses a string using buffer_compress.
A single-layer ds_map.
Note that this creates a map on receiving end and you will want to destroy that.
A single-layer ds_list.
Note that this creates a list on receiving end and you will want to destroy that.
Sends a part of the given buffer.
Note that this creates a buffer on receiving end and you will want to destroy that.
...
Automatically decides how to write a value.
Reads a value previously written by rpc_buffer_write.
For cases where the bandwidth is of uttermost concern,
you may define custom argument handlers on per-script basis.
Argument scripts will be called with 3 arguments - the buffer to write/read from,
whether this is a write operation, and the value (if it is a write operation).
The script should return the value on read operation and can return whatever on write.
For arguments out of specified argument range, the last defined argument's script will be called.
This defines how cmnRPC is going to write script indexes.
By default this is set to buffer_u16, but you may change it to buffer_u8 if your RPC-related scripts all are in ID<256 range, or increase it to buffer_u32 if you somehow have more than 65536 scripts.
Scripts can be assigned into these two global variables to filter RPC packets.
You may want to do so to automatically discard packets not belonging to the current game state.
Called after writing the script arguments, may write additional information into the buffer.
Called after reading the script arguments, may read what you wrote in the previous step and return whether to process the packet.
...
Creates a blank RPC adapter for you to fill out.
Should be called by your adapter whenever a packet is received.
endpoint can be any value so long as you provide the same endpoint for the same sender
(e.g. socket ID or )
Should be called when a client connects.
Can be ignored for connection-less protocols.
Should be called when a client disconnects.
Can be ignored for connection-less protocols.
Should be called when you find out whether a server could be created.
Should be called when you find out whether you could connect to a server.
Essentially a delayed script_execute -
calls the given script with given arguments the next time rpc_net_update is called.
This can be used when you want something to happen the next frame, but not immediately.
Field macros:
This should be set to something that uniquely identifies this adapter.
Called to cleanup the adapter state.
This is called by rpc_net_reset and should drop the existing connections (if any).
Is called once per frame.
Is called when something should be sent.
Should return whether it succeeded.
Is called when needing to drop connection to remote.
For connection-less protocols this can be ignored.
Is called when attempting to host a game.
Should call rpc_adapter_server_created once result is known.
Is called when attempting to join a game.
Should call rpc_adapter_connected_to_server once result is known.
Is called by rpc_net_set_allow_join.
For platforms without lobby visibility features this can be ignored.
This is the default adapter.
Should be called in Async - Network event.
This adapter uses connection-less UDP.
Note that it does not automatically handle packet loss/integrity,
so you would want to enable reliable UDP (via network_set_config)
when not targeting platforms with custom UDP socket types.
This is a value from network_socket_*
enumeration that will determine the type of socket
to create. By default this is network_socket_udp,
but you may change it to platforms-specific socket types.
Should be called in Async - Network event.
This adapter is included as cmnRPC_Steam extension.
Notes:
Should be called in Async - Steam event.
rpc_net_update();
rpc_adapter_tcp_async();
rpc_adapter_steam_async();
rpc_net_adapter = rpc_adapter_tcp_create();
rpc_net_version = "1.4";
rpc_net_max_clients = 3; // 4P total (including you)
This happens immediately before the user information is freed up.
if (keyboard_check_pressed(vk_enter)) {
rpc_call(keyboard_string, scr_text_message);
}
// in scr_text_message:
show_debug_message(rpc_user_get_name(rpc_sender) + ": " + argument0);
// or, in GMS2.3+:
rpc_call(keyboard_string, function(_text) {
show_debug_message(rpc_user_get_name(rpc_sender) + ": " + _text);
});
x += xspeed;
y += yspeed;
rpc_call_ns(x, y, scr_sync_player);
// and in scr_sync_player:
with (obj_player) if (uid == rpc_sender) {
x = argument0;
y = argument1;
break;
}
// or, in GMS2.3+:
x += xspeed;
y += yspeed;
rpc_call_ns(x, y, function(_x, _y) {
with (obj_player) if (uid == rpc_sender) {
x = _x;
y = _y;
break;
}
});
rpc_call_for(rpc_net_server_uid, block.x, block.y, scr_req_break_block);
// then in scr_req_break_block:
with (obj_block) if (x == argument0 && y == argument1) {
// (add server-side validation if needed)
rpc_call(x, y, scr_break_block);
break;
}
// and in scr_break_block:
with (obj_block) if (x == argument0 && y == argument1) {
instance_destroy();
break;
}
// or, in GMS2.3:
rpc_call_for(rpc_net_server_uid, block.x, block.y, function(_req_x, _req_y) {
with (obj_block) if (x == _req_x && y == _req_y) {
// (add server-side validation if needed)
rpc_call(x, y, function(_break_x, _break_y) {
with (obj_block) if (x == _break_x && y == _break_y) {
instance_destroy();
break;
}
});
break;
}
});
This packs the argument into a single byte.
This packs the argument into two bytes.
Good for large monotonous strings like JSON or INI-based save files.
// on init:
rpc_script_args[?scr_sync_pos] = [scr_proc_s16, scr_proc_s16];
// as per above, this would also work here:
// rpc_script_args[?scr_sync_pos] = [scr_proc_s16];
// later:
rpc_call_ns(x, y, scr_sync_pos);
// in scr_proc_s16:
if (argument1) {
buffer_write(argument0, buffer_s16, argument2);
} else return buffer_read(argument0, buffer_s16);
rpc_filter_write = scr_filter_write;
// in scr_filter_write:
buffer_write(argument0, buffer_s32, global.context);
rpc_filter_write = scr_filter_read;
// in scr_filter_write:
return buffer_read(argument0, buffer_s32) == global.context;
var q = new RpcAdapter();
q.kind = "Steam";
It uses Steamworks.gml;