Using API Keys

GD-Sync makes use of API keys to authenticate developers and their players. API Keys can be generated on our website. Once logged in, you can see an API Keys section on the navigation menu.

We highly recommend using a separate API key for each of your games. Lobbies are separated for each API key, using the same key for two different games means you will mix their lobbies together. Using separate keys also makes viewing statistics on the dashboard easier, since data is logged for each key.

In order to use an API Key, you can navigate to the GD-Sync configuration menu. The configuration menu can be found under Project > Tools > GD-Sync. The configuration menu contains two fields where you can enter both your public and private key. Once you have entered these correctly, you will be able to connect to our servers.

Basics

Once you have enabled the plugin you will get access to the GDSync class. This global class contains all the functions you will need to interact with our server network.

The GDSync class contains in-engine documentation which has descriptions for all signals, functions and their parameters. Extended documentation is available right here including code samples.

When testing your multiplayer game, Godot supports launching multiple instances of your game at once right in the editor. This feature can be extremely useful to test if your multiplayer game is working as expected. To launch multiple instances, go to Debug > Run Multiple Instances and select how many instances you want to launch.

Demo Project

In order to demonstrate our plugin and its capabilities, we have developed a demo project which makes full potential of all its features.

The demo project can be downloaded or cloned directly from Github (repo link). Make sure to enter your own public and private key in the configuration menu.

In-Engine Documentation

GD-Sync has in-engine documentation for every signal and function. This can be found by pressing the “Search Help” button on the top right of the Godot script editor. Once inside the search menu, search for “MultiplayerClient” and you’ll be able to view all functionalities.

The PropertySynchronizer and NodeInstantiator also have documentation including parameter descriptions.

Due to a Godot 4.x bug, the documentation might not show up. To fix this, please edit Addons > GD-Sync > MultiplayerClient.gd or any of the GD-Sync Nodes in any way and save it. Once you save it, the documentation shows up as normal.

C# Support

GD-Sync has built-in C# support. To use the C# API, you can simply download the regular GD-Sync version from the asset library.

Once you have the plugin installed and enabled, navigate to the configuration menu (Project > Tools > GD-Sync) and simply enable C# Support. This will download all necessary components required for C#.

Once enabled, restart and build your project. After this is complete, you can use the static GDSync class in C#.

Connecting

Connecting and going online using GD-Sync can be done with only a single function,  GDSync.start_multiplayer() . Make sure to connect relevant signals so you will be notified if the connection handshake has succeeded or not.

You can check whether you are connected or not using GDSync.is_active(). If the client disconnects for any reason (server outage, poor internet connection, etc), the GDSync.disconnected() signal is emitted. The plugin does not automatically try to reconnect if the connection fails completely.

func _ready():
	GDSync.connected.connect(connected)
	GDSync.connection_failed.connect(connection_failed)
	
	GDSync.start_multiplayer()

func connected():
	print("You are now connected!")

func connection_failed(error : int):
	match(error):
		ENUMS.CONNECTION_FAILED.INVALID_PUBLIC_KEY:
			push_error("The public or private key you entered were invalid.")
		ENUMS.CONNECTION_FAILED.TIMEOUT:
			push_error("Unable to connect, please check your internet connection.")
      

Local Multiplayer

GD-Sync also supports local multiplayer. This allows people to play together as long as they are on the same WIFI network. Using local multiplayer removes the requirement of having a GD-Sync account and does not use any data transfer. To start GD-Sync locally, you can use GDSync.start_local_multiplayer() . Make sure to connect relevant signals so you will be notified if the local server has started correctly.

Starting GD-Sync locally disables could storage features as it does not connect using the internet.

func _ready():
	GDSync.connected.connect(connected)
	GDSync.connection_failed.connect(connection_failed)
	
	GDSync.start_local_multiplayer()

func connected():
	print("You are now connected!")

func connection_failed(error : int):
	match(error):
		ENUMS.CONNECTION_FAILED.LOCAL_PORT_ERROR:
			push_error("Local GD-Sync server was unable to start. There is probably already a server running on this machine.")
      

Clients

Players that connect to our servers are called Clients. Clients are identified by their ID, which is an integer. Each client has its own unique ID. If you want to call a function on or get specific data from a specific client you will need to use their ID. When connected you can retrieve your own client ID using GDSync.get_client_id().

Player Data

Each client has their own collection of data. This data is automatically synchronized across all clients in the same lobby. It may take up to 1 second before the synchronization occurs on all clients.

Beware when updating the player data frequently as it can be a relatively expensive operation, using up quite a bit of transfer.

Each client can only modify their own player data. To set player data you can use GDSync.set_player_data(key : String, value : Variant). The key is the name of the variable you want to set. The value can be any Godot Variant. This function can be used to create a new entry or to overwrite existing data.

To erase data you can use GDSync.erase_player_data(key : String). This will erase existing data if the key exists.

Player data can be retrieved using GDSync.get_player_data(client_id : int, key : String, default : Variant). This function requires a client ID, as you can also use it to retrieve data from other clients in the same lobby. To retrieve your own data you can input your own client ID. Default is the value it will return in case the key does not exist, by default this is null. You can also check if an entry exists using GDSync.has_player_data(client_id : int, key : String). You can also retrieve all data from a player as a dictionary using GDSync.get_all_player_data(client_id : int).

When player data is changed it emits a signal on all clients, GDSync.player_data_changed(client_id : int, key : String, value). This signal will also emit when data is erased, in that case the value will be null. The example showcases a bit of code in a player script that updates their color on all clients if it changes.

Player data has a maximum storage of 2048 bytes. If you exceed it a critical error will be emitted, see Critical Errors for more info.

#Player script
func _ready():
	GDSync.player_data_changed.connect(player_data_changed)

func player_data_changed(client_id : int, key : String, value):
	if client_id != int(name): return #Data changed for a different player
	
	#Data changed for this player!
	if key == "Color" and value is Color: modulate = value
      

Usernames

As an optional feature, GD-Sync has a built-in username system. You can set your username using GDSync.set_username(username : String). Setting a username will emit the GDSync.player_data_changed(client_id : int, key : String, value) signal with the key “Username”.

You can retrieve other usernames including yourself using GDSync.get_player_data(client_id : int, key : String, default : Variant), with "Username" as the key.

