diff --git a/docs/_Sidebar.md b/docs/_Sidebar.md
index 0c549799..a56a9297 100644
--- a/docs/_Sidebar.md
+++ b/docs/_Sidebar.md
@@ -25,4 +25,5 @@
* ${module.timeline}
* ${module.networking_messages}
* ${module.networking_sockets}
+ * ${module.dedicated_servers}
* ${module.utilities}
diff --git a/docs/dedicated_servers.js b/docs/dedicated_servers.js
new file mode 100644
index 00000000..a47cf45e
--- /dev/null
+++ b/docs/dedicated_servers.js
@@ -0,0 +1,631 @@
+// FUNCTIONS
+
+/**
+ * @func steam_gameserver_init
+ * @desc > **Steam Function**: [SteamGameServer_Init](https://partner.steamgames.com/doc/api/steam_gameserver#SteamGameServer_Init)
+ *
+ * This function initializes the Steam GameServer API. Set the extension's **Initialization Mode** option to `Dedicated Server` before using this in a dedicated server build so the extension skips client Steam API startup.
+ *
+ * After this function succeeds, set required server fields such as ${function.steam_gameserver_set_product}, ${function.steam_gameserver_set_game_description}, ${function.steam_gameserver_set_mod_dir} and ${function.steam_gameserver_set_dedicated_server}, then call ${function.steam_gameserver_log_on_anonymous} or ${function.steam_gameserver_log_on}.
+ *
+ * @param {struct.GameServerInitConfig} config The initialization settings
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_initialised
+ * @desc This function returns whether the Steam GameServer API has been initialized by ${function.steam_gameserver_init}.
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_update
+ * @desc > **Steam Function**: [SteamGameServer_RunCallbacks](https://partner.steamgames.com/doc/api/steam_gameserver#SteamGameServer_RunCallbacks)
+ *
+ * This function dispatches Steam GameServer callbacks. Call it frequently while the dedicated server is running. The shared ${function.steam_update} function also dispatches GameServer callbacks after the GameServer API is initialized.
+ *
+ * @returns {bool}
+ *
+ * @event steam
+ * @desc Triggered when the game server connects to Steam.
+ * @member {string} event_type The string value `"steam_gameserver_servers_connected"`
+ * @event_end
+ *
+ * @event steam
+ * @desc Triggered when the game server fails to connect to Steam.
+ * @member {string} event_type The string value `"steam_gameserver_servers_connect_failure"`
+ * @member {real} result The Steam [EResult](https://partner.steamgames.com/doc/api/steam_api#EResult) code
+ * @member {bool} still_retrying Whether Steam is still retrying the connection
+ * @event_end
+ *
+ * @event steam
+ * @desc Triggered when the game server disconnects from Steam.
+ * @member {string} event_type The string value `"steam_gameserver_servers_disconnected"`
+ * @member {real} result The Steam [EResult](https://partner.steamgames.com/doc/api/steam_api#EResult) code
+ * @event_end
+ *
+ * @event steam
+ * @desc Triggered when Steam responds with the server's secure policy.
+ * @member {string} event_type The string value `"steam_gameserver_policy_response"`
+ * @member {bool} secure Whether the game server is VAC secure
+ * @event_end
+ *
+ * @event steam
+ * @desc Triggered when a game server auth session ticket is validated.
+ * @member {string} event_type The string value `"steam_gameserver_validate_auth_ticket_response"`
+ * @member {bool} success Whether the auth response was OK
+ * @member {real} response The Steam auth session response. You can use the `STEAMWORKS_AUTH_SESSION_RESPONSE` enum.
+ * @member {int64} steam_id The user SteamID
+ * @member {int64} owner_steam_id The owner SteamID
+ * @event_end
+ *
+ * @event steam
+ * @desc Triggered when a player is approved by the game server auth system.
+ * @member {string} event_type The string value `"steam_gameserver_client_approve"`
+ * @member {int64} steam_id The user SteamID
+ * @member {int64} owner_steam_id The owner SteamID
+ * @event_end
+ *
+ * @event steam
+ * @desc Triggered when a player is denied by the game server auth system.
+ * @member {string} event_type The string value `"steam_gameserver_client_deny"`
+ * @member {int64} steam_id The user SteamID
+ * @member {real} deny_reason The deny reason
+ * @member {string} optional_text Optional diagnostic text
+ * @event_end
+ *
+ * @event steam
+ * @desc Triggered when Steam requests that the game server kick a player.
+ * @member {string} event_type The string value `"steam_gameserver_client_kick"`
+ * @member {int64} steam_id The user SteamID
+ * @member {real} deny_reason The deny reason
+ * @event_end
+ *
+ * @event steam
+ * @desc Triggered when ${function.steam_gameserver_request_user_group_status} completes.
+ * @member {string} event_type The string value `"steam_gameserver_client_group_status"`
+ * @member {int64} steam_id The user SteamID
+ * @member {int64} group_steam_id The group SteamID
+ * @member {bool} member Whether the user is a member
+ * @member {bool} officer Whether the user is an officer
+ * @event_end
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_shutdown
+ * @desc > **Steam Function**: [SteamGameServer_Shutdown](https://partner.steamgames.com/doc/api/steam_gameserver#SteamGameServer_Shutdown)
+ *
+ * This function shuts down the Steam GameServer API.
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_product
+ * @desc > **Steam Function**: [ISteamGameServer::SetProduct](https://partner.steamgames.com/doc/api/ISteamGameServer#SetProduct)
+ *
+ * This function sets the game product identifier. Set it before logging on.
+ *
+ * @param {string} product The product identifier
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_game_description
+ * @desc > **Steam Function**: [ISteamGameServer::SetGameDescription](https://partner.steamgames.com/doc/api/ISteamGameServer#SetGameDescription)
+ *
+ * This function sets the game description displayed in the Steam server browser. Set it before logging on.
+ *
+ * @param {string} description The game description
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_mod_dir
+ * @desc > **Steam Function**: [ISteamGameServer::SetModDir](https://partner.steamgames.com/doc/api/ISteamGameServer#SetModDir)
+ *
+ * This function sets the mod directory identifier. For a non-mod game, pass an empty string.
+ *
+ * @param {string} mod_dir The mod directory identifier
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_dedicated_server
+ * @desc > **Steam Function**: [ISteamGameServer::SetDedicatedServer](https://partner.steamgames.com/doc/api/ISteamGameServer#SetDedicatedServer)
+ *
+ * This function sets whether the server is a dedicated server. Set it before logging on.
+ *
+ * @param {bool} dedicated Whether this is a dedicated server
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_log_on
+ * @desc > **Steam Function**: [ISteamGameServer::LogOn](https://partner.steamgames.com/doc/api/ISteamGameServer#LogOn)
+ *
+ * This function logs on using a persistent game server login token.
+ *
+ * @param {string} token The game server login token
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_log_on_anonymous
+ * @desc > **Steam Function**: [ISteamGameServer::LogOnAnonymous](https://partner.steamgames.com/doc/api/ISteamGameServer#LogOnAnonymous)
+ *
+ * This function logs on anonymously.
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_log_off
+ * @desc > **Steam Function**: [ISteamGameServer::LogOff](https://partner.steamgames.com/doc/api/ISteamGameServer#LogOff)
+ *
+ * This function logs the game server off Steam.
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_logged_on
+ * @desc > **Steam Function**: [ISteamGameServer::BLoggedOn](https://partner.steamgames.com/doc/api/ISteamGameServer#BLoggedOn)
+ *
+ * This function checks whether the game server is logged on.
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_secure
+ * @desc > **Steam Function**: [ISteamGameServer::BSecure](https://partner.steamgames.com/doc/api/ISteamGameServer#BSecure)
+ *
+ * This function checks whether the game server is VAC secure.
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_get_steam_id
+ * @desc > **Steam Function**: [ISteamGameServer::GetSteamID](https://partner.steamgames.com/doc/api/ISteamGameServer#GetSteamID)
+ *
+ * This function gets the SteamID assigned to the game server.
+ *
+ * @returns {int64}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_was_restart_requested
+ * @desc > **Steam Function**: [ISteamGameServer::WasRestartRequested](https://partner.steamgames.com/doc/api/ISteamGameServer#WasRestartRequested)
+ *
+ * This function checks whether Steam has requested a server restart.
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_max_player_count
+ * @desc > **Steam Function**: [ISteamGameServer::SetMaxPlayerCount](https://partner.steamgames.com/doc/api/ISteamGameServer#SetMaxPlayerCount)
+ *
+ * @param {real} max_players The maximum number of players
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_bot_player_count
+ * @desc > **Steam Function**: [ISteamGameServer::SetBotPlayerCount](https://partner.steamgames.com/doc/api/ISteamGameServer#SetBotPlayerCount)
+ *
+ * @param {real} bot_players The number of bot players
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_server_name
+ * @desc > **Steam Function**: [ISteamGameServer::SetServerName](https://partner.steamgames.com/doc/api/ISteamGameServer#SetServerName)
+ *
+ * @param {string} name The server name
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_map_name
+ * @desc > **Steam Function**: [ISteamGameServer::SetMapName](https://partner.steamgames.com/doc/api/ISteamGameServer#SetMapName)
+ *
+ * @param {string} map_name The map name
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_password_protected
+ * @desc > **Steam Function**: [ISteamGameServer::SetPasswordProtected](https://partner.steamgames.com/doc/api/ISteamGameServer#SetPasswordProtected)
+ *
+ * @param {bool} password_protected Whether the server requires a password
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_spectator_port
+ * @desc > **Steam Function**: [ISteamGameServer::SetSpectatorPort](https://partner.steamgames.com/doc/api/ISteamGameServer#SetSpectatorPort)
+ *
+ * @param {real} port The spectator port
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_spectator_server_name
+ * @desc > **Steam Function**: [ISteamGameServer::SetSpectatorServerName](https://partner.steamgames.com/doc/api/ISteamGameServer#SetSpectatorServerName)
+ *
+ * @param {string} name The spectator server name
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_clear_all_key_values
+ * @desc > **Steam Function**: [ISteamGameServer::ClearAllKeyValues](https://partner.steamgames.com/doc/api/ISteamGameServer#ClearAllKeyValues)
+ *
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_key_value
+ * @desc > **Steam Function**: [ISteamGameServer::SetKeyValue](https://partner.steamgames.com/doc/api/ISteamGameServer#SetKeyValue)
+ *
+ * @param {string} key The rule key
+ * @param {string} value The rule value
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_game_tags
+ * @desc > **Steam Function**: [ISteamGameServer::SetGameTags](https://partner.steamgames.com/doc/api/ISteamGameServer#SetGameTags)
+ *
+ * @param {string} tags The server tags string
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_game_data
+ * @desc > **Steam Function**: [ISteamGameServer::SetGameData](https://partner.steamgames.com/doc/api/ISteamGameServer#SetGameData)
+ *
+ * @param {string} game_data The game data string
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_region
+ * @desc > **Steam Function**: [ISteamGameServer::SetRegion](https://partner.steamgames.com/doc/api/ISteamGameServer#SetRegion)
+ *
+ * @param {string} region The region identifier
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_set_advertise_server_active
+ * @desc > **Steam Function**: [ISteamGameServer::SetAdvertiseServerActive](https://partner.steamgames.com/doc/api/ISteamGameServer#SetAdvertiseServerActive)
+ *
+ * This function controls whether the game server is advertised to master servers and responds to server browser queries.
+ *
+ * @param {bool} active Whether the server should advertise itself
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_get_public_ip
+ * @desc > **Steam Function**: [ISteamGameServer::GetPublicIP](https://partner.steamgames.com/doc/api/ISteamGameServer#GetPublicIP)
+ *
+ * This function gets the public IP address Steam sees for the game server.
+ *
+ * @returns {struct.SteamGameServerPublicIP}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_handle_incoming_packet
+ * @desc > **Steam Function**: [ISteamGameServer::HandleIncomingPacket](https://partner.steamgames.com/doc/api/ISteamGameServer#HandleIncomingPacket)
+ *
+ * This function passes a master server query packet to Steam when using shared query port mode.
+ *
+ * @param {buffer} buffer The buffer containing the packet
+ * @param {real} size The packet size
+ * @param {real} src_ip The source IP in host order
+ * @param {real} src_port The source port
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_get_next_outgoing_packet
+ * @desc > **Steam Function**: [ISteamGameServer::GetNextOutgoingPacket](https://partner.steamgames.com/doc/api/ISteamGameServer#GetNextOutgoingPacket)
+ *
+ * This function gets the next outgoing master server packet when using shared query port mode. Call this repeatedly until the returned `size` is 0.
+ *
+ * @param {buffer} buffer The buffer to write the packet to
+ * @param {real} [max_size] The maximum packet size, defaults to 16 KiB
+ * @returns {struct.SteamGameServerOutgoingPacket}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_get_auth_session_ticket
+ * @desc > **Steam Function**: [ISteamGameServer::GetAuthSessionTicket](https://partner.steamgames.com/doc/api/ISteamGameServer#GetAuthSessionTicket)
+ *
+ * This function creates an auth session ticket for the game server.
+ *
+ * @returns {struct.SteamGameServerAuthTicket}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_begin_auth_session
+ * @desc > **Steam Function**: [ISteamGameServer::BeginAuthSession](https://partner.steamgames.com/doc/api/ISteamGameServer#BeginAuthSession)
+ *
+ * This function begins validating an auth session ticket received from a player.
+ *
+ * @param {buffer} buffer The buffer holding the ticket
+ * @param {real} ticket_size The ticket size in bytes
+ * @param {int64} steam_id The SteamID that sent the ticket
+ * @returns {real} A Steam begin auth session result. You can use the `STEAMWORKS_BEGIN_AUTH_SESSION_RESULT` enum.
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_end_auth_session
+ * @desc > **Steam Function**: [ISteamGameServer::EndAuthSession](https://partner.steamgames.com/doc/api/ISteamGameServer#EndAuthSession)
+ *
+ * @param {int64} steam_id The SteamID to stop validating
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_cancel_auth_ticket
+ * @desc > **Steam Function**: [ISteamGameServer::CancelAuthTicket](https://partner.steamgames.com/doc/api/ISteamGameServer#CancelAuthTicket)
+ *
+ * @param {real} auth_ticket_handle The auth ticket handle returned by ${function.steam_gameserver_get_auth_session_ticket}
+ * @returns {bool}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_user_has_license_for_app
+ * @desc > **Steam Function**: [ISteamGameServer::UserHasLicenseForApp](https://partner.steamgames.com/doc/api/ISteamGameServer#UserHasLicenseForApp)
+ *
+ * @param {int64} steam_id The user SteamID
+ * @param {real} app_id The app ID to check
+ * @returns {constant.steam_user_has_license_for_app_result}
+ * @func_end
+ */
+
+/**
+ * @func steam_gameserver_request_user_group_status
+ * @desc > **Steam Function**: [ISteamGameServer::RequestUserGroupStatus](https://partner.steamgames.com/doc/api/ISteamGameServer#RequestUserGroupStatus)
+ *
+ * @param {int64} steam_id The user SteamID
+ * @param {int64} group_steam_id The group SteamID
+ * @returns {bool}
+ * @func_end
+ */
+
+// CONSTANTS
+
+/**
+ * @constant steam_gameserver_mode
+ * @desc > **Steamworks Enumeration**: [EServerMode](https://partner.steamgames.com/doc/api/steam_gameserver#EServerMode)
+ *
+ * [[Note: Alternatively, you can use the `STEAMWORKS_GAMESERVER_MODE` enum.]]
+ *
+ * @member steam_gameserver_mode_invalid Deprecated, do not use
+ * @member steam_gameserver_mode_no_authentication Do not authenticate logins and do not list on the server list
+ * @member steam_gameserver_mode_authentication Authenticate users and list on the server list
+ * @member steam_gameserver_mode_authentication_and_secure Authenticate users, list on the server list and VAC protect clients
+ * @constant_end
+ */
+
+/**
+ * @constant steam_gameserver_query_port
+ * @desc Query port constants for ${function.steam_gameserver_init}.
+ *
+ * [[Note: Alternatively, you can use the `STEAMWORKS_GAMESERVER_QUERY_PORT` enum.]]
+ *
+ * @member steam_gameserver_query_port_shared Use GameSocketShare mode. The value is 65535, equivalent to `(uint16)-1`.
+ * @constant_end
+ */
+
+/**
+ * @constant steam_user_has_license_for_app_result
+ * @desc > **Steamworks Enumeration**: [EUserHasLicenseForAppResult](https://partner.steamgames.com/doc/api/steam_api#EUserHasLicenseForAppResult)
+ *
+ * [[Note: Alternatively, you can use the `STEAMWORKS_USER_HAS_LICENSE_FOR_APP_RESULT` enum.]]
+ *
+ * @member steam_user_has_license_for_app_result_has_license The user has a license
+ * @member steam_user_has_license_for_app_result_does_not_have_license The user does not have a license
+ * @member steam_user_has_license_for_app_result_no_auth The server is not authenticated with Steam
+ * @constant_end
+ */
+
+// STRUCTS
+
+/**
+ * @struct GameServerInitConfig
+ * @desc This struct configures ${function.steam_gameserver_init}.
+ *
+ * @member {real|string} ip The IPv4 address to bind in host order, or a dotted IPv4 string. Use `0` or `"0.0.0.0"` for all local IPv4 addresses.
+ * @member {real} steam_port The local port used to communicate with Steam, defaults to 8766
+ * @member {real} game_port The gameplay port clients connect to, defaults to 27015
+ * @member {real} query_port The server browser query port, defaults to 27016. Use ${constant.steam_gameserver_query_port_shared} for shared query port mode.
+ * @member {constant.steam_gameserver_mode} server_mode The authentication mode, defaults to ${constant.steam_gameserver_mode_authentication}
+ * @member {string} version The server version string, defaults to `"1.0.0.0"`
+ * @struct_end
+ */
+
+/**
+ * @struct SteamGameServerPublicIP
+ * @desc This struct is returned by ${function.steam_gameserver_get_public_ip}.
+ *
+ * @member {bool} is_set Whether Steam reported an IP address
+ * @member {real} type The Steam IP type
+ * @member {real} ipv4 The IPv4 address in host order, or 0
+ * @member {string} ipv4_string The IPv4 address as a dotted string, or an empty string
+ * @struct_end
+ */
+
+/**
+ * @struct SteamGameServerOutgoingPacket
+ * @desc This struct is returned by ${function.steam_gameserver_get_next_outgoing_packet}.
+ *
+ * @member {bool} success Whether a packet was written
+ * @member {real} size The number of bytes written, or 0 if there are no packets
+ * @member {real} ip The destination IP in host order
+ * @member {string} ip_string The destination IP as a dotted string
+ * @member {real} port The destination port
+ * @struct_end
+ */
+
+/**
+ * @struct SteamGameServerAuthTicket
+ * @desc This struct is returned by ${function.steam_gameserver_get_auth_session_ticket}.
+ *
+ * @member {buffer} ticket_buffer The buffer containing the ticket, or -1 if no ticket was created
+ * @member {real} ticket_size The ticket size in bytes
+ * @member {real} auth_ticket_handle The auth ticket handle to pass to ${function.steam_gameserver_cancel_auth_ticket}
+ * @struct_end
+ */
+
+// MODULES
+
+/**
+ * @module dedicated_servers
+ * @title Dedicated Servers
+ * @desc > **Steamworks Interfaces**: [steam_gameserver.h](https://partner.steamgames.com/doc/api/steam_gameserver), [ISteamGameServer](https://partner.steamgames.com/doc/api/ISteamGameServer)
+ *
+ * This module exposes the Steam GameServer API for GameMaker dedicated server builds.
+ *
+ * @section Setup
+ * @desc For dedicated server builds, set the extension option **Initialization Mode** to `Dedicated Server`. This prevents client Steam API initialization and lets your server initialize with ${function.steam_gameserver_init}.
+ * @section_end
+ *
+ * @section_func Lifecycle
+ * @desc These functions initialize, update and shut down the GameServer API:
+ * @ref steam_gameserver_init
+ * @ref steam_gameserver_initialised
+ * @ref steam_gameserver_update
+ * @ref steam_gameserver_shutdown
+ * @section_end
+ *
+ * @section_func Login And Status
+ * @desc These functions log on and query server status:
+ * @ref steam_gameserver_log_on
+ * @ref steam_gameserver_log_on_anonymous
+ * @ref steam_gameserver_log_off
+ * @ref steam_gameserver_logged_on
+ * @ref steam_gameserver_secure
+ * @ref steam_gameserver_get_steam_id
+ * @ref steam_gameserver_was_restart_requested
+ * @section_end
+ *
+ * @section_func Server Configuration
+ * @desc These functions configure server browser data:
+ * @ref steam_gameserver_set_product
+ * @ref steam_gameserver_set_game_description
+ * @ref steam_gameserver_set_mod_dir
+ * @ref steam_gameserver_set_dedicated_server
+ * @ref steam_gameserver_set_max_player_count
+ * @ref steam_gameserver_set_bot_player_count
+ * @ref steam_gameserver_set_server_name
+ * @ref steam_gameserver_set_map_name
+ * @ref steam_gameserver_set_password_protected
+ * @ref steam_gameserver_set_spectator_port
+ * @ref steam_gameserver_set_spectator_server_name
+ * @ref steam_gameserver_clear_all_key_values
+ * @ref steam_gameserver_set_key_value
+ * @ref steam_gameserver_set_game_tags
+ * @ref steam_gameserver_set_game_data
+ * @ref steam_gameserver_set_region
+ * @ref steam_gameserver_set_advertise_server_active
+ * @ref steam_gameserver_get_public_ip
+ * @section_end
+ *
+ * @section_func Shared Query Port
+ * @desc These functions support GameSocketShare mode:
+ * @ref steam_gameserver_handle_incoming_packet
+ * @ref steam_gameserver_get_next_outgoing_packet
+ * @section_end
+ *
+ * @section_func Authentication
+ * @desc These functions handle server auth tickets and license checks:
+ * @ref steam_gameserver_get_auth_session_ticket
+ * @ref steam_gameserver_begin_auth_session
+ * @ref steam_gameserver_end_auth_session
+ * @ref steam_gameserver_cancel_auth_ticket
+ * @ref steam_gameserver_user_has_license_for_app
+ * @ref steam_gameserver_request_user_group_status
+ * @section_end
+ *
+ * @section_const Constants
+ * @desc Constants used by the dedicated server module:
+ * @ref steam_gameserver_mode
+ * @ref steam_gameserver_query_port
+ * @ref steam_user_has_license_for_app_result
+ * @section_end
+ *
+ * @section_struct Structs
+ * @desc Structs used by the dedicated server module:
+ * @ref GameServerInitConfig
+ * @ref SteamGameServerPublicIP
+ * @ref SteamGameServerOutgoingPacket
+ * @ref SteamGameServerAuthTicket
+ * @section_end
+ *
+ * @module_end
+ */
diff --git a/docs/documentation.js b/docs/documentation.js
index af89cddd..f521e7e8 100644
--- a/docs/documentation.js
+++ b/docs/documentation.js
@@ -31,6 +31,7 @@
* @ref module.networking
* @ref module.networking_sockets
* @ref module.networking_messages
+ * @ref module.dedicated_servers
* @ref module.input
* @ref module.music
* @ref module.timeline
diff --git a/docs/guides.md b/docs/guides.md
index 5d2b1583..b699d85f 100644
--- a/docs/guides.md
+++ b/docs/guides.md
@@ -24,7 +24,9 @@ To use the Steam API extension you should follow these steps:
* **Steam SDK**: The path to the folder where you unzipped the downloaded SDK.
* **Application ID**: The Steam App ID for you game (retrieved from the dashboard).
-
+
+ * **Initialization Mode**: Controls which Steam API is initialized automatically. Use `Client` for normal game builds. Use `Dedicated Server` for dedicated server builds that call [steam_gameserver_init](dedicated_servers#steam_gameserver_init).
+
* **Debug**: This is a flag option that allows you to force debug mode or set it to automatic. Debug mode will allow exports of your game to run a non-steam version of your game (warning, this should be only used during developement). We suggest leaving this set to `Auto`.
* **Build Options**
@@ -57,7 +59,11 @@ To use the Steam API extension you should follow these steps:
* [steam_upload_score_buffer_ext](leaderboards#steam_upload_score_buffer_ext)
* [steam_ugc_delete_item](ugc#steam_ugc_delete_item)
+## Dedicated Servers
+
+ Dedicated server builds should set **Initialization Mode** to `Dedicated Server`, then initialize Steam with [steam_gameserver_init](dedicated_servers#steam_gameserver_init). Call [steam_gameserver_update](dedicated_servers#steam_gameserver_update) or the shared [steam_update](management#steam_update) regularly to dispatch GameServer callbacks, and call [steam_gameserver_shutdown](dedicated_servers#steam_gameserver_shutdown) before the process exits.
+
----
\ No newline at end of file
+---
diff --git a/docs/management.js b/docs/management.js
index 6f63f32f..808f53dd 100644
--- a/docs/management.js
+++ b/docs/management.js
@@ -16,7 +16,7 @@
/**
* @func steam_update
- * @desc This function updates the Steam APIs.
+ * @desc This function updates the Steam APIs. If the GameServer API has been initialized, it also dispatches Steam GameServer callbacks.
*
* [[WARNING: This function is required to be called in order for the Steamworks extension to work. Certain async events are only triggered when you call this function. We recommend you place this function in a persistent controller object that calls it inside its ${event.step}.]]
*
@@ -80,4 +80,3 @@
* @section_end
* @module_end
*/
-
\ No newline at end of file
diff --git a/docs/networking_sockets.js b/docs/networking_sockets.js
index 0cbd2c11..c7883219 100644
--- a/docs/networking_sockets.js
+++ b/docs/networking_sockets.js
@@ -3,7 +3,7 @@
/**
* @func steam_net_sockets_create_listen_socket_ip
* @desc > **Steam Function**: [ISteamnetworkingSockets::CreateListenSocketIP](https://partner.steamgames.com/doc/api/ISteamnetworkingSockets#CreateListenSocketIP)
- *
+ *
* This function creates a "server" socket that listens for clients to connect to by calling ${function.steam_net_sockets_connect_by_ip}, over ordinary UDP (IPv4 or IPv6). You must select a specific local port to listen on and set it as the port field of the local address.
*
* @param {string} ip The local IP address
@@ -454,6 +454,111 @@
* @func_end
*/
+/**
+ * @func steam_net_sockets_received_relay_auth_ticket
+ * @desc > **Steam Function**: [ISteamNetworkingSockets::ReceivedRelayAuthTicket](https://partner.steamgames.com/doc/api/ISteamNetworkingSockets#ReceivedRelayAuthTicket)
+ *
+ * This function provides a relay auth ticket received from your game coordinator to the Steam Networking Sockets client interface.
+ *
+ * @param {buffer} ticket_buffer The buffer holding the relay auth ticket
+ * @param {real} ticket_size The ticket size in bytes
+ *
+ * @returns {bool}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_net_sockets_find_relay_auth_ticket_for_server
+ * @desc > **Steam Function**: [ISteamNetworkingSockets::FindRelayAuthTicketForServer](https://partner.steamgames.com/doc/api/ISteamNetworkingSockets#FindRelayAuthTicketForServer)
+ *
+ * This function checks whether a relay auth ticket is available for the requested hosted dedicated server.
+ *
+ * @param {int64} steam_id The hosted dedicated server SteamID
+ * @param {real} remote_virtual_port The remote virtual port
+ *
+ * @returns {real} The number of seconds until the ticket expires, 0 if no ticket is available, or a negative value on error
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_net_sockets_connect_to_hosted_dedicated_server
+ * @desc > **Steam Function**: [ISteamNetworkingSockets::ConnectToHostedDedicatedServer](https://partner.steamgames.com/doc/api/ISteamNetworkingSockets#ConnectToHostedDedicatedServer)
+ *
+ * This function begins connecting to a hosted dedicated server through Steam Datagram Relay.
+ *
+ * @param {int64} steam_id The hosted dedicated server SteamID
+ * @param {real} remote_virtual_port The remote virtual port
+ *
+ * @returns {real} Connection handle (>= 0) or -1
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_net_sockets_get_hosted_dedicated_server_port
+ * @desc > **Steam Function**: [ISteamNetworkingSockets::GetHostedDedicatedServerPort](https://partner.steamgames.com/doc/api/ISteamNetworkingSockets#GetHostedDedicatedServerPort)
+ *
+ * This function gets the physical port used by the hosted dedicated server Steam Networking Sockets interface.
+ *
+ * @returns {real}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_net_sockets_get_hosted_dedicated_server_popid
+ * @desc > **Steam Function**: [ISteamNetworkingSockets::GetHostedDedicatedServerPOPID](https://partner.steamgames.com/doc/api/ISteamNetworkingSockets#GetHostedDedicatedServerPOPID)
+ *
+ * This function gets the data center ID selected for the hosted dedicated server Steam Networking Sockets interface.
+ *
+ * @returns {real}
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_net_sockets_get_hosted_dedicated_server_address
+ * @desc > **Steam Function**: [ISteamNetworkingSockets::GetHostedDedicatedServerAddress](https://partner.steamgames.com/doc/api/ISteamNetworkingSockets#GetHostedDedicatedServerAddress)
+ *
+ * This function writes the hosted dedicated server routing address to a buffer.
+ *
+ * @param {buffer} routing_buffer The buffer to receive the SteamDatagramHostedAddress data
+ *
+ * @returns {real} A Steam [EResult](https://partner.steamgames.com/doc/api/steam_api#EResult) code
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_net_sockets_create_hosted_dedicated_server_listen_socket
+ * @desc > **Steam Function**: [ISteamNetworkingSockets::CreateHostedDedicatedServerListenSocket](https://partner.steamgames.com/doc/api/ISteamNetworkingSockets#CreateHostedDedicatedServerListenSocket)
+ *
+ * This function creates a listen socket for a hosted dedicated server using Steam Datagram Relay.
+ *
+ * @param {real} local_virtual_port The local virtual port
+ *
+ * @returns {real} Listen socket handle (>= 0) or -1
+ *
+ * @func_end
+ */
+
+/**
+ * @func steam_net_sockets_get_game_coordinator_server_login
+ * @desc > **Steam Function**: [ISteamNetworkingSockets::GetGameCoordinatorServerLogin](https://partner.steamgames.com/doc/api/ISteamNetworkingSockets#GetGameCoordinatorServerLogin)
+ *
+ * This function writes game coordinator login data for a hosted dedicated server.
+ *
+ * @param {buffer} login_buffer The buffer holding SteamDatagramGameCoordinatorServerLogin data
+ * @param {buffer} blob_buffer The buffer to receive the signed blob
+ * @param {real} blob_max_size The maximum signed blob size to write
+ *
+ * @returns {struct.GameCoordinatorServerLoginResult}
+ *
+ * @func_end
+ */
+
// CONSTANTS
/**
@@ -612,6 +717,15 @@
* @struct_end
*/
+/**
+ * @struct GameCoordinatorServerLoginResult
+ * @desc This struct is returned by ${function.steam_net_sockets_get_game_coordinator_server_login}.
+ *
+ * @member {real} result The Steam [EResult](https://partner.steamgames.com/doc/api/steam_api#EResult) code
+ * @member {real} blob_size The signed blob size in bytes
+ * @struct_end
+ */
+
// MODULES
/**
@@ -653,7 +767,7 @@
* The extension will trigger a ${event.steam} of `event_type` `"steam_net_message_on_state_change"` for callbacks of this type.
*
* @section_end
- *
+ *
* @section_func Basic Sockets
* @desc These are the basic functions for working with sockets:
* @ref steam_net_sockets_create_listen_socket_ip
@@ -664,7 +778,7 @@
* @ref steam_net_sockets_close_connection
* @ref steam_net_sockets_close_listen_socket
* @section_end
- *
+ *
* @section_func Socket Pair (Local Connection)
* @desc These functions allow working with socket pairs:
* @ref steam_net_sockets_create_socket_pair
@@ -714,6 +828,18 @@
* @ref steam_net_sockets_get_authentication_status
* @section_end
*
+ * @section_func Hosted Dedicated Server SDR
+ * @desc These functions are used by hosted dedicated servers and clients that connect to them through Steam Datagram Relay:
+ * @ref steam_net_sockets_received_relay_auth_ticket
+ * @ref steam_net_sockets_find_relay_auth_ticket_for_server
+ * @ref steam_net_sockets_connect_to_hosted_dedicated_server
+ * @ref steam_net_sockets_get_hosted_dedicated_server_port
+ * @ref steam_net_sockets_get_hosted_dedicated_server_popid
+ * @ref steam_net_sockets_get_hosted_dedicated_server_address
+ * @ref steam_net_sockets_create_hosted_dedicated_server_listen_socket
+ * @ref steam_net_sockets_get_game_coordinator_server_login
+ * @section_end
+ *
* @section_const Constants
* @desc These are the constants used by the Steam extension's networking sockets and messages modules:
* @ref steam_net_fake_ip_type
@@ -728,7 +854,8 @@
* @ref Identity
* @ref ConnectionInfo
* @ref ConnectionRealTimeStatus
+ * @ref GameCoordinatorServerLoginResult
* @section_end
*
* @module_end
- */
\ No newline at end of file
+ */
diff --git a/source/Steamworks_gml/extensions/steamworks/Steamworks.yy b/source/Steamworks_gml/extensions/steamworks/Steamworks.yy
index 6723555b..9d01c8d2 100644
--- a/source/Steamworks_gml/extensions/steamworks/Steamworks.yy
+++ b/source/Steamworks_gml/extensions/steamworks/Steamworks.yy
@@ -125,6 +125,14 @@
{"$GMExtensionConstant":"","%Name":"steam_net_packet_type_unreliable_nodelay","hidden":false,"name":"steam_net_packet_type_unreliable_nodelay","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"1",},
{"$GMExtensionConstant":"","%Name":"steam_net_packet_type_reliable","hidden":false,"name":"steam_net_packet_type_reliable","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"2",},
{"$GMExtensionConstant":"","%Name":"steam_net_packet_type_reliable_buffer","hidden":false,"name":"steam_net_packet_type_reliable_buffer","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"3",},
+ {"$GMExtensionConstant":"","%Name":"steam_gameserver_mode_invalid","hidden":false,"name":"steam_gameserver_mode_invalid","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"0",},
+ {"$GMExtensionConstant":"","%Name":"steam_gameserver_mode_no_authentication","hidden":false,"name":"steam_gameserver_mode_no_authentication","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"1",},
+ {"$GMExtensionConstant":"","%Name":"steam_gameserver_mode_authentication","hidden":false,"name":"steam_gameserver_mode_authentication","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"2",},
+ {"$GMExtensionConstant":"","%Name":"steam_gameserver_mode_authentication_and_secure","hidden":false,"name":"steam_gameserver_mode_authentication_and_secure","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"3",},
+ {"$GMExtensionConstant":"","%Name":"steam_gameserver_query_port_shared","hidden":false,"name":"steam_gameserver_query_port_shared","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"65535",},
+ {"$GMExtensionConstant":"","%Name":"steam_user_has_license_for_app_result_has_license","hidden":false,"name":"steam_user_has_license_for_app_result_has_license","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"0",},
+ {"$GMExtensionConstant":"","%Name":"steam_user_has_license_for_app_result_does_not_have_license","hidden":false,"name":"steam_user_has_license_for_app_result_does_not_have_license","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"1",},
+ {"$GMExtensionConstant":"","%Name":"steam_user_has_license_for_app_result_no_auth","hidden":false,"name":"steam_user_has_license_for_app_result_no_auth","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"2",},
{"$GMExtensionConstant":"","%Name":"steam_user_avatar_size_small","hidden":false,"name":"steam_user_avatar_size_small","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"0",},
{"$GMExtensionConstant":"","%Name":"steam_user_avatar_size_medium","hidden":false,"name":"steam_user_avatar_size_medium","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"1",},
{"$GMExtensionConstant":"","%Name":"steam_user_avatar_size_large","hidden":false,"name":"steam_user_avatar_size_large","resourceType":"GMExtensionConstant","resourceVersion":"2.0","value":"2",},
@@ -442,6 +450,43 @@
{"$GMExtensionFunction":"","%Name":"steam_initialised","argCount":1,"args":[],"documentation":"/// @returns {Bool}","externalName":"steam_initialised","help":"steam_initialised()","hidden":false,"kind":11,"name":"steam_initialised","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
{"$GMExtensionFunction":"","%Name":"steam_shutdown","argCount":0,"args":[],"documentation":"/// @returns {Undefined}","externalName":"steam_shutdown","help":"steam_shutdown()","hidden":false,"kind":1,"name":"steam_shutdown","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
{"$GMExtensionFunction":"","%Name":"steam_is_subscribed","argCount":0,"args":[],"documentation":"/// @returns {Bool}","externalName":"steam_is_subscribed","help":"steam_is_subscribed()","hidden":false,"kind":1,"name":"steam_is_subscribed","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_init","argCount":0,"args":[2,],"documentation":"","externalName":"steam_gameserver_init","help":"steam_gameserver_init(config)","hidden":false,"kind":4,"name":"steam_gameserver_init","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_initialised","argCount":0,"args":[],"documentation":"","externalName":"steam_gameserver_initialised","help":"steam_gameserver_initialised()","hidden":false,"kind":4,"name":"steam_gameserver_initialised","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_update","argCount":0,"args":[],"documentation":"","externalName":"steam_gameserver_update","help":"steam_gameserver_update()","hidden":false,"kind":4,"name":"steam_gameserver_update","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_shutdown","argCount":0,"args":[],"documentation":"","externalName":"steam_gameserver_shutdown","help":"steam_gameserver_shutdown()","hidden":false,"kind":4,"name":"steam_gameserver_shutdown","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_product","argCount":0,"args":[1,],"documentation":"","externalName":"steam_gameserver_set_product","help":"steam_gameserver_set_product(product)","hidden":false,"kind":4,"name":"steam_gameserver_set_product","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_game_description","argCount":0,"args":[1,],"documentation":"","externalName":"steam_gameserver_set_game_description","help":"steam_gameserver_set_game_description(description)","hidden":false,"kind":4,"name":"steam_gameserver_set_game_description","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_mod_dir","argCount":0,"args":[1,],"documentation":"","externalName":"steam_gameserver_set_mod_dir","help":"steam_gameserver_set_mod_dir(mod_dir)","hidden":false,"kind":4,"name":"steam_gameserver_set_mod_dir","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_dedicated_server","argCount":0,"args":[2,],"documentation":"","externalName":"steam_gameserver_set_dedicated_server","help":"steam_gameserver_set_dedicated_server(dedicated)","hidden":false,"kind":4,"name":"steam_gameserver_set_dedicated_server","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_log_on","argCount":0,"args":[1,],"documentation":"","externalName":"steam_gameserver_log_on","help":"steam_gameserver_log_on(token)","hidden":false,"kind":4,"name":"steam_gameserver_log_on","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_log_on_anonymous","argCount":0,"args":[],"documentation":"","externalName":"steam_gameserver_log_on_anonymous","help":"steam_gameserver_log_on_anonymous()","hidden":false,"kind":4,"name":"steam_gameserver_log_on_anonymous","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_log_off","argCount":0,"args":[],"documentation":"","externalName":"steam_gameserver_log_off","help":"steam_gameserver_log_off()","hidden":false,"kind":4,"name":"steam_gameserver_log_off","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_logged_on","argCount":0,"args":[],"documentation":"","externalName":"steam_gameserver_logged_on","help":"steam_gameserver_logged_on()","hidden":false,"kind":4,"name":"steam_gameserver_logged_on","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_secure","argCount":0,"args":[],"documentation":"","externalName":"steam_gameserver_secure","help":"steam_gameserver_secure()","hidden":false,"kind":4,"name":"steam_gameserver_secure","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_get_steam_id","argCount":0,"args":[],"documentation":"","externalName":"steam_gameserver_get_steam_id","help":"steam_gameserver_get_steam_id()","hidden":false,"kind":4,"name":"steam_gameserver_get_steam_id","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_was_restart_requested","argCount":0,"args":[],"documentation":"","externalName":"steam_gameserver_was_restart_requested","help":"steam_gameserver_was_restart_requested()","hidden":false,"kind":4,"name":"steam_gameserver_was_restart_requested","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_max_player_count","argCount":0,"args":[2,],"documentation":"","externalName":"steam_gameserver_set_max_player_count","help":"steam_gameserver_set_max_player_count(max_players)","hidden":false,"kind":4,"name":"steam_gameserver_set_max_player_count","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_bot_player_count","argCount":0,"args":[2,],"documentation":"","externalName":"steam_gameserver_set_bot_player_count","help":"steam_gameserver_set_bot_player_count(bot_players)","hidden":false,"kind":4,"name":"steam_gameserver_set_bot_player_count","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_server_name","argCount":0,"args":[1,],"documentation":"","externalName":"steam_gameserver_set_server_name","help":"steam_gameserver_set_server_name(name)","hidden":false,"kind":4,"name":"steam_gameserver_set_server_name","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_map_name","argCount":0,"args":[1,],"documentation":"","externalName":"steam_gameserver_set_map_name","help":"steam_gameserver_set_map_name(map_name)","hidden":false,"kind":4,"name":"steam_gameserver_set_map_name","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_password_protected","argCount":0,"args":[2,],"documentation":"","externalName":"steam_gameserver_set_password_protected","help":"steam_gameserver_set_password_protected(password_protected)","hidden":false,"kind":4,"name":"steam_gameserver_set_password_protected","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_spectator_port","argCount":0,"args":[2,],"documentation":"","externalName":"steam_gameserver_set_spectator_port","help":"steam_gameserver_set_spectator_port(port)","hidden":false,"kind":4,"name":"steam_gameserver_set_spectator_port","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_spectator_server_name","argCount":0,"args":[1,],"documentation":"","externalName":"steam_gameserver_set_spectator_server_name","help":"steam_gameserver_set_spectator_server_name(name)","hidden":false,"kind":4,"name":"steam_gameserver_set_spectator_server_name","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_clear_all_key_values","argCount":0,"args":[],"documentation":"","externalName":"steam_gameserver_clear_all_key_values","help":"steam_gameserver_clear_all_key_values()","hidden":false,"kind":4,"name":"steam_gameserver_clear_all_key_values","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_key_value","argCount":0,"args":[1,1,],"documentation":"","externalName":"steam_gameserver_set_key_value","help":"steam_gameserver_set_key_value(key,value)","hidden":false,"kind":4,"name":"steam_gameserver_set_key_value","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_game_tags","argCount":0,"args":[1,],"documentation":"","externalName":"steam_gameserver_set_game_tags","help":"steam_gameserver_set_game_tags(tags)","hidden":false,"kind":4,"name":"steam_gameserver_set_game_tags","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_game_data","argCount":0,"args":[1,],"documentation":"","externalName":"steam_gameserver_set_game_data","help":"steam_gameserver_set_game_data(game_data)","hidden":false,"kind":4,"name":"steam_gameserver_set_game_data","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_region","argCount":0,"args":[1,],"documentation":"","externalName":"steam_gameserver_set_region","help":"steam_gameserver_set_region(region)","hidden":false,"kind":4,"name":"steam_gameserver_set_region","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_set_advertise_server_active","argCount":0,"args":[2,],"documentation":"","externalName":"steam_gameserver_set_advertise_server_active","help":"steam_gameserver_set_advertise_server_active(active)","hidden":false,"kind":4,"name":"steam_gameserver_set_advertise_server_active","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_get_public_ip","argCount":0,"args":[],"documentation":"","externalName":"steam_gameserver_get_public_ip","help":"steam_gameserver_get_public_ip()","hidden":false,"kind":4,"name":"steam_gameserver_get_public_ip","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_handle_incoming_packet","argCount":0,"args":[2,2,2,2,],"documentation":"","externalName":"steam_gameserver_handle_incoming_packet","help":"steam_gameserver_handle_incoming_packet(buffer,size,src_ip,src_port)","hidden":false,"kind":4,"name":"steam_gameserver_handle_incoming_packet","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_get_next_outgoing_packet","argCount":0,"args":[2,2,],"documentation":"","externalName":"steam_gameserver_get_next_outgoing_packet","help":"steam_gameserver_get_next_outgoing_packet(buffer,max_size)","hidden":false,"kind":4,"name":"steam_gameserver_get_next_outgoing_packet","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_get_auth_session_ticket","argCount":0,"args":[],"documentation":"","externalName":"steam_gameserver_get_auth_session_ticket","help":"steam_gameserver_get_auth_session_ticket()","hidden":false,"kind":4,"name":"steam_gameserver_get_auth_session_ticket","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_begin_auth_session","argCount":0,"args":[2,2,2,],"documentation":"","externalName":"steam_gameserver_begin_auth_session","help":"steam_gameserver_begin_auth_session(buffer,ticket_size,steam_id)","hidden":false,"kind":4,"name":"steam_gameserver_begin_auth_session","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_end_auth_session","argCount":0,"args":[2,],"documentation":"","externalName":"steam_gameserver_end_auth_session","help":"steam_gameserver_end_auth_session(steam_id)","hidden":false,"kind":4,"name":"steam_gameserver_end_auth_session","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_cancel_auth_ticket","argCount":0,"args":[2,],"documentation":"","externalName":"steam_gameserver_cancel_auth_ticket","help":"steam_gameserver_cancel_auth_ticket(auth_ticket_handle)","hidden":false,"kind":4,"name":"steam_gameserver_cancel_auth_ticket","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_user_has_license_for_app","argCount":0,"args":[2,2,],"documentation":"","externalName":"steam_gameserver_user_has_license_for_app","help":"steam_gameserver_user_has_license_for_app(steam_id,app_id)","hidden":false,"kind":4,"name":"steam_gameserver_user_has_license_for_app","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_gameserver_request_user_group_status","argCount":0,"args":[2,2,],"documentation":"","externalName":"steam_gameserver_request_user_group_status","help":"steam_gameserver_request_user_group_status(steam_id,group_steam_id)","hidden":false,"kind":4,"name":"steam_gameserver_request_user_group_status","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
{"$GMExtensionFunction":"","%Name":"steam_set_warning_message_hook","argCount":0,"args":[],"documentation":"/// @returns {Undefined}","externalName":"steam_set_warning_message_hook","help":"steam_set_warning_message_hook()","hidden":false,"kind":1,"name":"steam_set_warning_message_hook","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
{"$GMExtensionFunction":"","%Name":"steam_lobby_send_chat_message","argCount":0,"args":[1,],"documentation":"/// @param {String} text The string to be sent (up to 4000 characters)\n/// @returns {Bool}","externalName":"steam_lobby_send_chat_message","help":"steam_lobby_send_chat_message(text)","hidden":false,"kind":4,"name":"steam_lobby_send_chat_message","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
{"$GMExtensionFunction":"","%Name":"steam_lobby_send_chat_message_buffer","argCount":0,"args":[2,2,],"documentation":"/// @param {Id.Buffer} buffer The buffer to be sent (up to 4 Kilobytes in size)\n/// @param {Real} size The amount of byte to be sent (there is no offset).\n/// @returns {Bool}","externalName":"steam_lobby_send_chat_message_buffer","help":"steam_lobby_send_chat_message_buffer(buf, size)","hidden":false,"kind":4,"name":"steam_lobby_send_chat_message_buffer","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
@@ -678,14 +723,14 @@
{"$GMExtensionFunction":"","%Name":"steam_net_sockets_get_listen_socket_address","argCount":0,"args":[1,1,],"documentation":"","externalName":"steam_net_sockets_get_listen_socket_address","help":"steam_net_sockets_get_listen_socket_address(listen_socket,buff)","hidden":false,"kind":4,"name":"steam_net_sockets_get_listen_socket_address","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
{"$GMExtensionFunction":"","%Name":"steam_net_sockets_init_authentication","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_init_authentication","help":"steam_net_sockets_init_authentication()","hidden":false,"kind":4,"name":"steam_net_sockets_init_authentication","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
{"$GMExtensionFunction":"","%Name":"steam_net_sockets_get_authentication_status","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_get_authentication_status","help":"steam_net_sockets_get_authentication_status()","hidden":false,"kind":4,"name":"steam_net_sockets_get_authentication_status","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
- {"$GMExtensionFunction":"","%Name":"steam_net_sockets_received_relay_auth_ticket","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_received_relay_auth_ticket","help":"steam_net_sockets_received_relay_auth_ticket()","hidden":false,"kind":4,"name":"steam_net_sockets_received_relay_auth_ticket","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
- {"$GMExtensionFunction":"","%Name":"steam_net_sockets_find_relay_auth_ticket_for_server","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_find_relay_auth_ticket_for_server","help":"steam_net_sockets_find_relay_auth_ticket_for_server()","hidden":false,"kind":4,"name":"steam_net_sockets_find_relay_auth_ticket_for_server","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
- {"$GMExtensionFunction":"","%Name":"steam_net_sockets_connect_to_hosted_dedicated_server","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_connect_to_hosted_dedicated_server","help":"steam_net_sockets_connect_to_hosted_dedicated_server()","hidden":false,"kind":4,"name":"steam_net_sockets_connect_to_hosted_dedicated_server","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
- {"$GMExtensionFunction":"","%Name":"steam_net_sockets_get_hosted_dedicated_server_port","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_get_hosted_dedicated_server_port","help":"steam_net_sockets_get_hosted_dedicated_server_port()","hidden":false,"kind":4,"name":"steam_net_sockets_get_hosted_dedicated_server_port","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
- {"$GMExtensionFunction":"","%Name":"steam_net_sockets_get_hosted_dedicated_server_popid","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_get_hosted_dedicated_server_popid","help":"steam_net_sockets_get_hosted_dedicated_server_popid()","hidden":false,"kind":4,"name":"steam_net_sockets_get_hosted_dedicated_server_popid","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
- {"$GMExtensionFunction":"","%Name":"steam_net_sockets_get_hosted_dedicated_server_address","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_get_hosted_dedicated_server_address","help":"steam_net_sockets_get_hosted_dedicated_server_address()","hidden":false,"kind":4,"name":"steam_net_sockets_get_hosted_dedicated_server_address","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
- {"$GMExtensionFunction":"","%Name":"steam_net_sockets_create_hosted_dedicated_server_listen_socket","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_create_hosted_dedicated_server_listen_socket","help":"steam_net_sockets_create_hosted_dedicated_server_listen_socket()","hidden":false,"kind":4,"name":"steam_net_sockets_create_hosted_dedicated_server_listen_socket","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
- {"$GMExtensionFunction":"","%Name":"steam_net_sockets_get_game_coordinator_server_login","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_get_game_coordinator_server_login","help":"steam_net_sockets_get_game_coordinator_server_login()","hidden":false,"kind":4,"name":"steam_net_sockets_get_game_coordinator_server_login","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
+ {"$GMExtensionFunction":"","%Name":"steam_net_sockets_received_relay_auth_ticket","argCount":0,"args":[2,2,],"documentation":"","externalName":"steam_net_sockets_received_relay_auth_ticket","help":"steam_net_sockets_received_relay_auth_ticket(ticket_buffer,ticket_size)","hidden":false,"kind":4,"name":"steam_net_sockets_received_relay_auth_ticket","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_net_sockets_find_relay_auth_ticket_for_server","argCount":0,"args":[2,2,],"documentation":"","externalName":"steam_net_sockets_find_relay_auth_ticket_for_server","help":"steam_net_sockets_find_relay_auth_ticket_for_server(steam_id,remote_virtual_port)","hidden":false,"kind":4,"name":"steam_net_sockets_find_relay_auth_ticket_for_server","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_net_sockets_connect_to_hosted_dedicated_server","argCount":0,"args":[2,2,],"documentation":"","externalName":"steam_net_sockets_connect_to_hosted_dedicated_server","help":"steam_net_sockets_connect_to_hosted_dedicated_server(steam_id,remote_virtual_port)","hidden":false,"kind":4,"name":"steam_net_sockets_connect_to_hosted_dedicated_server","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_net_sockets_get_hosted_dedicated_server_port","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_get_hosted_dedicated_server_port","help":"steam_net_sockets_get_hosted_dedicated_server_port()","hidden":false,"kind":4,"name":"steam_net_sockets_get_hosted_dedicated_server_port","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_net_sockets_get_hosted_dedicated_server_popid","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_get_hosted_dedicated_server_popid","help":"steam_net_sockets_get_hosted_dedicated_server_popid()","hidden":false,"kind":4,"name":"steam_net_sockets_get_hosted_dedicated_server_popid","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_net_sockets_get_hosted_dedicated_server_address","argCount":0,"args":[2,],"documentation":"","externalName":"steam_net_sockets_get_hosted_dedicated_server_address","help":"steam_net_sockets_get_hosted_dedicated_server_address(routing_buffer)","hidden":false,"kind":4,"name":"steam_net_sockets_get_hosted_dedicated_server_address","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_net_sockets_create_hosted_dedicated_server_listen_socket","argCount":0,"args":[2,],"documentation":"","externalName":"steam_net_sockets_create_hosted_dedicated_server_listen_socket","help":"steam_net_sockets_create_hosted_dedicated_server_listen_socket(local_virtual_port)","hidden":false,"kind":4,"name":"steam_net_sockets_create_hosted_dedicated_server_listen_socket","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":2,},
+ {"$GMExtensionFunction":"","%Name":"steam_net_sockets_get_game_coordinator_server_login","argCount":0,"args":[2,2,2,],"documentation":"","externalName":"steam_net_sockets_get_game_coordinator_server_login","help":"steam_net_sockets_get_game_coordinator_server_login(login_buffer,blob_buffer,blob_max_size)","hidden":false,"kind":4,"name":"steam_net_sockets_get_game_coordinator_server_login","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
{"$GMExtensionFunction":"","%Name":"steam_net_sockets_begin_async_request_fake_ip","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_begin_async_request_fake_ip","help":"steam_net_sockets_begin_async_request_fake_ip()","hidden":false,"kind":4,"name":"steam_net_sockets_begin_async_request_fake_ip","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
{"$GMExtensionFunction":"","%Name":"steam_net_sockets_get_fake_ip","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_get_fake_ip","help":"steam_net_sockets_get_fake_ip()","hidden":false,"kind":4,"name":"steam_net_sockets_get_fake_ip","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
{"$GMExtensionFunction":"","%Name":"steam_net_sockets_create_listen_socket_p2p_fake_ip","argCount":0,"args":[],"documentation":"","externalName":"steam_net_sockets_create_listen_socket_p2p_fake_ip","help":"steam_net_sockets_create_listen_socket_p2p_fake_ip()","hidden":false,"kind":4,"name":"steam_net_sockets_create_listen_socket_p2p_fake_ip","resourceType":"GMExtensionFunction","resourceVersion":"2.0","returnType":1,},
@@ -739,6 +784,10 @@
{"$GMExtensionOption":"","%Name":"sdkPath","defaultValue":"../Steamworks_sdk","description":"The path to the steam sdk (v1.63)","displayName":"Steam SDK","exportToINI":false,"extensionId":null,"guid":"2d6a1c61-98e7-4289-8eff-d70ef6dac1ff","hidden":false,"listItems":[],"name":"sdkPath","optType":4,"resourceType":"GMExtensionOption","resourceVersion":"2.0",},
{"$GMExtensionOption":"","%Name":"__label2","defaultValue":"APP OPTIONS:","description":"","displayName":"","exportToINI":false,"extensionId":null,"guid":"c37a2e1d-c82f-4d90-9d0a-b4a2b3b3da08","hidden":false,"listItems":[],"name":"__label2","optType":5,"resourceType":"GMExtensionOption","resourceVersion":"2.0",},
{"$GMExtensionOption":"","%Name":"appID","defaultValue":"480","description":"Get this value from your steam dashboard.","displayName":"Application ID","exportToINI":false,"extensionId":null,"guid":"2d512363-a8c0-4025-8b9f-b53e231b025d","hidden":false,"listItems":[],"name":"appID","optType":1,"resourceType":"GMExtensionOption","resourceVersion":"2.0",},
+ {"$GMExtensionOption":"","%Name":"initializationMode","defaultValue":"Client","description":"Client - auto-initializes the Steam client API on startup; Dedicated Server - skips client API startup so steam_gameserver_init() can initialize the Steam GameServer API.","displayName":"Initialization Mode","exportToINI":true,"extensionId":null,"guid":"79b07611-f716-4cd8-90e4-f93f2e6fa18f","hidden":false,"listItems":[
+ "Client",
+ "Dedicated Server",
+ ],"name":"initializationMode","optType":6,"resourceType":"GMExtensionOption","resourceVersion":"2.0",},
{"$GMExtensionOption":"","%Name":"debug","defaultValue":"Auto","description":"Auto - automatically sets debug to false on export;\nEnabled - will always keep debug mode on (USE AT OWN RISK);","displayName":"Debug","exportToINI":true,"extensionId":null,"guid":"96a61e5d-c4ac-4104-b5ac-c8ef936e2609","hidden":false,"listItems":[
"Auto",
"Enabled",
@@ -784,4 +833,4 @@
"tvosProps":false,
"tvosSystemFrameworkEntries":[],
"tvosThirdPartyFrameworkEntries":[],
-}
\ No newline at end of file
+}
diff --git a/source/Steamworks_gml/extensions/steamworks/steamworks_cpp/GMLSteam/steam_common.h b/source/Steamworks_gml/extensions/steamworks/steamworks_cpp/GMLSteam/steam_common.h
index 50f55f1c..0b7adc79 100644
--- a/source/Steamworks_gml/extensions/steamworks/steamworks_cpp/GMLSteam/steam_common.h
+++ b/source/Steamworks_gml/extensions/steamworks/steamworks_cpp/GMLSteam/steam_common.h
@@ -12,6 +12,7 @@
const int EVENT_OTHER_WEB_STEAM = 69;
extern bool steam_is_initialised;
+extern bool steam_gameserver_is_initialised;
extern void Steam_UserStats_Init();
extern void Steam_Friends_Init();
diff --git a/source/Steamworks_gml/extensions/steamworks/steamworks_cpp/GMLSteam/steam_init_new.cpp b/source/Steamworks_gml/extensions/steamworks/steamworks_cpp/GMLSteam/steam_init_new.cpp
index 6a849a3b..5cd7c0ce 100644
--- a/source/Steamworks_gml/extensions/steamworks/steamworks_cpp/GMLSteam/steam_init_new.cpp
+++ b/source/Steamworks_gml/extensions/steamworks/steamworks_cpp/GMLSteam/steam_init_new.cpp
@@ -1,10 +1,13 @@
#include "pch.h"
#include "steam_api.h"
+#include "steam_gameserver.h"
#include "Extension_Interface.h"
#include "YYRValue.h"
#include "steam_common.h"
+#include "gml_glue.h"
+#include
#include
#include
#include
@@ -21,21 +24,240 @@ int getAsyncRequestInd()
YYEXPORT void steam_update(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
{
- if (!steam_is_initialised)
- {
- Result.kind = VALUE_REAL;
- Result.val = 0;
- return;
- }
+ bool updated = false;
- SteamAPI_RunCallbacks();
- Steam_UserStats_Process();
+ if (steam_is_initialised)
+ {
+ SteamAPI_RunCallbacks();
+ Steam_UserStats_Process();
+ updated = true;
+ }
+
+ if (steam_gameserver_is_initialised)
+ {
+ SteamGameServer_RunCallbacks();
+ updated = true;
+ }
Result.kind = VALUE_REAL;
- Result.val = 1;
+ Result.val = updated ? 1 : 0;
}
bool steam_is_initialised = false;
+bool steam_gameserver_is_initialised = false;
+
+static uint32 SteamGameServer_ParseIPv4(const char* ip, uint32 fallback)
+{
+ if (ip == nullptr || ip[0] == '\0')
+ return fallback;
+
+ unsigned int a = 0, b = 0, c = 0, d = 0;
+ char tail = '\0';
+ if (std::sscanf(ip, "%u.%u.%u.%u%c", &a, &b, &c, &d, &tail) == 4 &&
+ a <= 255 && b <= 255 && c <= 255 && d <= 255)
+ {
+ return (a << 24) | (b << 16) | (c << 8) | d;
+ }
+
+ return fallback;
+}
+
+static std::string SteamGameServer_IPv4ToString(uint32 ip)
+{
+ std::ostringstream stream;
+ stream << ((ip >> 24) & 0xff) << "."
+ << ((ip >> 16) & 0xff) << "."
+ << ((ip >> 8) & 0xff) << "."
+ << (ip & 0xff);
+ return stream.str();
+}
+
+static RValue* SteamGameServer_GetStructMember(RValue* config, const char* key)
+{
+ if (!config || KIND_RValue(config) != VALUE_OBJECT)
+ return nullptr;
+ return YYStructGetMember(config, key);
+}
+
+static double SteamGameServer_GetStructReal(RValue* config, const char* key, double fallback)
+{
+ RValue* member = SteamGameServer_GetStructMember(config, key);
+ if (!member || KIND_RValue(member) == VALUE_UNDEFINED)
+ return fallback;
+ return REAL_RValue(member);
+}
+
+static bool SteamGameServer_GetStructBool(RValue* config, const char* key, bool fallback)
+{
+ RValue* member = SteamGameServer_GetStructMember(config, key);
+ if (!member || KIND_RValue(member) == VALUE_UNDEFINED)
+ return fallback;
+ return BOOL_RValue(member);
+}
+
+static const char* SteamGameServer_GetStructString(RValue* config, const char* key, const char* fallback)
+{
+ RValue* member = SteamGameServer_GetStructMember(config, key);
+ if (!member || KIND_RValue(member) != VALUE_STRING)
+ return fallback;
+ return member->GetString();
+}
+
+static uint32 SteamGameServer_GetStructIP(RValue* config, const char* key, uint32 fallback)
+{
+ RValue* member = SteamGameServer_GetStructMember(config, key);
+ if (!member || KIND_RValue(member) == VALUE_UNDEFINED)
+ return fallback;
+ if (KIND_RValue(member) == VALUE_STRING)
+ return SteamGameServer_ParseIPv4(member->GetString(), fallback);
+ return static_cast(REAL_RValue(member));
+}
+
+static uint16 SteamGameServer_GetStructPort(RValue* config, const char* key, int fallback)
+{
+ int value = static_cast(SteamGameServer_GetStructReal(config, key, fallback));
+ return value < 0 ? static_cast(0xffff) : static_cast(value);
+}
+
+static ISteamGameServer* SteamGameServer_GetAPI()
+{
+ if (!steam_gameserver_is_initialised)
+ return nullptr;
+ return SteamGameServer();
+}
+
+static void SteamGameServer_ReturnBool(RValue& Result, bool value)
+{
+ Result.kind = VALUE_BOOL;
+ Result.val = value;
+}
+
+static void SteamGameServer_ReturnReal(RValue& Result, double value)
+{
+ Result.kind = VALUE_REAL;
+ Result.val = value;
+}
+
+class GMGameServerCallbackHandler
+{
+public:
+ GMGameServerCallbackHandler() :
+ m_CallbackSteamServersConnected(this, &GMGameServerCallbackHandler::OnSteamServersConnected),
+ m_CallbackSteamServerConnectFailure(this, &GMGameServerCallbackHandler::OnSteamServerConnectFailure),
+ m_CallbackSteamServersDisconnected(this, &GMGameServerCallbackHandler::OnSteamServersDisconnected),
+ m_CallbackGSPolicyResponse(this, &GMGameServerCallbackHandler::OnGSPolicyResponse),
+ m_CallbackValidateAuthTicketResponse(this, &GMGameServerCallbackHandler::OnValidateAuthTicketResponse),
+ m_CallbackGSClientApprove(this, &GMGameServerCallbackHandler::OnGSClientApprove),
+ m_CallbackGSClientDeny(this, &GMGameServerCallbackHandler::OnGSClientDeny),
+ m_CallbackGSClientKick(this, &GMGameServerCallbackHandler::OnGSClientKick),
+ m_CallbackGSClientGroupStatus(this, &GMGameServerCallbackHandler::OnGSClientGroupStatus)
+ {}
+
+ STEAM_GAMESERVER_CALLBACK(GMGameServerCallbackHandler, OnSteamServersConnected, SteamServersConnected_t, m_CallbackSteamServersConnected);
+ STEAM_GAMESERVER_CALLBACK(GMGameServerCallbackHandler, OnSteamServerConnectFailure, SteamServerConnectFailure_t, m_CallbackSteamServerConnectFailure);
+ STEAM_GAMESERVER_CALLBACK(GMGameServerCallbackHandler, OnSteamServersDisconnected, SteamServersDisconnected_t, m_CallbackSteamServersDisconnected);
+ STEAM_GAMESERVER_CALLBACK(GMGameServerCallbackHandler, OnGSPolicyResponse, GSPolicyResponse_t, m_CallbackGSPolicyResponse);
+ STEAM_GAMESERVER_CALLBACK(GMGameServerCallbackHandler, OnValidateAuthTicketResponse, ValidateAuthTicketResponse_t, m_CallbackValidateAuthTicketResponse);
+ STEAM_GAMESERVER_CALLBACK(GMGameServerCallbackHandler, OnGSClientApprove, GSClientApprove_t, m_CallbackGSClientApprove);
+ STEAM_GAMESERVER_CALLBACK(GMGameServerCallbackHandler, OnGSClientDeny, GSClientDeny_t, m_CallbackGSClientDeny);
+ STEAM_GAMESERVER_CALLBACK(GMGameServerCallbackHandler, OnGSClientKick, GSClientKick_t, m_CallbackGSClientKick);
+ STEAM_GAMESERVER_CALLBACK(GMGameServerCallbackHandler, OnGSClientGroupStatus, GSClientGroupStatus_t, m_CallbackGSClientGroupStatus);
+};
+
+static GMGameServerCallbackHandler g_GameServerCallbacks;
+
+void GMGameServerCallbackHandler::OnSteamServersConnected(SteamServersConnected_t* pInfo)
+{
+ steam_net_event event((char*)"steam_gameserver_servers_connected");
+ event.dispatch();
+}
+
+void GMGameServerCallbackHandler::OnSteamServerConnectFailure(SteamServerConnectFailure_t* pInfo)
+{
+ steam_net_event event((char*)"steam_gameserver_servers_connect_failure");
+ event.set((char*)"result", static_cast(pInfo ? pInfo->m_eResult : k_EResultFail));
+ event.set((char*)"still_retrying", pInfo ? pInfo->m_bStillRetrying : false);
+ event.dispatch();
+}
+
+void GMGameServerCallbackHandler::OnSteamServersDisconnected(SteamServersDisconnected_t* pInfo)
+{
+ steam_net_event event((char*)"steam_gameserver_servers_disconnected");
+ event.set((char*)"result", static_cast(pInfo ? pInfo->m_eResult : k_EResultFail));
+ event.dispatch();
+}
+
+void GMGameServerCallbackHandler::OnGSPolicyResponse(GSPolicyResponse_t* pInfo)
+{
+ steam_net_event event((char*)"steam_gameserver_policy_response");
+ event.set((char*)"secure", pInfo ? (pInfo->m_bSecure != 0) : false);
+ event.dispatch();
+}
+
+void GMGameServerCallbackHandler::OnValidateAuthTicketResponse(ValidateAuthTicketResponse_t* pInfo)
+{
+ steam_net_event event((char*)"steam_gameserver_validate_auth_ticket_response");
+ if (pInfo)
+ {
+ event.set((char*)"success", pInfo->m_eAuthSessionResponse == k_EAuthSessionResponseOK);
+ event.set((char*)"response", static_cast(pInfo->m_eAuthSessionResponse));
+ event.set_steamid_all("steam_id", pInfo->m_SteamID);
+ event.set_steamid_all("owner_steam_id", pInfo->m_OwnerSteamID);
+ }
+ else
+ {
+ event.set((char*)"success", false);
+ event.set((char*)"response", static_cast(k_EAuthSessionResponseAuthTicketInvalid));
+ }
+ event.dispatch();
+}
+
+void GMGameServerCallbackHandler::OnGSClientApprove(GSClientApprove_t* pInfo)
+{
+ steam_net_event event((char*)"steam_gameserver_client_approve");
+ if (pInfo)
+ {
+ event.set_steamid_all("steam_id", pInfo->m_SteamID);
+ event.set_steamid_all("owner_steam_id", pInfo->m_OwnerSteamID);
+ }
+ event.dispatch();
+}
+
+void GMGameServerCallbackHandler::OnGSClientDeny(GSClientDeny_t* pInfo)
+{
+ steam_net_event event((char*)"steam_gameserver_client_deny");
+ if (pInfo)
+ {
+ event.set_steamid_all("steam_id", pInfo->m_SteamID);
+ event.set((char*)"deny_reason", static_cast(pInfo->m_eDenyReason));
+ event.set((char*)"optional_text", pInfo->m_rgchOptionalText);
+ }
+ event.dispatch();
+}
+
+void GMGameServerCallbackHandler::OnGSClientKick(GSClientKick_t* pInfo)
+{
+ steam_net_event event((char*)"steam_gameserver_client_kick");
+ if (pInfo)
+ {
+ event.set_steamid_all("steam_id", pInfo->m_SteamID);
+ event.set((char*)"deny_reason", static_cast(pInfo->m_eDenyReason));
+ }
+ event.dispatch();
+}
+
+void GMGameServerCallbackHandler::OnGSClientGroupStatus(GSClientGroupStatus_t* pInfo)
+{
+ steam_net_event event((char*)"steam_gameserver_client_group_status");
+ if (pInfo)
+ {
+ event.set_steamid_all("steam_id", pInfo->m_SteamIDUser);
+ event.set_steamid_all("group_steam_id", pInfo->m_SteamIDGroup);
+ event.set((char*)"member", pInfo->m_bMember);
+ event.set((char*)"officer", pInfo->m_bOfficer);
+ }
+ event.dispatch();
+}
YYEXPORT void steam_init(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)//(int appid)
{
@@ -52,6 +274,9 @@ YYEXPORT void steam_init(RValue& Result, CInstance* selfinst, CInstance* otherin
void OldPreGraphicsInitialisation()
{
+ const char* initMode = extOptGetString("Steamworks", "initializationMode");
+ bool dedicatedServerMode = initMode && strncmp(initMode, "Dedicated Server", 16) == 0;
+
uint32 AppID = static_cast(extOptGetReal("Steamworks", "appID"));
// a game cannot have an invalid appid
@@ -101,13 +326,19 @@ void OldPreGraphicsInitialisation()
}
// https://partner.steamgames.com/doc/sdk/api#initialization_and_shutdown
- if (SteamAPI_RestartAppIfNecessary(AppID))
+ if (!dedicatedServerMode && SteamAPI_RestartAppIfNecessary(AppID))
{
tracef("RestartAppIfNecessary check failed, the game is not allowed to continue");
exit(0);
return;
- }
- }
+ }
+ }
+
+ if (dedicatedServerMode)
+ {
+ tracef("Steam client auto-init skipped because Initialization Mode is Dedicated Server.");
+ return;
+ }
// will also check if it can determine the app id
SteamErrMsg errMsg;
@@ -213,6 +444,402 @@ YYEXPORT void steam_shutdown(RValue& Result, CInstance* selfinst, CInstance* oth
}
SteamAPI_Shutdown();
+ steam_is_initialised = false;
+}
+
+YYEXPORT void steam_gameserver_init(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ SteamGameServer_ReturnBool(Result, false);
+
+ if (steam_gameserver_is_initialised)
+ {
+ Result.val = true;
+ return;
+ }
+
+ RValue* config = (argc > 0 && arg) ? YYGetStruct(arg, 0) : nullptr;
+ if (!config)
+ {
+ DebugConsoleOutput("steam_gameserver_init() - expected a config struct\n");
+ return;
+ }
+
+ uint32 ip = SteamGameServer_GetStructIP(config, "ip", 0);
+ uint16 steamPort = SteamGameServer_GetStructPort(config, "steam_port", 8766);
+ uint16 gamePort = SteamGameServer_GetStructPort(config, "game_port", 27015);
+ uint16 queryPort = SteamGameServer_GetStructPort(config, "query_port", 27016);
+ EServerMode serverMode = static_cast(static_cast(SteamGameServer_GetStructReal(config, "server_mode", eServerModeAuthentication)));
+ const char* version = SteamGameServer_GetStructString(config, "version", "1.0.0.0");
+
+ bool ok = SteamGameServer_Init(ip, steamPort, gamePort, queryPort, serverMode, version);
+ steam_gameserver_is_initialised = ok;
+ Result.val = ok;
+}
+
+YYEXPORT void steam_gameserver_initialised(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ SteamGameServer_ReturnBool(Result, steam_gameserver_is_initialised);
+}
+
+YYEXPORT void steam_gameserver_update(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ if (!steam_gameserver_is_initialised)
+ {
+ SteamGameServer_ReturnBool(Result, false);
+ return;
+ }
+
+ SteamGameServer_RunCallbacks();
+ SteamGameServer_ReturnBool(Result, true);
+}
+
+YYEXPORT void steam_gameserver_shutdown(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ if (!steam_gameserver_is_initialised)
+ {
+ SteamGameServer_ReturnBool(Result, false);
+ return;
+ }
+
+ SteamGameServer_Shutdown();
+ steam_gameserver_is_initialised = false;
+ SteamGameServer_ReturnBool(Result, true);
+}
+
+YYEXPORT void steam_gameserver_set_product(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetProduct(YYGetString(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_game_description(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetGameDescription(YYGetString(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_mod_dir(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetModDir(YYGetString(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_dedicated_server(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetDedicatedServer(YYGetBool(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_log_on(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->LogOn(YYGetString(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_log_on_anonymous(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->LogOnAnonymous();
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_log_off(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->LogOff();
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_logged_on(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ SteamGameServer_ReturnBool(Result, p ? p->BLoggedOn() : false);
+}
+
+YYEXPORT void steam_gameserver_secure(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ SteamGameServer_ReturnBool(Result, p ? p->BSecure() : false);
+}
+
+YYEXPORT void steam_gameserver_get_steam_id(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ Result.kind = VALUE_INT64;
+ Result.v64 = p ? static_cast(p->GetSteamID().ConvertToUint64()) : 0;
+}
+
+YYEXPORT void steam_gameserver_was_restart_requested(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ SteamGameServer_ReturnBool(Result, p ? p->WasRestartRequested() : false);
+}
+
+YYEXPORT void steam_gameserver_set_max_player_count(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetMaxPlayerCount(static_cast(YYGetReal(arg, 0)));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_bot_player_count(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetBotPlayerCount(static_cast(YYGetReal(arg, 0)));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_server_name(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetServerName(YYGetString(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_map_name(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetMapName(YYGetString(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_password_protected(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetPasswordProtected(YYGetBool(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_spectator_port(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetSpectatorPort(static_cast(YYGetReal(arg, 0)));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_spectator_server_name(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetSpectatorServerName(YYGetString(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_clear_all_key_values(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->ClearAllKeyValues();
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_key_value(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetKeyValue(YYGetString(arg, 0), YYGetString(arg, 1));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_game_tags(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetGameTags(YYGetString(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_game_data(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetGameData(YYGetString(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_region(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetRegion(YYGetString(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_set_advertise_server_active(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->SetAdvertiseServerActive(YYGetBool(arg, 0));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_get_public_ip(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ YYStructCreate(&Result);
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ SteamIPAddress_t ip{};
+ ip.m_eType = k_ESteamIPTypeIPv4;
+ if (p)
+ ip = p->GetPublicIP();
+ uint32 ipv4 = ip.m_eType == k_ESteamIPTypeIPv4 ? ip.m_unIPv4 : 0;
+ std::string ipString = SteamGameServer_IPv4ToString(ipv4);
+ YYStructAddBool(&Result, "is_set", p ? ip.IsSet() : false);
+ YYStructAddInt(&Result, "type", static_cast(ip.m_eType));
+ YYStructAddDouble(&Result, "ipv4", static_cast(ipv4));
+ YYStructAddString(&Result, "ipv4_string", ip.m_eType == k_ESteamIPTypeIPv4 ? ipString.c_str() : "");
+}
+
+YYEXPORT void steam_gameserver_handle_incoming_packet(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (!p)
+ {
+ SteamGameServer_ReturnBool(Result, false);
+ return;
+ }
+
+ int bufferIndex = YYGetInt32(arg, 0);
+ int packetSize = YYGetInt32(arg, 1);
+ uint32 srcIP = static_cast(YYGetReal(arg, 2));
+ uint16 srcPort = static_cast(YYGetReal(arg, 3));
+ void* bufferData = nullptr;
+ int bufferSize = 0;
+ if (bufferIndex < 0 || !BufferGetContent(bufferIndex, &bufferData, &bufferSize) || !bufferData)
+ {
+ SteamGameServer_ReturnBool(Result, false);
+ return;
+ }
+ if (packetSize <= 0 || packetSize > bufferSize)
+ packetSize = bufferSize;
+
+ bool ok = p->HandleIncomingPacket(bufferData, packetSize, srcIP, srcPort);
+ YYFree(bufferData);
+ SteamGameServer_ReturnBool(Result, ok);
+}
+
+YYEXPORT void steam_gameserver_get_next_outgoing_packet(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ YYStructCreate(&Result);
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ int bufferIndex = YYGetInt32(arg, 0);
+ int maxSize = (argc > 1) ? YYGetInt32(arg, 1) : 16 * 1024;
+ if (!p || bufferIndex < 0 || maxSize <= 0)
+ {
+ YYStructAddBool(&Result, "success", false);
+ YYStructAddInt(&Result, "size", 0);
+ return;
+ }
+
+ std::vector packet(static_cast(maxSize));
+ uint32 ip = 0;
+ uint16 port = 0;
+ int size = p->GetNextOutgoingPacket(packet.data(), maxSize, &ip, &port);
+ bool success = false;
+ if (size > 0)
+ success = BufferWriteContent(bufferIndex, 0, packet.data(), size, false, false) == size;
+
+ std::string ipString = SteamGameServer_IPv4ToString(ip);
+ YYStructAddBool(&Result, "success", success);
+ YYStructAddInt(&Result, "size", success ? size : 0);
+ YYStructAddDouble(&Result, "ip", static_cast(ip));
+ YYStructAddString(&Result, "ip_string", ipString.c_str());
+ YYStructAddInt(&Result, "port", port);
+}
+
+YYEXPORT void steam_gameserver_get_auth_session_ticket(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ YYStructCreate(&Result);
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ uint8 ticketBytes[3000] = { 0 };
+ uint32 ticketSize = 0;
+ HAuthTicket ticket = p ? p->GetAuthSessionTicket(ticketBytes, sizeof(ticketBytes), &ticketSize, nullptr) : k_HAuthTicketInvalid;
+ int ticketBuffer = -1;
+ if (ticket != k_HAuthTicketInvalid && ticketSize > 0)
+ {
+ ticketBuffer = CreateBuffer(ticketSize, eBuffer_Format_Fixed, 1);
+ BufferWriteContent(ticketBuffer, 0, ticketBytes, ticketSize);
+ }
+
+ YYStructAddInt(&Result, "ticket_buffer", ticketBuffer);
+ YYStructAddInt(&Result, "ticket_size", static_cast(ticketSize));
+ YYStructAddDouble(&Result, "auth_ticket_handle", static_cast(ticket));
+}
+
+YYEXPORT void steam_gameserver_begin_auth_session(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (!p)
+ {
+ SteamGameServer_ReturnReal(Result, -1.0);
+ return;
+ }
+
+ int32 bufferId = YYGetInt32(arg, 0);
+ int32 ticketSize = YYGetInt32(arg, 1);
+ int64 steamId = YYGetInt64(arg, 2);
+ void* bufferData = nullptr;
+ int bufferSize = 0;
+ if (ticketSize <= 0 || bufferId < 0 || !BufferGetContent(bufferId, &bufferData, &bufferSize) || !bufferData)
+ {
+ SteamGameServer_ReturnReal(Result, -1.0);
+ return;
+ }
+ if (ticketSize > bufferSize)
+ ticketSize = bufferSize;
+
+ CSteamID target;
+ target.SetFromUint64(static_cast(steamId));
+ EBeginAuthSessionResult authResult = p->BeginAuthSession(bufferData, ticketSize, target);
+ YYFree(bufferData);
+ SteamGameServer_ReturnReal(Result, static_cast(authResult));
+}
+
+YYEXPORT void steam_gameserver_end_auth_session(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p)
+ {
+ CSteamID steamId;
+ steamId.SetFromUint64(static_cast(YYGetInt64(arg, 0)));
+ p->EndAuthSession(steamId);
+ }
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_cancel_auth_ticket(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (p) p->CancelAuthTicket(static_cast(YYGetReal(arg, 0)));
+ SteamGameServer_ReturnBool(Result, p != nullptr);
+}
+
+YYEXPORT void steam_gameserver_user_has_license_for_app(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (!p)
+ {
+ SteamGameServer_ReturnReal(Result, -1.0);
+ return;
+ }
+
+ CSteamID steamId;
+ steamId.SetFromUint64(static_cast(YYGetInt64(arg, 0)));
+ AppId_t appId = static_cast(YYGetReal(arg, 1));
+ SteamGameServer_ReturnReal(Result, static_cast(p->UserHasLicenseForApp(steamId, appId)));
+}
+
+YYEXPORT void steam_gameserver_request_user_group_status(RValue& Result, CInstance* selfinst, CInstance* otherinst, int argc, RValue* arg)
+{
+ ISteamGameServer* p = SteamGameServer_GetAPI();
+ if (!p)
+ {
+ SteamGameServer_ReturnBool(Result, false);
+ return;
+ }
+
+ CSteamID userId;
+ userId.SetFromUint64(static_cast(YYGetInt64(arg, 0)));
+ CSteamID groupId;
+ groupId.SetFromUint64(static_cast(YYGetInt64(arg, 1)));
+ SteamGameServer_ReturnBool(Result, p->RequestUserGroupStatus(userId, groupId));
}
//dllg void Steam_Json_Test() {
diff --git a/source/Steamworks_gml/extensions/steamworks/steamworks_cpp/GMLSteam/steam_networking_sockets.cpp b/source/Steamworks_gml/extensions/steamworks/steamworks_cpp/GMLSteam/steam_networking_sockets.cpp
index 94a4b676..f61959e9 100644
--- a/source/Steamworks_gml/extensions/steamworks/steamworks_cpp/GMLSteam/steam_networking_sockets.cpp
+++ b/source/Steamworks_gml/extensions/steamworks/steamworks_cpp/GMLSteam/steam_networking_sockets.cpp
@@ -1,24 +1,35 @@
#include "pch.h"
#include "steam_glue.h"
#include "steam_api.h"
+#include "steam_gameserver.h"
#include "Extension_Interface.h"
#include "YYRValue.h"
#include "steam_common.h"
#include
+static ISteamNetworkingSockets* SteamNetSockets_GetGameServerSockets();
+
class GMNetSocketsCallbackHandler
{
public:
GMNetSocketsCallbackHandler() :
m_CallbackConnectionStatusChanged(this, &GMNetSocketsCallbackHandler::OnConnectionStatusChanged),
+ m_CallbackGameServerConnectionStatusChanged(this, &GMNetSocketsCallbackHandler::OnGameServerConnectionStatusChanged),
m_CallbackAuthenticationStatusChanged(this, &GMNetSocketsCallbackHandler::OnAuthenticationStatusChanged)
{}
+ void DispatchConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t* pInfo, ISteamNetworkingSockets* p);
+
STEAM_CALLBACK(GMNetSocketsCallbackHandler,
OnConnectionStatusChanged,
SteamNetConnectionStatusChangedCallback_t,
m_CallbackConnectionStatusChanged);
+ STEAM_GAMESERVER_CALLBACK(GMNetSocketsCallbackHandler,
+ OnGameServerConnectionStatusChanged,
+ SteamNetConnectionStatusChangedCallback_t,
+ m_CallbackGameServerConnectionStatusChanged);
+
STEAM_CALLBACK(GMNetSocketsCallbackHandler,
OnAuthenticationStatusChanged,
SteamNetAuthenticationStatus_t,
@@ -30,6 +41,17 @@ static GMNetSocketsCallbackHandler g_NetSocketsCallbacks;
void GMNetSocketsCallbackHandler::OnConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t* pInfo)
{
ISteamNetworkingSockets* p = SteamNetworkingSockets();
+ DispatchConnectionStatusChanged(pInfo, p);
+}
+
+void GMNetSocketsCallbackHandler::OnGameServerConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t* pInfo)
+{
+ ISteamNetworkingSockets* p = SteamNetSockets_GetGameServerSockets();
+ DispatchConnectionStatusChanged(pInfo, p);
+}
+
+void GMNetSocketsCallbackHandler::DispatchConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t* pInfo, ISteamNetworkingSockets* p)
+{
if (!p || !pInfo)
return;
@@ -387,7 +409,7 @@ YYEXPORT void steam_net_sockets_send_messages(
std::vector msgs(n, nullptr);
std::vector results(n, 0);
- // Allocate all message objects first so we don�t leak payload on partial failure
+ // Allocate all message objects first so we don't leak payload on partial failure.
for (int i = 0; i < n; ++i)
{
msgs[i] = pUtils->AllocateMessage(0); // we provide our own payload
@@ -623,7 +645,7 @@ YYEXPORT void steam_net_sockets_recv_messages_on_poll_group(
YYStructAddInt(&entry, "lane", (int)msg->m_idxLane);
SET_RValue(&Result, &entry, nullptr, i);
- YYFree(&entry);
+ // SET_RValue transfers ownership of the struct data into the array slot.
writeOffset += toWrite;
totalWritten += toWrite;
@@ -775,15 +797,17 @@ YYEXPORT void steam_net_sockets_set_connection_name(
RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
{
ISteamNetworkingSockets* p = SteamNetworkingSockets();
+ bool ok = false;
if (p)
{
HSteamNetConnection hConn = (HSteamNetConnection)YYGetReal(args, 0);
const char* name = YYGetString(args, 1);
p->SetConnectionName(hConn, name);
+ ok = true;
}
- Result.kind = VALUE_REAL;
- Result.val = 1.0;
+ Result.kind = VALUE_BOOL;
+ Result.val = ok;
}
YYEXPORT void steam_net_sockets_get_connection_name(
@@ -1044,278 +1068,205 @@ YYEXPORT void steam_net_sockets_get_authentication_status(
-///////////////Gameservers utilizing Steam Datagram Relay///////////////// This secction will be ignored for now....
+///////////////Gameservers utilizing Steam Datagram Relay/////////////////
+static ISteamNetworkingSockets* SteamNetSockets_GetGameServerSockets()
+{
+ if (!steam_gameserver_is_initialised)
+ return nullptr;
+ return SteamGameServerNetworkingSockets();
+}
-//YYEXPORT void steam_net_sockets_received_relay_auth_ticket(
-// RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
-//{
-// ISteamNetworkingSockets* p = SteamNetworkingSockets();
-// if (!p)
-// {
-// Result.kind = VALUE_REAL;
-// Result.val = 0.0;
-// return;
-// }
-//
-// int ticketBufIndex = (int)YYGetReal(args, 0);
-// int ticketSize = (int)YYGetReal(args, 1);
-// int parsedBufIndex = (int)YYGetReal(args, 2);
-//
-// void* ticket_data = nullptr;
-// int ticket_buf_size = 0;
-// if (!BufferGetContent(ticketBufIndex, &ticket_data, &ticket_buf_size) || !ticket_data)
-// {
-// DebugConsoleOutput("steam_net_sockets_received_relay_auth_ticket() - invalid ticket buffer\n");
-// Result.kind = VALUE_REAL;
-// Result.val = 0.0;
-// return;
-// }
-// if (ticketSize > ticket_buf_size)
-// ticketSize = ticket_buf_size;
-//
-// void* parsed_data = nullptr;
-// int parsed_size = 0;
-// if (!BufferGetContent(parsedBufIndex, &parsed_data, &parsed_size) || !parsed_data)
-// {
-// DebugConsoleOutput("steam_net_sockets_received_relay_auth_ticket() - invalid parsed buffer\n");
-// Result.kind = VALUE_REAL;
-// Result.val = 0.0;
-// return;
-// }
-//
-// // NOTE: we do NOT use SteamDatagramRelayAuthTicket here to avoid incomplete-type issues.
-// // Pass nullptr for the parsed ticket pointer.
-// int res = p->ReceivedRelayAuthTicket(
-// ticket_data,
-// ticketSize,
-// nullptr // SteamDatagramRelayAuthTicket* (ignored)
-// );
-//
-// // Optional: copy the raw ticket into parsed_buf for convenience/debugging
-// if (res >= 0)
-// {
-// int sz = ticketSize;
-// if (sz > parsed_size) sz = parsed_size;
-// memcpy(parsed_data, ticket_data, sz);
-// }
-//
-// Result.kind = VALUE_REAL;
-// Result.val = (double)res;
-//}
-//
-//YYEXPORT void steam_net_sockets_find_relay_auth_ticket_for_server(
-// RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
-//{
-// ISteamNetworkingSockets* p = SteamNetworkingSockets();
-// if (!p)
-// {
-// Result.kind = VALUE_REAL;
-// Result.val = -1.0;
-// return;
-// }
-//
-// // Read GML args � keep order exactly as you already expose them
-// int64 steamID64 = (int64)YYGetInt64(args, 0);
-// int remoteVirtPort = (int)YYGetReal(args, 1);
-// int parsedBufIndex = (int)YYGetReal(args, 2);
-//
-// // Validate parsed buffer
-// void* parsed_data = nullptr;
-// int parsed_size = 0;
-// if (!BufferGetContent(parsedBufIndex, &parsed_data, &parsed_size) || !parsed_data)
-// {
-// DebugConsoleOutput("steam_net_sockets_find_relay_auth_ticket_for_server() - error: parsed buffer %d not found\n", parsedBufIndex);
-// Result.kind = VALUE_REAL;
-// Result.val = -1.0;
-// return;
-// }
-//
-// // Build identity from SteamID64
-// SteamNetworkingIdentity id;
-// id.Clear();
-// id.SetSteamID64(steamID64);
-//
-// // Call Steam and DO NOT request the parsed struct to avoid incomplete type error
-// int seconds_left = p->FindRelayAuthTicketForServer(
-// id,
-// remoteVirtPort,
-// nullptr // SteamDatagramRelayAuthTicket* � we ignore it
-// );
-//
-// // Return seconds until expiry (0 = no ticket, >0 = time left, <0 = error)
-// Result.kind = VALUE_REAL;
-// Result.val = (double)seconds_left;
-//}
-//
-//YYEXPORT void steam_net_sockets_connect_to_hosted_dedicated_server(
-// RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
-//{
-// ISteamNetworkingSockets* p = SteamNetworkingSockets();
-// if (!p)
-// {
-// Result.kind = VALUE_REAL;
-// Result.val = -1.0;
-// return;
-// }
-//
-// int64 steamID64 = (int64)YYGetInt64(args, 0);
-// int remoteVirtPort = (int)YYGetReal(args, 1);
-//
-// SteamNetworkingIdentity id;
-// id.Clear();
-// id.SetSteamID64(steamID64);
-//
-// HSteamNetConnection hConn = p->ConnectToHostedDedicatedServer(
-// id,
-// remoteVirtPort,
-// 0, nullptr
-// );
-//
-// Result.kind = VALUE_REAL;
-// Result.val = (double)hConn;
-//}
-//
-//YYEXPORT void steam_net_sockets_get_hosted_dedicated_server_port(
-// RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
-//{
-// ISteamNetworkingSockets* p = SteamNetworkingSockets();
-// uint16 port = 0;
-// if (p)
-// port = p->GetHostedDedicatedServerPort();
-//
-// Result.kind = VALUE_REAL;
-// Result.val = (double)port;
-//}
-//
-//YYEXPORT void steam_net_sockets_get_hosted_dedicated_server_popid(
-// RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
-//{
-// ISteamNetworkingSockets* p = SteamNetworkingSockets();
-// SteamNetworkingPOPID popid = 0;
-// if (p)
-// popid = p->GetHostedDedicatedServerPOPID();
-//
-// Result.kind = VALUE_REAL;
-// Result.val = (double)popid;
-//}
-//
-//YYEXPORT void steam_net_sockets_get_hosted_dedicated_server_address(
-// RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
-//{
-// ISteamNetworkingSockets* p = SteamNetworkingSockets();
-// if (!p)
-// {
-// Result.kind = VALUE_REAL;
-// Result.val = (double)k_EResultFail;
-// return;
-// }
-//
-// int routingBufIndex = (int)YYGetReal(args, 0);
-//
-// void* routing_data = nullptr;
-// int routing_size = 0;
-// if (!BufferGetContent(routingBufIndex, &routing_data, &routing_size) || !routing_data)
-// {
-// DebugConsoleOutput("steam_net_sockets_get_hosted_dedicated_server_address() - invalid routing buffer %d\n", routingBufIndex);
-// Result.kind = VALUE_REAL;
-// Result.val = (double)k_EResultFail;
-// return;
-// }
-//
-// // Treat the GM buffer memory as a SteamDatagramHostedAddress struct.
-// // This only needs a *pointer* to the type, so the type can be incomplete.
-// SteamDatagramHostedAddress* pRouting = reinterpret_cast(routing_data);
-//
-// EResult er = p->GetHostedDedicatedServerAddress(pRouting);
-//
-// // We don't do memcpy � Steam already wrote the struct directly into the buffer.
-// // Caller just gets 'er' and the raw bytes in routing_buf.
-//
-// Result.kind = VALUE_REAL;
-// Result.val = (double)er;
-//}
-//
-//YYEXPORT void steam_net_sockets_create_hosted_dedicated_server_listen_socket(
-// RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
-//{
-// ISteamNetworkingSockets* p = SteamNetworkingSockets();
-// if (!p)
-// {
-// Result.kind = VALUE_REAL;
-// Result.val = -1.0;
-// return;
-// }
-//
-// int localVirtPort = (int)YYGetReal(args, 0);
-//
-// HSteamListenSocket hListen = p->CreateHostedDedicatedServerListenSocket(
-// localVirtPort,
-// 0, nullptr
-// );
-//
-// Result.kind = VALUE_REAL;
-// Result.val = (double)hListen;
-//}
-//
-//YYEXPORT void steam_net_sockets_get_game_coordinator_server_login(
-// RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
-//{
-// ISteamNetworkingSockets* p = SteamNetworkingSockets();
-// if (!p)
-// {
-// Result.kind = VALUE_REAL;
-// Result.val = 0.0;
-// return;
-// }
-//
-// int loginBufIndex = (int)YYGetReal(args, 0);
-// int blobBufIndex = (int)YYGetReal(args, 1);
-// int blobMaxSize = (int)YYGetReal(args, 2);
-//
-// void* login_data = nullptr;
-// void* blob_data = nullptr;
-// int login_size = 0;
-// int blob_size = 0;
-//
-// if (!BufferGetContent(loginBufIndex, &login_data, &login_size) || !login_data ||
-// !BufferGetContent(blobBufIndex, &blob_data, &blob_size) || !blob_data)
-// {
-// DebugConsoleOutput("steam_net_sockets_get_game_coordinator_server_login() - invalid buffers\n");
-// Result.kind = VALUE_REAL;
-// Result.val = 0.0;
-// return;
-// }
-//
-// if (blobMaxSize > blob_size)
-// blobMaxSize = blob_size;
-//
-// int cbSignedBlob = blobMaxSize;
-//
-// // Use the GameMaker buffer memory as the SteamDatagramGameCoordinatorServerLogin struct.
-// // This only needs a pointer type, so the type can be incomplete.
-// SteamDatagramGameCoordinatorServerLogin* pLogin =
-// reinterpret_cast(login_data);
-//
-// EResult er = p->GetGameCoordinatorServerLogin(
-// pLogin, // struct written directly into login_buf
-// &cbSignedBlob, // in/out size of signed blob
-// blob_data // blob written directly into blob_buf
-// );
-//
-// if (er != k_EResultOK)
-// {
-// cbSignedBlob = 0;
-//
-// // Optional: clear login buffer on failure so GML doesn't read garbage
-// if (login_data && login_size > 0)
-// memset(login_data, 0, login_size);
-// }
-//
-// // Return blob size actually written (0 on failure)
-// Result.kind = VALUE_REAL;
-// Result.val = (double)cbSignedBlob;
-//}
+YYEXPORT void steam_net_sockets_received_relay_auth_ticket(
+ RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
+{
+ Result.kind = VALUE_BOOL;
+ Result.val = false;
+
+ ISteamNetworkingSockets* p = SteamNetworkingSockets();
+ if (!p)
+ return;
+
+ int ticketBufIndex = YYGetInt32(args, 0);
+ int ticketSize = YYGetInt32(args, 1);
+ void* ticketData = nullptr;
+ int ticketBufferSize = 0;
+ if (ticketBufIndex < 0 || !BufferGetContent(ticketBufIndex, &ticketData, &ticketBufferSize) || !ticketData)
+ return;
+
+ if (ticketSize <= 0 || ticketSize > ticketBufferSize)
+ ticketSize = ticketBufferSize;
+
+ Result.val = p->ReceivedRelayAuthTicket(ticketData, ticketSize, nullptr);
+ YYFree(ticketData);
+}
+
+YYEXPORT void steam_net_sockets_find_relay_auth_ticket_for_server(
+ RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
+{
+ ISteamNetworkingSockets* p = SteamNetworkingSockets();
+ if (!p)
+ {
+ Result.kind = VALUE_REAL;
+ Result.val = -1.0;
+ return;
+ }
+
+ int64 steamID64 = static_cast(YYGetInt64(args, 0));
+ int remoteVirtPort = static_cast(YYGetReal(args, 1));
+
+ SteamNetworkingIdentity id;
+ id.Clear();
+ id.SetSteamID64(steamID64);
+
+ Result.kind = VALUE_REAL;
+ Result.val = static_cast(p->FindRelayAuthTicketForServer(id, remoteVirtPort, nullptr));
+}
+
+YYEXPORT void steam_net_sockets_connect_to_hosted_dedicated_server(
+ RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
+{
+ ISteamNetworkingSockets* p = SteamNetworkingSockets();
+ if (!p)
+ {
+ Result.kind = VALUE_REAL;
+ Result.val = -1.0;
+ return;
+ }
+
+ int64 steamID64 = static_cast(YYGetInt64(args, 0));
+ int remoteVirtPort = static_cast(YYGetReal(args, 1));
+
+ SteamNetworkingIdentity id;
+ id.Clear();
+ id.SetSteamID64(steamID64);
+
+ HSteamNetConnection hConn = p->ConnectToHostedDedicatedServer(id, remoteVirtPort, 0, nullptr);
+
+ Result.kind = VALUE_REAL;
+ Result.val = static_cast(hConn);
+}
+
+YYEXPORT void steam_net_sockets_get_hosted_dedicated_server_port(
+ RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
+{
+ ISteamNetworkingSockets* p = SteamNetSockets_GetGameServerSockets();
+ uint16 port = p ? p->GetHostedDedicatedServerPort() : 0;
+ Result.kind = VALUE_REAL;
+ Result.val = static_cast(port);
+}
+
+YYEXPORT void steam_net_sockets_get_hosted_dedicated_server_popid(
+ RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
+{
+ ISteamNetworkingSockets* p = SteamNetSockets_GetGameServerSockets();
+ SteamNetworkingPOPID popid = p ? p->GetHostedDedicatedServerPOPID() : 0;
+ Result.kind = VALUE_REAL;
+ Result.val = static_cast(popid);
+}
+
+YYEXPORT void steam_net_sockets_get_hosted_dedicated_server_address(
+ RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
+{
+ ISteamNetworkingSockets* p = SteamNetSockets_GetGameServerSockets();
+ if (!p)
+ {
+ Result.kind = VALUE_REAL;
+ Result.val = static_cast(k_EResultFail);
+ return;
+ }
+
+ int routingBufIndex = YYGetInt32(args, 0);
+ void* routingData = nullptr;
+ int routingSize = 0;
+ if (routingBufIndex < 0 || !BufferGetContent(routingBufIndex, &routingData, &routingSize) || !routingData)
+ {
+ Result.kind = VALUE_REAL;
+ Result.val = static_cast(k_EResultFail);
+ return;
+ }
+
+ EResult er = p->GetHostedDedicatedServerAddress(reinterpret_cast(routingData));
+ BufferWriteContent(routingBufIndex, 0, routingData, routingSize, false, false);
+ YYFree(routingData);
+
+ Result.kind = VALUE_REAL;
+ Result.val = static_cast(er);
+}
+
+YYEXPORT void steam_net_sockets_create_hosted_dedicated_server_listen_socket(
+ RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
+{
+ ISteamNetworkingSockets* p = SteamNetSockets_GetGameServerSockets();
+ if (!p)
+ {
+ Result.kind = VALUE_REAL;
+ Result.val = -1.0;
+ return;
+ }
+
+ int localVirtPort = static_cast(YYGetReal(args, 0));
+ HSteamListenSocket hListen = p->CreateHostedDedicatedServerListenSocket(localVirtPort, 0, nullptr);
+
+ Result.kind = VALUE_REAL;
+ Result.val = static_cast(hListen);
+}
+
+YYEXPORT void steam_net_sockets_get_game_coordinator_server_login(
+ RValue& Result, CInstance* self, CInstance* other, int argc, RValue* args)
+{
+ YYStructCreate(&Result);
+
+ ISteamNetworkingSockets* p = SteamNetSockets_GetGameServerSockets();
+ if (!p)
+ {
+ YYStructAddDouble(&Result, "result", static_cast(k_EResultFail));
+ YYStructAddInt(&Result, "blob_size", 0);
+ return;
+ }
+
+ int loginBufIndex = YYGetInt32(args, 0);
+ int blobBufIndex = YYGetInt32(args, 1);
+ int blobMaxSize = YYGetInt32(args, 2);
+
+ void* loginData = nullptr;
+ void* blobData = nullptr;
+ int loginSize = 0;
+ int blobSize = 0;
+ if (loginBufIndex < 0 || blobBufIndex < 0 ||
+ !BufferGetContent(loginBufIndex, &loginData, &loginSize) || !loginData ||
+ !BufferGetContent(blobBufIndex, &blobData, &blobSize) || !blobData)
+ {
+ if (loginData) YYFree(loginData);
+ if (blobData) YYFree(blobData);
+ YYStructAddDouble(&Result, "result", static_cast(k_EResultFail));
+ YYStructAddInt(&Result, "blob_size", 0);
+ return;
+ }
+
+ if (blobMaxSize <= 0 || blobMaxSize > blobSize)
+ blobMaxSize = blobSize;
+
+ int signedBlobSize = blobMaxSize;
+ EResult er = p->GetGameCoordinatorServerLogin(
+ reinterpret_cast(loginData),
+ &signedBlobSize,
+ blobData
+ );
+
+ if (er != k_EResultOK)
+ signedBlobSize = 0;
+
+ BufferWriteContent(loginBufIndex, 0, loginData, loginSize, false, false);
+ if (signedBlobSize > 0)
+ BufferWriteContent(blobBufIndex, 0, blobData, signedBlobSize, false, false);
+
+ YYFree(loginData);
+ YYFree(blobData);
+
+ YYStructAddDouble(&Result, "result", static_cast(er));
+ YYStructAddInt(&Result, "blob_size", signedBlobSize);
+}
diff --git a/source/Steamworks_gml/scripts/Steamworks_Definitions/Steamworks_Definitions.gml b/source/Steamworks_gml/scripts/Steamworks_Definitions/Steamworks_Definitions.gml
index e4f278f1..ec78809f 100644
--- a/source/Steamworks_gml/scripts/Steamworks_Definitions/Steamworks_Definitions.gml
+++ b/source/Steamworks_gml/scripts/Steamworks_Definitions/Steamworks_Definitions.gml
@@ -50,8 +50,28 @@ enum STEAMWORKS_PERSONA_STATE
INVISIBLE = 7, // ONLINE, BUT APPEARS OFFLINE TO FRIENDS. THIS STATUS IS NEVER PUBLISHED TO CLIENTS.
MAX,
};
+// EServerMode
+enum STEAMWORKS_GAMESERVER_MODE
+{
+ INVALID = 0,
+ NO_AUTHENTICATION = 1,
+ AUTHENTICATION = 2,
+ AUTHENTICATION_AND_SECURE = 3,
+}
+// SteamGameServer_Init query port values
+enum STEAMWORKS_GAMESERVER_QUERY_PORT
+{
+ SHARED = 65535,
+}
+// EUserHasLicenseForAppResult
+enum STEAMWORKS_USER_HAS_LICENSE_FOR_APP_RESULT
+{
+ HAS_LICENSE = 0,
+ DOES_NOT_HAVE_LICENSE = 1,
+ NO_AUTH = 2,
+}
// SteamNetworking connection states (ESteamNetworkingConnectionState)