Work in progress.
This is a native ("C") Game Maker compatible wrapper for the C++ Archipelago Client Lib apclientpp, allowing to connect to an Archipelago server with native performance and SSL support.
This was written for Game Maker 7 and 8, however all versions of GM that support the API described in "Using DLL" in
"Designing Games with Game Maker" of GM7/GM8 should be supported.
It only depends on external_define
and external_call
. This is not an "extension" (.gex) file.
For GM7/GM8, execute_string
can be used to handle events. For GMS, an alternative to this is provided.
GMS2+ is (currently) not supported because it uses different syntax for strings.
- Arrays have to be passed as JSON string from GM to the DLL.
- Arrays have to be passed as globals from the DLL to GM callback functions/scripts.
- There is a limit of how many arguments can be passed into DLL functions and as such there are some extra helper functions to "pre-set" arguments.
- DLL functions that have no return value will return true on success or false in case of error.
GM only has number
and string
, so the types listed below are defined as follows
int
is anumber
with an integer value in the range ± 253-1bool
is anumber
with value0
forfalse
ornot 0
(typically1
) fortrue
. This is different from GM's boolean handling where true / false is>=0.5
/<0.5
.str
is astring
in GM, and aconst char*
in Cjson
is astr
that is valid json. A json array is'[...]'
, a json object is'{...}'
.int[]
is either ajson
array of numbers (GM -> DLL) or a global array variable in GM (DLL -> GM)str[]
is either ajson
array of strings (GM -> DLL) or a global array variable in GM (DLL -> GM)double
is anumber
in GM, and adouble
in C.
Below are the possible calls from GM to DLL. The actual call uses external_call
, but should be wrapped in a script
with the same name, making the calls possible as listed below. See gml/.
We use the following notation below: name(arg_name: arg_type, ...): return_type
Warning: some of the functions will only return correct values after room info or when is_data_package_valid()
returns true, typically it's safe to just wait until ap_connected
event was called.
apclient_init(api_version: int): bool
initialize the lib and set API version (where1 <= api_version <= 2
), returnstrue
on success.apclient_deinit(): bool
free all resources (kind of optional, but has to be called before a secondapclient_init
)apclient_connect(uuid: str, game: str, host: str): bool
start connecting to a server. uuid can be an empty string (for now).apclient_poll(): str
call this each frame (step) to handle communication and receive events, returns a string that needs to be executed usingexecute_string
to call your event callbacks.apclient_disconnect(): bool
reset internal state and disconnect.apclient_reset(): bool
reset internal state and reconnect on nextpoll
.apclient_get_player_alias(slot: int): str
returns player alias, player name, or'Unknown'
for given slot.apclient_get_player_game(slot: int): str
returns game name or''
for given slot.apclient_get_location_name(id: int, game: str): str
returns location name or'Unknown'
for given id and game.apclient_get_location_id(name: str): int
returns location id for given name in connected game, or< -9007199254740991
if not found.apclient_get_item_name(id: int, game: str): str
like location_name, but for items.apclient_get_item_id(name: str): int
like location_id, but for items.apclient_render_json(json: str, format: int): str
takes a json string as received byap_print_json
event and turns it into text. Pass0
(orglobal.AP_RENDER_FORMAT_TEXT
) as format to receive plain text.apclient_get_state(): int
returns one of the internal state values, see constantsglobal.AP_STATE_*
.apclient_get_seed(): str
returns seed for connected room or''
if not connected yetapclient_get_slot(): str
returns connecting player (slot) name or""
ifconnect_slot
wasn't called yet.apclient_get_player_number(): int
returns player number for connected player, or -1 if not connected yet.apclient_get_team_number(): int
returns team number for connected player, or -1 if not connected yet.apclient_get_hint_points(): int
returns hint points for connected player.apclient_get_hint_cost_points(): int
returns hint cost in points (locations checked).apclient_get_hint_cost_percent(): int
returns hint cost in percent (% of locations checked).apclient_is_data_package_valid(): bool
returnstrue
if all strings for item and location names are available.apclient_get_server_time(): number
: returns the estimated server time stamp as floating point number. This may be useful for things like deathlink.apclient_has_password(): bool
: available in and afterap_room_info
,true
ifconnect_slot
requires password.apclient_get_checked_locations(): str
: returns a string that should be passed intoexecute_string
to setglobal.ap_checked_locations: int[]
andglobal.ap_checked_locations_len: int
apclient_get_missing_locations(): str
: returns a string that should be passed intoexecute_string
to setglobal.ap_missing_locations: int[]
andglobal.ap_missing_locations_len: int
apclient_bounce(data: json): bool
sends a Bounce with the provided data and the targets selected throughapclient_set_bounce_targets
.apclient_death_link(cause: string): bool
sends a DeathLink Bounce with the provided cause, unless cause is an empty string, in which case it will be omitted. The time and source are retrieved automatically. Targets selected throughapclient_set_bounce_targets
will be ignored in favor of sending the Bounce only to the DeathLink tag.
Some of the events require the return of data that is difficult to represent directly in GM. To remedy this, the data is
stored in JSON form and made accessible through function calls. If an event is received in response to apclient_poll
,
the following functions can be used to query its corresponding JSON data until the next call to apclient_poll
. Most of
the events do not require these functions, as long as execute_string
is available.
These functions take a proxy
, which is an int
that represents a location in the JSON data. When data is first
accessed after an event only proxy = 0
is available, which represents the root level. Some of these functions also take
a key
, which is the name of the element to be accessed. If the data at proxy
is an array, then key
must be a str
representing a number equal to or greater than 0.
apclient_json_proxy(proxy: int, key: str): int
returns a newproxy
, making the data atkey
available through its use, ifkey
is not found atproxy
,-1
is returned instead.apclient_json_exists(proxy: int, key: str): bool
returntrue
ifkey
can be found atproxy
.apclient_json_typeof(proxy: int): int
returns the type ofproxy
, which will be one of the JSON type constants set up in init.apclient_json_size(proxy: int): int
returns the number of elements atproxy
.apclient_json_get_string(proxy: int): str
ifproxy
is of typeAP_JSON_STRING
, returns its value.apclient_json_string_at(proxy: int, key: str): str
shorthand forapclient_json_get_string(apclient_json_proxy(proxy, key))
.apclient_json_get_number(proxy: int): double
ifproxy
is of typeAP_JSON_NUMBER
, returns its value.apclient_json_number_at(proxy: int, key: str): double
shorthand forapclient_json_get_number(apclient_json_proxy(proxy, key))
.apclient_json_dump(proxy: int): str
returns thejson
corresponding toproxy
.apclient_json_source(): str
returns the name of the event that made the data available.
The following functions set variables that would normally be passed into another function, but can't be because of GM limitations.
apclient_set_items_handling(items_handling: int): bool
set the items handling that will be passed to ConnectSlot and ConnectUpdate.apclient_set_version(ma: int, mi: int, r: int): bool
set the mod/client version that will be passed to ConnectSlot.apclient_set_bounce_targets(games: str[], slots: int[], tags: str[]): bool
set the games, slot IDs and tags that will be passed to Bounce.
The following functions interact directly with the AP server and return true
if the command was successfully queue,
or false if the command was invalid or the connection was not established yet.
apclient_say(message: str): bool
sends a chat messageapclient_connect_slot(name: str, password: str, tags: json): bool
connects to a slot. Wait forconnected
orconnection_refused
event to get the result.apclient_connect_update_items_handling(): bool
send ConnectUpdate, only changing items_handling.apclient_connect_update(tags: str[]): bool
send ConnectUpdate, changing both tags and items_handling.apclient_sync(): bool
send Sync message, this should not be needed.apclient_status_update(status: int): bool
sends StatusUpdate message, seeAP_CLIENT_STATUS_*
constants.apclient_location_checks(locations: int[]): bool
sends LocationChecks, marking locations as checked.apclient_location_scouts(locations: int[], create_as_hints: int): bool
sends LocationScouts. Wait forlocation_info
event to get the result.
The following are not implemented. Open for suggestions / requests.
apclient_get_players(): List[NetWorkPlayer]
the return type of this is not easy to implement in GM.apclient_get(...): bool
data storage / GetReply and Received not implemented yetapclient_set_notify(...): bool
as aboveapclient_set(...): bool
as above
The following consts should be set up in the init:
global AP_RENDER_FORMAT_TEXT = 0
- get plain text from render_jsonglobal AP_RENDER_FORMAT_HTML = 1
- this can't be used at the moment because it's not implemented in render_json.global AP_RENDER_FORMAT_ANSI = 2
- get ansi (terminal) color codes from render_jsonglobal.AP_STATE_DISCONNECTED = 0
- disconnectedglobal.AP_STATE_SOCKET_CONNECTING = 1
- connect startedglobal.AP_STATE_SOCKET_CONNECTED = 2
- network connection accepted, nothing received yetglobal.AP_STATE_ROOM_INFO = 3
- room info was received, slot not connected yetglobal.AP_STATE_SLOT_CONNECTED = 4
- slot successfully connected, can send/receive locations/items and chat, etc.global.AP_CLIENT_STATUS_UNKNOWN = 0
- default statusglobal.AP_CLIENT_STATUS_READY = 10
- player is ready to play (setting this is optional at the moment)global.AP_CLIENT_STATUS_PLAYING = 20
- player is playing their game (as above)global.AP_CLIENT_STATUS_GOAL = 30
- player reached their goal. Use this inapclient_status_update
on goal completion.global.AP_JSON_MISSING = -1
- returned byapclient_json_typeof
when failing to determine the type.global.AP_JSON_OBJECT = 0
- returned byapclient_json_typeof
when the value atproxy
is a json object.global.AP_JSON_ARRAY = 1
- returned byapclient_json_typeof
when the value atproxy
is an array.global.AP_JSON_STRING = 2
- returned byapclient_json_typeof
when the value atproxy
is compatible withstring
.global.AP_JSON_NUMBER = 3
- returned byapclient_json_typeof
when the value atproxy
is compatible withnumber
.global.AP_JSON_NULL = 4
- returned byapclient_json_typeof
when the value atproxy
isnull
.
The script returned by apclient_poll
expects the following functions to exist in your scripts to handle events,
so you need to define all of them:
ap_room_info(data: json)
called when receiving RoomInfo from server, argument is currently unused ('{}'
), but the JSON calls can be used to retrieve the data.ap_slot_refused(len: int)
called whenapclient_connect_slot
failed, len is the size of error messages, messages can be retrieved fromglobal.arg_errors: str[]
.ap_slot_connected(slot_data: json)
called whenapclient_connect_slot
succeededap_items_received(index: int, len: int)
called when receiving items, index is the "item index" of the first item that was received, len is the number of items that were received. Useglobal.arg_ids: int[]
,global.arg_names: str[]
,global.arg_flags: int[]
,global.arg_players: int[]
andglobal.arg_locations: int[]
to retrieve the items.ap_location_info(len: int)
called as result of a scout, len is the number of results (individual scouts). Useglobal.arg_locations: int[]
,global.arg_items: int[]
,global.arg_flags: int[]
andglobal.arg_players: int[]
to get the corresponding location ids and item ids.ap_location_checked(len: int)
called when locations for the connected slot were checked, len is the count. Useglobal.arg_ids: int[]
to retrieve the checked locations ids.ap_print_json(message: json)
passmessage
back in to render_json to get a human readable textap_socket_connected()
network connection established, useful to postpone connection timeouts,room_info
will automatically be received later.ap_socket_disconnected()
network connection died, can't send things until reconnected.ap_socket_error(error: str)
network connection failed. Useful for debugging, but client will retry internally. Either keep a count of errors (abort for >2 failed attempts) or use a timeout forap_room_info
(abort after >5sec).ap_bounced(bounce: json)
called when receiving Bounced from server. The JSON calls can be used to retrieve the data.
These events also make the following JSON data available through the apclient_json_...
calls:
ap_slot_refused
{ "len":int
, "reasons" :str[]
}ap_slot_connected
see "slot_data" of Connected.ap_items_received
{ "index":int
, "len":int
, "ids":int[]
, "names":str[]
, "flags":int[]
, "players":int[]
, "locations":int[]
}ap_location_info
{ "len":int
, "items":int[]
, "flags":int[]
, "players":int[]
, "locations":int[]
}ap_location_checked
{ "len":int
, "locations":int[]
}ap_print_json
see PrintJSON.ap_socket_error
{ "message":str
}ap_bounced
see Bounced.
Additionally, these built-in GM functions may be called by an apclient_poll
script:
show_message(message: str): none
when an exception occurs.
If this happens, the following JSON data is made available:
show_message
{ "message":str
}
The following are not implemented.
ap_retrieved(...)
result forapclient_get
ap_set_reply(...)
when a player sent a Set that matches your SetNotify
Initial version.
Bounce implemented through the following calls:
apclient_bounce
apclient_death_link
apclient_set_bounce_targets
The ap_bounced
event is now supported.