In the configuration menu you can also enforce unique usernames. This rule will be enforced for each lobby separately. Enabling this feature will block two clients with the same username from joining, ensuring that each username in the lobby is unique

Lobbies

GD-Sync makes use of lobbies in which players can play together. Lobbies have a bunch of built-in functionalities that can aid you in the development of your game and networking.

Our service simplifies matchmaking by allowing you to retrieve all lobbies that are publicly visible. Lobbies can either be private or public, only public lobbies will show up when browsing. See Lobby Visibility And Browsing for more info.

Our lobby system has the addition of a tag system. Tags are publicly available data that can be viewed from the outside. Tags are useful to display information like the selected map or gamemode. See Tags And Data for more info.

Lobbies offer automatic host selection which simplifies authoritative logic when developing peer-to-peer games, see Host Selection for more info.

Creation

In order to create a lobby, you can use GDSync.create_lobby(name : String, password : String, public : bool, playerlimit : int, tags : Dictionary). Lobbies are identified using their name, thus it’s the only required parameter. All other parameters (password, public visibility, maximum player limit and tags) are optional. If no player limit is provided the limit will automatically be set to the maximum your plan allows.

Creating a lobby does not automatically join it, for joining a lobby see Joining And Leaving. After creating a lobby you have 5 seconds to join it. A lobby will get automatically deleted after 5 seconds if no players join within this timeframe.

The lobby name must be a minimum of 3 characters and a maximum of 32 characters. The password can be a maximum of 16 characters. If the password is left empty, joining a lobby will always succeed if you have not yet hit the lobby player limit.

func _ready():
	GDSync.lobby_created.connect(lobby_created)
	GDSync.lobby_creation_failed.connect(lobby_creation_failed)
	
	GDSync.create_lobby(
		"Lobby Name",
		"Password123",
		true,
		10,
		{
			"Map" : "Desert",
			"Game Mode" : "Free For All"
		}
	)

func lobby_created(lobby_name : String):
	print("Succesfully created lobby "+lobby_name)
#	Now you can join the lobby!

func lobby_creation_failed(lobby_name : String, error : int):
	match(error):
		ENUMS.LOBBY_CREATION_ERROR.LOBBY_ALREADY_EXISTS:
			push_error("A lobby with the name "+lobby_name+" already exists.")
		ENUMS.LOBBY_CREATION_ERROR.NAME_TOO_SHORT:
			push_error(lobby_name+" is too short.")
		ENUMS.LOBBY_CREATION_ERROR.NAME_TOO_LONG:
			push_error(lobby_name+" is too long.")
		ENUMS.LOBBY_CREATION_ERROR.PASSWORD_TOO_LONG:
			push_error("The password for "+lobby_name+" is too long.")
		ENUMS.LOBBY_CREATION_ERROR.TAGS_TOO_LARGE:
			push_error("The tags have exceeded the 2048 byte limit.")
		ENUMS.LOBBY_CREATION_ERROR.DATA_TOO_LARGE:
			push_error("The data have exceeded the 2048 byte limit.")
		ENUMS.LOBBY_CREATION_ERROR.ON_COOLDOWN:
			push_error("Please wait a few seconds before creating another lobby.")
      

Joining

After a lobby is created you will be able to join it. After creation you have to join within 5 seconds, otherwise it will get automatically deleted.

Joining a lobby can be done using GDSync.join_lobby(name : String, password : String). This function requires you to enter the name of the lobby you would like to join and the password if it has one. Make sure to connect relevant signals to be notified whether the join attempt succeeded or not.

func _ready():
	GDSync.lobby_joined.connect(lobby_joined)
	GDSync.lobby_join_failed.connect(lobby_join_failed)
	
	GDSync.join_lobby("Lobby Name", "Password")

func lobby_joined(lobby_name : String):
	print("Succesfully joined lobby "+lobby_name)

func lobby_join_failed(lobby_name : String, error : int):
	match(error):
		ENUMS.LOBBY_JOIN_ERROR.LOBBY_DOES_NOT_EXIST:
			push_error("The lobby "+lobby_name+" does not exist.")
		ENUMS.LOBBY_JOIN_ERROR.LOBBY_IS_CLOSED:
			push_error("The lobby "+lobby_name+" is closed.")
		ENUMS.LOBBY_JOIN_ERROR.LOBBY_IS_FULL:
			push_error("The lobby "+lobby_name+" is full.")
		ENUMS.LOBBY_JOIN_ERROR.INCORRECT_PASSWORD:
			push_error("The password for lobby "+lobby_name+" was incorrect.")
		ENUMS.LOBBY_JOIN_ERROR.DUPLICATE_USERNAME:
			push_error("The lobby "+lobby_name+" already contains your username.")
      

Leaving

Leaving a lobby is quite simple, once you are inside a lobby you can use GDSync.leave_lobby() to exit it. Leaving a lobby does not emit any signals and always succeeds.

If all players inside the lobby have left, it will automatically get deleted.

Visibility & Browsing

In GD-Sync, lobbies can be either public or private. If the lobby is public, it can be retrieved using GDSync.get_public_lobbies(). Using this function will in turn emit the signal GDSync.lobbies_received(lobbies : Array). The signal returns an array of all public lobbies in the format displayed on the right.

[
  {
    "Name": "Super Cool Lobby",
    "PlayerCount": 2,
    "PlayerLimit": 4,
    "Public": true,
    "Closed": false,
    "Tags": {
      "Map": "Desert",
      "Gamemode": "Free For All"
    }
  },
  {
    "Name": "Another Cool Lobby",
    "PlayerCount": 4,
    "PlayerLimit": 4,
    "Public": true,
    "Closed": true,
    "Tags": {
      "Map": "City",
      "Gamemode": "Team Deathmatch"
    }
  }
]

      

Opening & Closing

Lobbies can be either open or closed. If a lobby is closed it no longer allows new players to join. This can be used to prevent players from joining while a game is in-progress. Lobbies can be opened using GDSync.open_lobby() and closed using GDSync.close_lobby().

Joining & Leaving Signals

If a client joins or leaves a lobby a signal is emitted. If a client joins a lobby, the signal GDSync.client_joined(client_id : int). The ID is the id of the client that joined the lobby. This signal is emitted for all clients that join including yourself.

If a client leaves the signal GDSync.client_left(client_id : int) is emitted. This signal is emitted for all clients excluding you. The reason for this is that when you leave a lobby you no longer receive messages from it, thus the signal is not emitted for yourself.

var clients : Dictionary = {}

func _init():
	GDSync.client_joined.connect(client_joined)
	GDSync.client_left.connect(client_left)

func client_joined(client_id : int):
	print("A new client has joined!")
	var client : Node = player_controller.instantiate()
	client.name = str(client_id)
	
	clients[client_id] = client
	add_child(client)

func client_left(client_id : int):
	print("A client has left")
	
	clients[client_id].queue_free()
	clients.erase(client_id)
      

Tags & Data

Lobbies can store temporary variables using tags and lobby data. Both are functionally the same with one key difference, tags are publicly visible and data is not.

Lobby tags and data are automatically synchronized across all clients in the same lobby. It may take up to 1 second before the synchronization occurs on all clients.

Beware when updating the tags or data frequently as it can be a relatively expensive operation, using up quite a bit of transfer.

A client can only modify the tags or data of the lobby they are in. To set lobby tags you can use GDSync.set_lobby_tags(key : String, value : Variant). The key is the name of the variable you want to set. The value can be any Godot Variant. This function can be used to create a new entry or to overwrite existing data.

To erase tags you can use GDSync.erase_lobby_tags(key : String). This will erase the existing tag if the key exists.

Lobby tags can be retrieved using GDSync.get_lobby_tag(key : String, default : Variant). Key is the variable you want to retrieve. Default is the value it will return in case the key does not exist, by default this is null. You can also check if an entry exists using GDSync.has_lobby_tag(key : String). You can also retrieve all lobby tags as a dictionary using GDSync.get_all_lobby_tags().

When lobby tags are changed it emits a signal on all clients in the lobby, GDSync.lobby_tags_changed(key : String, value : Variant).

To set lobby data you can use GDSync.set_lobby_data(key : String, value : Variant). The key is the name of the variable you want to set. The value can be any Godot Variant. This function can be used to create a new entry or to overwrite existing data.

To erase data you can use GDSync.erase_lobby_data(key : String). This will erase existing data if the key exists.

Lobby data can be retrieved using GDSync.get_lobby_data(key : String, default : Variant). Key is the variable you want to retrieve. Default is the value it will return in case the key does not exist, by default this is null. You can also check if an entry exists using GDSync.has_lobby_data(key : String). You can also retrieve all lobby data as a dictionary using GDSync.get_all_lobby_data().

When lobby data is changed it emits a signal on all clients in the lobby, GDSync.lobby_data_changed(key : String, value : Variant).

Host Selection

When developing a peer-to-peer game, generally one client is always picked as the host. The host client will perform most authoritative logic such as spawning enemies, starting and ending matches, etc.

GD-Sync has built-in host selection which gives you an easy way to know on which client to perform important tasks. This is an essential part of the plugin that is used often. By default, the first player that joins a lobby is picked as the host. If the current host leaves, a new one is automatically selected.

Checking if you are the host can be done in two ways. The first method is using GDSync.is_host(), which will return whether you are the host of the current lobby or not. The second method is using the signal GDSync.host_changed(is_host : bool). This signal is automatically emitted when the host changes (if the current host disconnects or leaves).

func _ready():
	GDSync.host_changed.connect(host_changed)
	
	if GDSync.is_host():
		print("I am the host!")
	else:
		print("I am not the host.")

func host_changed(is_host : bool, new_host_id : int):
	print("Client "+str(new_host_id)+" is the new host")
	if is_host:
		print("I am the new host!")
	else:
		print("I am not new the host.")
      

Accessing & Synchronizing Remote Nodes

GD-Sync is entirely dependent on Node paths. When synchronizing variables or calling remote functions, it is required that the path of the Node you are trying to access is the same across all clients. This also means a Node must be present in the scene tree in order for GDSync to access it.

When spawning in Nodes such as enemies or bullets, make sure that the instantiated Node has the same name across all clients if you want to synchronize it.

Remote Function Calls

GD-Sync allows you to call functions on other clients that are in the same lobby as you. As a safety feature, GD-Sync automatically blocks all remote function calls. In order to allow remote calls, you can use GDSync.expose_func(callable : Callable) or GDSync.expose_node(node : Node) to grant full access to an entire Node.

To call a function on another client you can use GDSync.call_func(callable : Callable, parameter_array : Array). This function requires parameters to be passed inside an array. Using this function will perform a remote call on all clients except yourself. The example code shows a script that spawns an enemy every 10 seconds.

In this example a single client is authoritative (the host) which remotely spawns in the enemies on other clients. This script also ensures that the NodePath of the instantiated Node is the same across all clients, so it can be properly accessed later on.

Instead of manually instantiating nodes, we highly recommend using the custom NodeInstantiator.



GD-Sync also allows you to see which client a remote function call came from. GDSync.get_sender_id() returns the client ID of the last client that performed a remote function call. By default this feature is disabled, as it slightly increases data usage. It can be enabled through the configuration menu.

func _ready():
	GDSync.expose_func(instantiate_enemy)

var t : float = 0.0
var enemy_index : int = 0
func _process(delta):
	t += delta
	if t >= 10:
		t -= 10
		spawn_enemy()

func spawn_enemy():
	if !GDSync.is_host(): return
	
	var enemy_name : String = "Enemy"+str(enemy_index)
	instantiate_enemy(enemy_name)
	GDSync.call_func(instantiate_enemy, [enemy_name])

func instantiate_enemy(name : String):
	var enemy : Node = enemy_scene.instantiate()
	enemy.name = name
	add_child(enemy)

	enemy_index += 1
      

Remote Variable Syncing

GD-Sync allows you to synchronize Node variables on clients that are in the same lobby as you. As a safety feature, GD-Sync automatically blocks all sync requests. In order to allow variable synchronization, you can use GDSync.expose_var(variable_name : String) or GDSync.expose_node(node : Node) to grant full access to an entire Node.

To synchronize a variable to all other clients you can use GDSync.sync_var(node : Node, variable_name). Using this function will sync the value of the variable to all other clients.

Instead of synchronizing variables manually, why highly recommend using the custom PropertySynchronizer. See PropertySynchronizer Class for more info.

var i : int = 0
func _ready():
	GDSync.expose_var("i")
	
	i = 10
	GDSync.sync_var(self, "i")
#	The value of i will now be 10 on all clients

Instantiating Nodes

GD-Sync offers a simple solution to spawning in Nodes across multiple clients. Spawning in Nodes can be done in two ways:
- Using the custom NodeInstantiator class.
- Using a global function.

An example of the NodeInstantiator is spawning in bullets that come out of a gun. The screenshot shows a NodeInstantiator that spawns bullets inside the current scene. If “Sync Starting Changes” is enabled, any changes made to the root Node of the instantiated scene within the same frame will automatically be synchronized across all clients. This can be useful for when setting the position, direction and speed of the bullet, doing this will set those values on all clients. This setting will only synchronize properties on the root Node of the Instantiated scene.

To instantiate a Node using the NodeInstantiator you can use instantiate_node(), which will return the instantiated Node. This Node will automatically be instantiated on all other clients as well. The NodeInstantiator automatically ensures the NodePath matches up on all clients so that GD-Sync may properly use it. When a Node is instantiated by a NodeInstantiator, it will emit the node_instantiated(node : Node) signal.

The second way to instantiate a scene is using GDSync.multiplayer_instantiate(scene : PackedScene, parent : Node, sync_starting_changes : bool, excluded_properties : PackedStringArray, replicate_on_join : bool = true). This method is functionally the same as the NodeInstantiator.

Any scripts that are instantiated using these two methods may override the function _mutliplayer_ready(). This is called after _ready(), when the starting synchronizations have been applied. The order of these operations is as followed:
_ready() is called -> starting changes are applied -> _multiplayer_ready() is called.

Instantiating scenes also supports scene replication.

PropertySynchronizer Class

GD-Sync comes with a custom PropertySynchronizer. The PropertySynchronizer ensures that a value of a variable is synced across all clients. Simply point it to a variable and it will do the rest. The PropertySynchronizer is optimized for data usage and will only synchronize if the property it is linked to changes.

When using the PropertySynchronizer, make sure its NodePath matches up on all clients. If this is not the case it won’t be able to find itself and perform the sync. When spawning in Nodes using the custom NodeInstanciator Class, this is automatically done for you.

The PropertySynchronizer also offers built-in interpolation. Interpolation only works with certain Variants (int, float, vector2 ,vector3, vector4, color, quaternion, basis), if one of these is selected the interpolation settings will automatically appear in the inspector.

The frequency of the sync requests is entirely dependent on the refresh rate. We recommend keeping this relatively low (< 30) in combination with interpolation, to keep data usage as low as possible. If you want to synchronize the rotation/orientation of a Node3D, make sure to fill in the property “basis” instead of “rotation”.

SynchronizedAnimationPlayer Class

GD-Sync comes with an extended version of the default AnimationPlayer. The SynchronizedAnimationPlayer adds full multiplayer support to AnimationPlayers without any extra steps.

All functions related to animation playback will automatically be called on all other clients:
- play()
- play_backwards()
- pause()
- stop()
- queue()
- seek()
- advance()

The SynchronizedAnimationPlayer does NOT synchronize the playback of AnimationTrees. It only works for AnimationPlayers who play animations directly.

Scene Replication

GD-Sync offers built-in scene replication with NodeInstantiators. Once enabled, it will replicate spawned Nodes on new clients that join the lobby. If "Sync Starting Changes" is enabled, any changes made to the root Node of the instantiated scene will also be replicated on other clients.

Node replication keeps functioning, even if the NodeInstantiator itself has been destroyed.

Once a spawned Node is removed from the scene tree, it is considered deleted and will no longer be replicated for new clients.

Node Ownership

GD-Sync has a built-in method to assign node ownership. Assigning an owner to a Node does not do anything by itself, but it is an extremely useful feature when writing your code. Node ownership is global across all clients, if you assign ownership to a Node it will do it on all clients.

A good example of this is when writing player scripts. When re-using the same scene for all players, you only want movement inputs to function on YOUR player. An easy way to do this is when spawning the client in, to assign it to yourself. When a client joins the lobby you can make them the owner of their own player. This can later be used inside the player script to handle the inputs to only allow you to move your player instance.

Ownership can be assigned using GDSync.set_gdsync_owner(node : Node, owner : int). The owner can be the client ID of any client in your lobby. To remove ownership, use GDSync.clear_gdsync_owner(node : Node).

You can check whether you are the owner of a Node with GDSync.is_gdsync_owner(node : Node). You can also retrieve the owner of a node with GDSync.get_gdsync_owner(node : Node). If the returned id is -1, it means the object has no owner.

Node ownership is stored locally per client, so beware that assigning ownership is not instantly synchronized among clients. This can lead to race conditions where clients do not have the same ownership information. Make sure to write your code to handle possible race conditions. An example of a script that does this can be seen on the right. This script is for grabbable objects where all ownership checks are done on the host, eliminating race conditions entirely.

#Player Spawner Script
func _ready():
	GDSync.client_joined.connect(client_joined)

func client_joined(client_id : int):
	print("A new client has joined!")
	var client : Node = player_controller.instantiate()
	client.name = str(client_id)
	
	clients[client_id] = client
	add_child(client)
	GDSync.set_gdsync_owner(client, client_id)
#Player Script
func _process(delta):
	if !GDSync.is_gdsync_owner(self): return
	var direction : Vector2 = Vector2.ZERO
	
	if Input.is_action_pressed("Up"):
		direction.y -= 1
	if Input.is_action_pressed("Down"):
		direction.y += 1
	if Input.is_action_pressed("Left"):
		direction.x -= 1
	if Input.is_action_pressed("Right"):
		direction.x += 1
	
	direction = direction.normalized()
	position += direction * speed * delta
#Object grab script
func _ready():
	#Expose functions
	GDSync.expose_func(grab_succes)
	GDSync.expose_func(grab_failed)
	GDSync.expose_func(host_grab_request)

func try_grab() -> void:
	#Send a grab request to the host.
	#Make sure to send your own ID so the host knows who is making the request.
	GDSync.call_func_on(GDSync.get_host(), host_grab_request, [GDSync.get_client_id()]) 

func try_release() -> void:
	#Check if you are actually the one holding the object
	if GDSync.get_gdsync_owner(self) != GDSync.get_client_id():
		return
	
	#Clear ownership
	GDSync.clear_gdsync_owner(self)
	
	#Implement further release logic

func grab_succes() -> void:
	#Grab was succesful, implement grab logic
	pass

func grab_failed() -> void:
	#Grab failed, someone else is holding it
	pass

func host_grab_request(client : int) -> void:
	#Check if it already has an owner, if so it already being held by someone.
	if is_grabbed():
		#Notify that the grab has failed
		GDSync.call_func_on(client, grab_failed)
		return
	
	#Grab was succesful, assign new owner and notify the client
	GDSync.set_gdsync_owner(self, client)
	GDSync.call_func_on(client, grab_succes)

func is_grabbed() -> bool:
	return GDSync.get_gdsync_owner(self) >= 0

Time Synchronized Events

GD-Sync comes with a feature called synchronized events. This system allows you to create events that trigger after a set delay. Events trigger on all clients simultaneously, regardless of latency between clients. This can be essential for time-critical mechanics where exact timing is crucial. We recommend a delay >= 1.0 seconds to account for latency. This will make sure all clients know of the event before it triggers.

Events can be created using GDSync.create_synced_event(event_name : String, delay : float, parameters : Array). Events also allow you to bind an array of parameters, which can be read when the event is triggered.

You can listen to events using the signal GDSync.synced_event_triggered(event_name : String, parameters : Array).


func _ready():
	#Connect signal
	GDSync.synced_event_triggered.connect(synced_event_triggered)
	
	#Create an event, that triggers after 5 seconds
	GDSync.create_synced_event("MyEvent", 5.0, ["param1", "param2", true])

func synced_event_triggered(event_name : String, parameters : Array) -> void:
	if event_name == "MyEvent":
		#This gets executed on all clients at the same time
		print("MyEvent triggered!")
		print("params: ", parameters)

Account System

GD-Sync comes with a full player account management system. This allows players to create their own accounts, store data in a database and submit scores to leaderboards.

In order to make use of player accounts, you must first create a database on the control panel. Once a database has been created, make sure to go to your API key and link it.

Accounts are identified by both their email address and username, which are both unique.

The account system also comes with a built-in moderation system which allows you to ban accounts. Players may report each other using the report system.

Creation

Accounts can be created from inside the engine using GDSync.create_account(email : String, username : String, password : String).

This function will return a response code which can be any value from ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.

Account usernames and passwords must be between 3 and 20 characters long. Usernames must follow the set username regex settings. The email has to be in a valid email format.

If email verification is enabled, an email with a verification code is automatically sent to the email address.

After an account has been created, the user may log in.

func _ready():
	var response_code : int = await GDSync.create_account("email@email.com", "username", "password")
	
	if response_code == ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.SUCCESS:
		print("Account created!")
	else:
		match(response_code):
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.NO_RESPONSE_FROM_SERVER:
				push_error("No response from server")
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.DATA_CAP_REACHED:
				push_error("Data transfer cap has been reached.")
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.RATE_LIMIT_EXCEEDED:
				push_error("Rate limit exceeded, please wait and try again.")
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.NO_DATABASE:
				push_error("API key has no linked database.")
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.STORAGE_FULL:
				push_error("Database is full.")
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.INVALID_EMAIL:
				push_error("Invalid email address.")
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.INVALID_USERNAME:
				push_error("Username contains illegal characters.")
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.EMAIL_ALREADY_EXISTS:
				push_error("An account with this email address already exists.")
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.USERNAME_ALREADY_EXISTS:
				push_error("An account with this username address already exists.")
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.USERNAME_TOO_SHORT:
				push_error("Username is too short.")
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.USERNAME_TOO_LONG:
				push_error("Username is too long.")
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.PASSWORD_TOO_SHORT:
				push_error("Password is too short.")
			ENUMS.ACCOUNT_CREATION_RESPONSE_CODE.PASSWORD_TOO_LONG:
				push_error("Password is too long.")
      

Deletion

Accounts can be deleted from inside the engine using GDSync.delete_account(email : String, password : String). Accounts may also be deleted from the control panel.

This function will return a response code which can be any value from ENUMS.ACCOUNT_DELETION_RESPONSE_CODE.

func _ready():
	var response_code : int = await GDSync.delete_account("email@email.com", "password")
	
	if response_code == ENUMS.ACCOUNT_DELETION_RESPONSE_CODE.SUCCESS:
		print("Account deleted!")
	else:
		match(response_code):
			ENUMS.ACCOUNT_DELETION_RESPONSE_CODE.NO_RESPONSE_FROM_SERVER:
				push_error("No response from server")
			ENUMS.ACCOUNT_DELETION_RESPONSE_CODE.DATA_CAP_REACHED:
				push_error("Data transfer cap has been reached.")
			ENUMS.ACCOUNT_DELETION_RESPONSE_CODE.RATE_LIMIT_EXCEEDED:
				push_error("Rate limit exceeded, please wait and try again.")
			ENUMS.ACCOUNT_DELETION_RESPONSE_CODE.NO_DATABASE:
				push_error("API key has no linked database.")
			ENUMS.ACCOUNT_DELETION_RESPONSE_CODE.EMAIL_OR_PASSWORD_INCORRECT:
				push_error("Email or password incorrect.")
      

Logging In/Out

Accounts can be logged into using GDSync.login(email : String, password : String, valid_time : float).

Valid time indicates how long this login sessions should be valid for in seconds. Login sessions do not automatically refresh.

This function will return dictionary in the format on the right. BanTime shows the unix timestamp until the account is banned. If BanTime is -1, the account is permanently banned.  It contains a response code which can be any value from ENUMS.LOGIN_RESPONSE_CODE.

You may also attempt to login using GDSync.login_from_session(valid_time : float). This will attempt to login using the previous login session, if it has not yet expired. This can also be used to refresh the current login session to keep it alive.

You can log out using GDSync.logout(). This function will return a response which can be any value from ENUMS.LOGOUT_RESPONSE_CODE.

IMPORTANT: When logged into an account, please use GDSync.quit() instead of get_tree().quit(). This allows the plugin to perform a few crucial callbacks to the server backend.

{
	"Code" : 0,
	"BanTime" : 1719973379
}
      
func _ready():
	var response : Dictionary = await GDSync.login("email@email.com", "password")
	var response_code : int = response["Code"]
	
	if response_code == ENUMS.LOGIN_RESPONSE_CODE.SUCCESS:
		print("Logged in!")
	else:
		match(response_code):
			ENUMS.LOGIN_RESPONSE_CODE.NO_RESPONSE_FROM_SERVER:
				push_error("No response from server")
			ENUMS.LOGIN_RESPONSE_CODE.DATA_CAP_REACHED:
				push_error("Data transfer cap has been reached.")
			ENUMS.LOGIN_RESPONSE_CODE.RATE_LIMIT_EXCEEDED:
				push_error("Rate limit exceeded, please wait and try again.")
			ENUMS.LOGIN_RESPONSE_CODE.NO_DATABASE:
				push_error("API key has no linked database.")
			ENUMS.LOGIN_RESPONSE_CODE.EMAIL_OR_PASSWORD_INCORRECT:
				push_error("Email or password incorrect.")
			ENUMS.LOGIN_RESPONSE_CODE.NOT_VERIFIED:
				push_error("Email is not verified.")
			ENUMS.LOGIN_RESPONSE_CODE.BANNED:
				var ban_time : int = response["BanTime"]
			
				if ban_time == -1:
					push_error("Account is permanently banned.")
				else:
					var ban_time_string : String = Time.get_datetime_string_from_unix_time(ban_time, true)
					push_error("Account is banned until "+ban_time_string+".")
      

Email Verification

GD-Sync offers built-in email verification for accounts. If enabled in the user accounts settings, a verification email is sent to the user after creating their account.

This email contains a verification code which must be entered in-game to verify their email address.

The verification code can be entered using GDSync.verify_account(email : String, code : String, valid_time : float). This function returns a response code which can be any value from ENUMS.ACCOUNT_VERIFICATION_RESPONSE_CODE. If verification is successful, the user will automatically be logged in. Valid time indicates how long this login sessions should be valid for in seconds. See logging in for more information.

Once a verification code has been sent, it is valid for the next 10 minutes. If it expires, you can re-send it using GDSync.resend_verification_code(email : String, password : String). This function returns a response code which can be any value from ENUMS.RESEND_VERIFICATION_RESPONSE_CODE.

Moderation

GD-Sync offers a built-in account moderation system. This system allows you to moderate accounts and ban them in case they violate the terms of your game.

Accounts can be moderated from the control panel where users may be manually banned.

Accounts can be banned from within the engine. The function GDSync.ban_account(ban_duration : float), bans the currently logged-in account.

Users may also report each other in-game using GDSync.report_account(username_to_report : String, report : String). This function returns a response code which can be any value from ENUMS.REPORT_USER_RESPONSE_CODE. A user may file up to 10 reports. A user may only have one active report against a user, reporting a user while a report is still open will overwrite the previous one.

Reports made against users can be viewed on the control panel.

If automated bans are enabled, a user that is reported more than the set report-threshold will automatically be banned.

Friends

GD-Sync accounts may have up to 50 friends. If a friend is inside a lobby it will be visible to all other friends, allowing them to easily play with each other.

Friend requests may be sent to other accounts using GDSync.send_friend_request(friend : String). This function returns a response code which can be any value from ENUMS.SEND_FRIEND_REQUEST_RESPONSE_CODE.

Once a friend request has been sent, the other account needs to accept it. A friend request may be accepted using GDSync.accept_friend_request(friend : String). This function returns a response code which can be any value from ENUMS.ACCEPT_FRIEND_REQUEST_RESPONSE_CODE.

To remove a friend or to deny a pending request, you can use GDSync.remove_friend(friend : String). This function returns a response code which can be any value from ENUMS.REMOVE_FRIEND_RESPONSE_CODE.

Once a friend has been added you can retrieve the status of a single friend or all friends. To retrieve the status of a single friend you can use GDSync.get_friend_status(friend : String). This function returns a dictionary in the format on the right. The response code can be any value from  ENUMS.GET_FRIEND_STATUS_RESPONSE_CODE.

To retrieve the status of a all friends and pending friend requests, you can use GDSync.get_friends(). This function returns a dictionary in the format on the right. The response code can be any value from  ENUMS.GET_FRIENDS_RESPONSE_CODE.

The status of a friend is indicated by the enum ENUMS.FRIEND_STATUS.

{
	"Code" : 0,
	"Result" : {
		"FriendStatus" : 2,
		"Lobby" : {
			"Name" : "Epic Lobby",
			"HasPassword" : false
		}
	}
}
      
{
	"Code" : 0,
	"Result" : {
		[
			{
				"Username" : "Epic Username",
				"FriendStatus" : 2,
				"Lobby" : {
					"Name" : "Epic Lobby",
					"HasPassword" : true
				}
			},
				"Username" : "Cool Username",
				"FriendStatus" : 2,
				"Lobby" : {
					"Name" : "",
					"HasPassword" : false
				}
			},
				"Username" : "Awesome Username",
				"FriendStatus" : 1
			}
		]
	}
}
      

Persistent Data Storage

GD-Sync comes with persistent data storage, which allows you to store data online so it can be retrieved later on at any time. Persistent data storage allows you to store up to 1MB of data per account.

In order to make use of persistent data storage, you must first create a database on the control panel. Once a database has been created, make sure to go to your API key and link it.

Each account has their own filesystem, which allows you to create collections and documents. Documents themselves also function as collections, allowing you to nest collections/documents inside other documents.

An example use-case of persistent data storage is storing save files online. This allows users to upload their saves and continue playing on any other device by retrieving them.

Storing

To store data on an account, you can use GDSync.set_player_document(path : String, document : Dictionary, externally_visible : bool). This function returns a response code which can be any value from ENUMS.SET_PLAYER_DOCUMENT_RESPONSE_CODE .

You may specify a path where the document should be stored. If the collections specified in the path don't already exist, they are automatically created.

A document can contain all Godot Variants. It does not support object encoding due to safety reasons. To retrieve a document, see document retrieval.

A document/collection can either be public or private. Public documents can be read by other players. If externally visible is set to true, it will automatically make all parent collections/documents visible as well. Setting it to false will hide all nested collections/documents.

You may update the visibility of a collection or document at any time using GDSync.set_external_visible(path : String, externally_visible : bool = false). This function returns a response code which can be any value from ENUMS.SET_EXTERNAL_VISIBLE_RESPONSE_CODE.

func _ready():
	var document : Dictionary = {
		"Info" : "This is a document!",
		"Numbers" : [1, 3, 523],
		"Godot" : "Is awesome"
	}
	
	var response_code : int = await GDSync.set_player_document("collection1/collection2/document1", document)
	
	if response_code == ENUMS.SET_PLAYER_DOCUMENT_RESPONSE_CODE.SUCCESS:
		print("Document created!")
	else:
		match(response_code):
			ENUMS.SET_PLAYER_DOCUMENT_RESPONSE_CODE.NO_RESPONSE_FROM_SERVER:
				push_error("No response from server")
			ENUMS.SET_PLAYER_DOCUMENT_RESPONSE_CODE.DATA_CAP_REACHED:
				push_error("Data transfer cap has been reached.")
			ENUMS.SET_PLAYER_DOCUMENT_RESPONSE_CODE.RATE_LIMIT_EXCEEDED:
				push_error("Rate limit exceeded, please wait and try again.")
			ENUMS.SET_PLAYER_DOCUMENT_RESPONSE_CODE.NO_DATABASE:
				push_error("API key has no linked database.")
			ENUMS.SET_PLAYER_DOCUMENT_RESPONSE_CODE.NOT_LOGGED_IN:
				push_error("There is not valid login session.")
			ENUMS.SET_PLAYER_DOCUMENT_RESPONSE_CODE.STORAGE_FULL:
				push_error("Database is full.")
      

Deleting

To delete data from an account, you can use GDSync.delete_player_document(path : String). This function returns a response code which can be any value from ENUMS.DELETE_PLAYER_DOCUMENT_RESPONSE_CODE .

This function can be used to delete both collections and documents. If the document or collection specified by the path has any nested collections or documents, those will also be deleted.

func _ready():
	var response_code : int = await GDSync.delete_player_document("collection1/collection2/document1")
	
	if response_code == ENUMS.DELETE_PLAYER_DOCUMENT_RESPONSE_CODE.SUCCESS:
		print("Document deleted!")
	else:
		match(response_code):
			ENUMS.DELETE_PLAYER_DOCUMENT_RESPONSE_CODE.NO_RESPONSE_FROM_SERVER:
				push_error("No response from server")
			ENUMS.DELETE_PLAYER_DOCUMENT_RESPONSE_CODE.DATA_CAP_REACHED:
				push_error("Data transfer cap has been reached.")
			ENUMS.DELETE_PLAYER_DOCUMENT_RESPONSE_CODE.RATE_LIMIT_EXCEEDED:
				push_error("Rate limit exceeded, please wait and try again.")
			ENUMS.DELETE_PLAYER_DOCUMENT_RESPONSE_CODE.NO_DATABASE:
				push_error("API key has no linked database.")
			ENUMS.DELETE_PLAYER_DOCUMENT_RESPONSE_CODE.NOT_LOGGED_IN:
				push_error("There is not valid login session.")
			ENUMS.DELETE_PLAYER_DOCUMENT_RESPONSE_CODE.DOESNT_EXIST:
				push_error("The specified path does not exist.")
      

Retrieving

To retrieve data from an account, you can use GDSync.get_player_document(path : String). This function returns a dictionary in the format on the right. The response code can be any value from  ENUMS.GET_PLAYER_DOCUMENT_RESPONSE_CODE .

You can also check if a collection or document exists using GDSync.has_player_document(path : String). This function returns a dictionary in the format on the right. The response code can be any value from  ENUMS.HAS_PLAYER_DOCUMENT_RESPONSE_CODE .

If you want to retrieve a document from a different account, you can use GDSync.get_external_player_document(external_username : String, path : String). This function returns the same result as the regular get. Make sure the document you are trying to retrieve is public.

{
	"Code" : 0,
	"Result" : {*document*}
}
      
{
	"Code" : 0,
	"Result" : true
}
      
func _ready():
	var response : Dictionary = await GDSync.get_player_document("collection1/collection2/document1")
	var response_code : int = response["Code"]
	
	if response_code == ENUMS.GET_PLAYER_DOCUMENT_RESPONSE_CODE.SUCCESS:
		var document : Dictionary = response["Result"]
		
		print("Document retrieved!")
	else:
		match(response_code):
			ENUMS.GET_PLAYER_DOCUMENT_RESPONSE_CODE.NO_RESPONSE_FROM_SERVER:
				push_error("No response from server")
			ENUMS.GET_PLAYER_DOCUMENT_RESPONSE_CODE.DATA_CAP_REACHED:
				push_error("Data transfer cap has been reached.")
			ENUMS.GET_PLAYER_DOCUMENT_RESPONSE_CODE.RATE_LIMIT_EXCEEDED:
				push_error("Rate limit exceeded, please wait and try again.")
			ENUMS.GET_PLAYER_DOCUMENT_RESPONSE_CODE.NO_DATABASE:
				push_error("API key has no linked database.")
			ENUMS.GET_PLAYER_DOCUMENT_RESPONSE_CODE.NOT_LOGGED_IN:
				push_error("There is not valid login session.")
			ENUMS.GET_PLAYER_DOCUMENT_RESPONSE_CODE.DOESNT_EXIST:
				push_error("The specified path does not exist.")
      

Browsing

Collections may be browsed using GDSync.browse_player_collection(path : String). This function returns a dictionary in the format on the right. The response code can be any value from  ENUMS.BROWSE_PLAYER_COLLECTION_RESPONSE_CODE .

{
	"Code" : 0,
	"Result" :
		[
			{"ExternallyVisible": true, "Name": "profile", "Path": "saves/profile", "Type": "Document"},
			{"ExternallyVisible": false, "Name": "save1", "Path": "saves/save1", "Type": "Document"},
			{"ExternallyVisible": false, "Name": "save2", "Path": "saves/save2", "Type": "Document"},
			{"ExternallyVisible": false, "Name": "configs", "Path": "saves/configs", "Type": "Collection"}
		]
}
      
func _ready():
	var response : Dictionary = await GDSync.browse_player_collection("collection1")
	var response_code : int = response["Code"]
	
	if response_code == ENUMS.BROWSE_PLAYER_COLLECTION_RESPONSE_CODE.SUCCESS:
		var results : Array = response["Result"]
		
		print("Collection browsed!")
	else:
		match(response_code):
			ENUMS.BROWSE_PLAYER_COLLECTION_RESPONSE_CODE.NO_RESPONSE_FROM_SERVER:
				push_error("No response from server")
			ENUMS.BROWSE_PLAYER_COLLECTION_RESPONSE_CODE.DATA_CAP_REACHED:
				push_error("Data transfer cap has been reached.")
			ENUMS.BROWSE_PLAYER_COLLECTION_RESPONSE_CODE.RATE_LIMIT_EXCEEDED:
				push_error("Rate limit exceeded, please wait and try again.")
			ENUMS.BROWSE_PLAYER_COLLECTION_RESPONSE_CODE.NO_DATABASE:
				push_error("API key has no linked database.")
			ENUMS.BROWSE_PLAYER_COLLECTION_RESPONSE_CODE.NOT_LOGGED_IN:
				push_error("There is not valid login session.")
			ENUMS.BROWSE_PLAYER_COLLECTION_RESPONSE_CODE.DOESNT_EXIST:
				push_error("The specified path does not exist.")
      

Leaderboards

GD-Sync comes with a built-in leaderboard system. This allows users to submit their scores to global leaderboards to compete with other players.

In order to make use of leaderboards, you must first create a database on the control panel. Once a database has been created, make sure to go to your API key and link it.

Leaderboards may be created on the control panel. Only users that are logged-in may submit scores to a leaderboard. A user can have one score submission per leaderboard.

Scores

To submit a score to a leaderboard, you can useGDSync.submit_score(leaderboard : String, score : int). This function returns a response code which can be any value from ENUMS.SUBMIT_SCORE_RESPONSE_CODE .

In order for a score to be submitted, there must be a valid login session.

To delete score from a leaderboard, you can use GDSync.delete_score(leaderboard : String). This function returns a response code which can be any value from ENUMS.DELETE_SCORE_RESPONSE_CODE. Scores may also be deleted from the control panel.

func _ready():
	var response_code : int = await GDSync.submit_score("Leaderboard1", 100)
	
	if response_code == ENUMS.SUBMIT_SCORE_RESPONSE_CODE.SUCCESS:
		print("Score submitted!")
	else:
		match(response_code):
			ENUMS.SUBMIT_SCORE_RESPONSE_CODE.NO_RESPONSE_FROM_SERVER:
				push_error("No response from server")
			ENUMS.SUBMIT_SCORE_RESPONSE_CODE.DATA_CAP_REACHED:
				push_error("Data transfer cap has been reached.")
			ENUMS.SUBMIT_SCORE_RESPONSE_CODE.RATE_LIMIT_EXCEEDED:
				push_error("Rate limit exceeded, please wait and try again.")
			ENUMS.SUBMIT_SCORE_RESPONSE_CODE.NO_DATABASE:
				push_error("API key has no linked database.")
			ENUMS.SUBMIT_SCORE_RESPONSE_CODE.NOT_LOGGED_IN:
				push_error("There is not valid login session.")
			ENUMS.SUBMIT_SCORE_RESPONSE_CODE.STORAGE_FULL:
				push_error("The database is full.")
			ENUMS.SUBMIT_SCORE_RESPONSE_CODE.LEADERBOARD_DOESNT_EXIST:
				push_error("The leaderboard does not exist.")
      

Browsing

Leaderboards and their submitted scores can be browsed usingGDSync.browse_leaderboard(leaderboard : String, page_size : int, page : int). This function returns a dictionary in the format on the right. The response code can be any value from  ENUMS.BROWSE_LEADERBOARD_RESPONSE_CODE .

GD-Sync comes with a built-in template that showcases how to make a leaderboard browser.

You can also retrieve the score and rank of an individual user using GDSync.get_leaderboard_score(leaderboard : String, username : String). This function returns a dictionary in the format on the right. The response code can be any value from  ENUMS.GET_LEADERBOARD_SCORE_RESPONSE_CODE .

{
	"Code" : 0,
	"FinalPage": 7,
	"Result" : 
		[
			{"Rank": 1, "Score": 828, "Username": "User1"},
			{"Rank": 2, "Score": 700, "Username": "User2"},
			{"Rank": 3, "Score": 10, "Username": "User3"}
		]
}
      
{
	"Code" : 0,
	"Result" : 
		{
			"Score" : 100,
			"Rank" : 1
		}
}
      

Steam Integration

GD-Sync comes with built-in Steam integration. When the GodotSteam plugin is installed, GD-Sync will automatically detected it and enable extra features.

When using Steam integration, please make sure to properly initialize GodotSteam first.

Joining Friends

Steam friends may seamlessly join each other when one of them is in a lobby

If a player is inside a GD-Sync lobby it is automatically detected by Steam. Their friends may simply right click their name on Steam and press join. Once the join button inside Steam is pressed the signal GDSync.steam_join_request(lobby_name : String, has_password : bool) is emitted.

Once this is emitted you can either directly join the lobby if it has no password, or switch to a screen where a password can be entered.

func _ready() -> void:
	GDSync.steam_join_request.connect(steam_join_request)

#Player pressed join to play with a friend inside Steam
func steam_join_request(lobby_name : String, has_password : bool) -> void:
	if !has_password:
		#Directly join the lobby
		#GDSync.join_lobby(lobby_name)
	else:
		#A password has to be entered first
      

Account Linking

GD-Sync accounts and Steam accounts may be linked together. When linked, it allows players to log into their GD-Sync account using their active Steam session. Doing this skips entering an email and password entirely.

To link a GD-Sync account with Steam, make sure you are logged into the GD-Sync account first. Once logged in, you can link your Steam account usingGDSync.link_steam_account(). This function returns a response code which can be any value from ENUMS.LINK_STEAM_ACCOUNT_RESPONSE_CODE.

After linking your Steam account, you can log into your GD-Sync account using GDSync.steam_login(valid_time : float). This function returns a response code which can be any value from ENUMS.STEAM_LOGIN_RESPONSE_CODE. This function works practically the same as the normal login function.

To remove the link between the GD-Sync and Steam account, use GDSync.unlink_steam_account(). This function returns a response code which can be any value from ENUMS.UNLINK_STEAM_ACCOUNT_RESPONSE_CODE.

Critical Errors

The GD-Sync plugin has some limitations which are in place to protect the servers from malicious attacks. If these limits are exceeded a critical error will be returned and printed in the error logs of the console.

These limits include a 2048 byte limit on lobby data and tags and player data. It also limits the maximum packet size to 20480 bytes. This means that if you exceed this limit per packet you need to adjust your code. This can either be caused by too many PropertySynchronisers or remote calls that are too large.

All requests related to accounts, leaderboards or persistent data storage are rate limited. There are two separate limits:
- 5 requests per second
- 60 requests per minute

Have a question for us?

Send us a message and we'll be in touch.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.