### Run server using start script Source: https://mirror-networking.gitbook.io/docs/hosting/server-hosting/google-cloud Execute the previously created start script to launch the server. ```bash ./start.sh ``` -------------------------------- ### SyncDictionary Implementation Example Source: https://mirror-networking.gitbook.io/docs/manual/guides/synchronization/syncdictionary A complete example showing how to define a SyncDictionary, populate it on the server, and handle synchronization events on the client. ```csharp using System.Collections.Generic; using UnityEngine; using Mirror; public enum Slots : byte { head, body, feet, hands } public struct Item { public string name; public int hitPoints; public int durability; public Item(string name, int hitPoints, int durability) { this.name = name; this.hitPoints = hitPoints; this.durability = durability; } public override string ToString() { return $"name={name} hitPoints={hitPoints} durability={durability}"; } } public class SyncDictionaryExample : NetworkBehaviour { public readonly SyncDictionary Equipment = new SyncDictionary(); public override void OnStartServer() { Equipment[Slots.head] = new Item("Helmet", 10, 20); Equipment[Slots.body] = new Item("Epic Armor", 50, 50); Equipment[Slots.feet] = new Item("Sneakers", 3, 40); Equipment[Slots.hands] = new Item("Sword", 30, 15); } public override void OnStartClient() { // Add handlers for SyncDictionary Actions Equipment.OnAdd += OnItemAdded; Equipment.OnSet += OnItemChanged; Equipment.OnRemove += OnItemRemoved; Equipment.OnClear += OnDictionaryCleared; // OnChange is a catch-all event that is called for any change // to the dictionary. It is called after the specific events above. // Strongly recommended to use the specific events above instead! Equipment.OnChange += OnDictionaryChanged; // Dictionary is populated before handlers are wired up so we // need to manually invoke OnAdd for each element. foreach (Slots key in Equipment.Keys) Equipment.OnAdd.Invoke(key); } public override void OnStopClient() { // Remove handlers when client stops Equipment.OnAdd -= OnItemAdded; Equipment.OnSet -= OnItemChanged; Equipment.OnRemove -= OnItemRemoved; Equipment.OnClear -= OnDictionaryCleared; Equipment.OnChange -= OnDictionaryChanged; } void OnItemAdded(Slots key) { Debug.Log($"Element added {key} {Equipment[key]}"); } void OnItemChanged(Slots key, Item oldValue) { Debug.Log($"Element changed {key} from {oldValue} to {Equipment[key]}"); } void OnItemRemoved(Slots key, Item oldValue) { Debug.Log($"Element removed {key} {oldValue}"); } void OnDictionaryCleared() { // OnDictionaryCleared is called before the dictionary is actually cleared // so we can iterate the dictionary to get the elements if needed. foreach (KeyValuePair kvp in Equipment) Debug.Log($"Element cleared {kvp.Key} {kvp.Value}"); } // OnDictionaryChanged is a catch-all event that is called for any change // to the dictionary. It is called after the specific events above. // // NOTE: It's strongly recommended to use the specific events above instead! // // For OP_ADD, the value param is the NEW entry. // For OP_SET, the value param is the OLD entry. // For OP_REMOVE, the value param is the OLD entry. // For OP_CLEAR, the value param is null / default. void OnDictionaryChanged(SyncDictionary.Operation op, Slots key, Item value) { switch (op) { case SyncDictionary.Operation.OP_ADD: // value is the new entry Debug.Log($"Element added {key} {value}"); break; case SyncDictionary.Operation.OP_SET: // value is the old entry Debug.Log($"Element set {key} from {value} to {Equipment[key]}"); break; case SyncDictionary.Operation.OP_REMOVE: // value is the old entry Debug.Log($"Element removed {key} {value}"); break; case SyncDictionary.Operation.OP_CLEAR: // value is null / default // we can iterate the dictionary to get the elements if needed. foreach (KeyValuePair kvp in Equipment) Debug.Log($"Element cleared {kvp.Key} {kvp.Value}"); break; } } } ``` -------------------------------- ### SSH connection example Source: https://mirror-networking.gitbook.io/docs/hosting/server-hosting/oracle-free-tier An example of the SSH command with a local file path and IP address. ```bash ssh -i c:\users\user\desktop\ssh-key-2021-09-15.key ubuntu@0.0.0.0 ``` -------------------------------- ### SyncHashSet Usage Example Source: https://mirror-networking.gitbook.io/docs/manual/guides/synchronization/synchashset Example demonstrating how to declare, initialize, and handle SyncHashSet events in a NetworkBehaviour. ```APIDOC ## SyncHashSet Usage ### Description Demonstrates how to use SyncHashSet in a Mirror NetworkBehaviour, including event subscription and handling. ### Usage - Declare SyncHashSet as readonly and initialize in constructor. - Wire up event handlers in OnStartClient. - Remove handlers in OnStopClient. - Manually invoke OnAdd for initial data. ### Code Example ```csharp using UnityEngine; using Mirror; public class SyncHashSetExample : NetworkBehaviour { public readonly SyncHashSet namesHashSet = new SyncHashSet(); public override void OnStartClient() { // Add handlers for SyncHashSet Actions namesHashSet.OnAdd += OnItemAdded; namesHashSet.OnRemove += OnItemRemoved; namesHashSet.OnClear += OnHashSetCleared; // OnChange is a catch-all event that is called for any change // to the hashset. It is called after the specific events above. // Strongly recommended to use the specific events above instead! namesHashSet.OnChange += OnHashSetChanged; // Hashset is populated before handlers are wired up so we // need to manually invoke OnAdd for each element. foreach (string value in namesHashSet) namesHashSet.OnAdd.Invoke(value); } public override void OnStopClient() { // Remove handlers when client stops namesHashSet.OnAdd -= OnItemAdded; namesHashSet.OnRemove -= OnItemRemoved; namesHashSet.OnClear -= OnHashSetCleared; namesHashSet.OnChange -= OnHashSetChanged; } void OnItemAdded(string value) { Debug.Log($"Element added {value}"); } void OnItemRemoved(string value) { Debug.Log($"Element removed {value}"); } void OnHashSetCleared() { // OnHashSetCleared is called before the hashset is actually cleared // so we can iterate the hashset to get the elements if needed. foreach (string value in namesHashSet) Debug.Log($"Element cleared {value}"); } // OnHashSetChanged is a catch-all event that is called for any change // to the hashset. It is called after the specific events above. // // NOTE: It's strongly recommended to use the specific events above instead! // // For OP_ADD, the value param is the NEW entry. // For OP_REMOVE, the value param is the OLD entry. // For OP_CLEAR, the value param is null / default. void OnHashSetChanged(SyncHashSet.Operation op, string value) { switch (op) { case SyncHashSet.Operation.OP_ADD: // value is the new entry Debug.Log($"Element added {value}"); break; case SyncHashSet.Operation.OP_REMOVE: // value is the old entry Debug.Log($"Element removed {value}"); break; case SyncHashSet.Operation.OP_CLEAR: // value is null / default // we can iterate the hashset to get the elements if needed. foreach (string name in namesHashSet) Debug.Log($"Element cleared {name}"); break; } } } ``` ### Events - **OnAdd** (Action) - Called when an element is added. - **OnRemove** (Action) - Called when an element is removed. - **OnClear** (Action) - Called before the hashset is cleared. - **OnChange** (Action) - Called for any change, after specific events. Operation can be OP_ADD, OP_REMOVE, or OP_CLEAR. ### Notes - SyncHashSet must be declared readonly and initialized in the constructor. - Handlers are not invoked for initial data; manually invoke OnAdd for each element. - OnChange is a catch-all event; prefer specific events. ``` -------------------------------- ### SyncList Usage Example Source: https://mirror-networking.gitbook.io/docs/manual/guides/synchronization/synclists Example showing how to declare a SyncList, wire up event handlers, and handle list operations in a NetworkBehaviour. ```APIDOC ## SyncList Usage Example ### Description Demonstrates how to use Mirror's SyncList in a NetworkBehaviour, including declaration, initialization, and event handling for list operations. ### Usage - SyncList must be declared **readonly** and initialized in the constructor. - Wire up Action handlers in OnStartClient; the list is already initialized, so handlers are not invoked for initial data. - Remove handlers in OnStopClient. ### Code Example ```csharp using UnityEngine; using Mirror; public class SyncListExample : NetworkBehaviour { public readonly SyncList namesList = new SyncList(); public override void OnStartClient() { // Add handlers for SyncList Actions namesList.OnAdd += OnItemAdded; namesList.OnInsert += OnItemInserted; namesList.OnSet += OnItemChanged; namesList.OnRemove += OnItemRemoved; namesList.OnClear += OnListCleared; // OnChange is a catch-all event that is called for any change // to the list. It is called after the specific events above. // Strongly recommended to use the specific events above instead! namesList.OnChange += OnListChanged; // List is populated before handlers are wired up so we // need to manually invoke OnAdd for each element. for (int i = 0; i < namesList.Count; i++) namesList.OnAdd.Invoke(i); } public override void OnStopClient() { // Remove handlers when client stops namesList.OnAdd -= OnItemAdded; namesList.OnInsert -= OnItemInserted; namesList.OnSet -= OnItemChanged; namesList.OnRemove -= OnItemRemoved; namesList.OnClear -= OnListCleared; namesList.OnChange -= OnListChanged; } void OnItemAdded(int index) { Debug.Log($"Element added at index {index} {namesList[index]}"); } void OnItemInserted(int index) { Debug.Log($"Element inserted at index {index} {namesList[index]}"); } void OnItemChanged(int index, string oldValue) { Debug.Log($"Element changed at index {index} from {oldValue} to {namesList[index]}"); } void OnItemRemoved(int index, string oldValue) { Debug.Log($"Element removed at index {index} {oldValue}"); } void OnListCleared() { // OnListCleared is called before the list is actually cleared // so we can iterate the list to get the elements if needed. foreach (string name in namesList) Debug.Log($"Element cleared {name}"); } // OnListChanged is a catch-all event that is called for any change // to the list. It is called after the specific events above. // // NOTE: It's strongly recommended to use the specific events above instead! // // For OP_ADD and OP_INSERT, the value param is the NEW entry. // For OP_SET and OP_REMOVEAT, the value param is the OLD entry. // For OP_CLEAR, the param is null / default but you can iterate the list. void OnListChanged(SyncList.Operation op, int index, string value) { switch (op) { case SyncList.Operation.OP_ADD: // value is the NEW value of the entry Debug.Log($"Element added at index {index} {value}"); break; case SyncList.Operation.OP_INSERT: // value is the NEW value of the entry Debug.Log($"Element inserted at index {index} {value}"); break; case SyncList.Operation.OP_SET: // value is the OLD value of the entry Debug.Log($"Element changed at index {index} from {value} to {namesList[index]}"); break; case SyncList.Operation.OP_REMOVEAT: // value is the OLD value of the entry Debug.Log($"Element removed at index {index} was {value}"); break; case SyncList.Operation.OP_CLEAR: // value is null / default // we can iterate the list to get the elements if needed. foreach (string name in namesList) Debug.Log($"Element cleared {name}"); break; } } } ``` ### Events - **OnAdd** (Action) - Called when an element is added to the list. - **OnInsert** (Action) - Called when an element is inserted into the list. - **OnSet** (Action) - Called when an element is changed. - **OnRemove** (Action) - Called when an element is removed. - **OnClear** (Action) - Called before the list is cleared. - **OnChange** (Action.Operation, int, string>) - Catch-all event called after the specific events above. For OP_ADD and OP_INSERT, value is the new entry; for OP_SET and OP_REMOVEAT, value is the old entry; for OP_CLEAR, value is null/default. ``` -------------------------------- ### Install screen and unzip Source: https://mirror-networking.gitbook.io/docs/hosting/server-hosting/google-cloud Installs the screen utility for terminal management and unzip for file extraction. ```bash sudo apt-get install -y screen unzip ``` -------------------------------- ### Install Mega client on Debian Source: https://mirror-networking.gitbook.io/docs/hosting/hosting-with-a-remote-desktop Commands to verify the system version and install the Mega client package downloaded via Firefox. ```bash hostnamectl // to see your debian version // next: use firefox to download the Mega client for Debian 10 sudo apt install /home/USERNAME/Downloads/FILENAME.deb open via applications->system->megasync ``` -------------------------------- ### Implement Spawn and UnSpawn handlers Source: https://mirror-networking.gitbook.io/docs/manual/guides/gameobjects/custom-spawnfunctions Example implementation of spawn and unspawn logic for a coin object. ```csharp public GameObject SpawnCoin(SpawnMessage msg) { return Instantiate(m_CoinPrefab, msg.position, msg.rotation); } public void UnSpawnCoin(GameObject spawned) { Destroy(spawned); } ``` -------------------------------- ### Create and configure start script Source: https://mirror-networking.gitbook.io/docs/hosting/server-hosting/google-cloud Create a shell script to automate the server startup process and make it executable. ```bash echo "screen -d -m -S mirrorServer ./mirror-demo.x86_64 -logfile ~/server.log" > ./start.sh ``` ```bash chmod +x ./start.sh ``` -------------------------------- ### NetworkTime.time Source: https://mirror-networking.gitbook.io/docs/manual/guides/time-sync Gets the synchronized time between client and server. Starts at 0 when the server starts. Returns a double and should not be cast to float. ```APIDOC ## NetworkTime.time ### Description Gets the current synchronized time between client and server. Starts at 0 when the server starts. Returns a double and should not be cast to float. ### Property `double NetworkTime.time` ### Return Value - **double** - The synchronized time in seconds. ### Example ```csharp double now = NetworkTime.time; ``` ``` -------------------------------- ### Get current synchronized time Source: https://mirror-networking.gitbook.io/docs/manual/guides/time-sync Retrieves the current synchronized time as a double. This value is identical on both client and server, starting at 0 when the server starts. ```csharp double now = NetworkTime.time; ``` -------------------------------- ### Run server build Source: https://mirror-networking.gitbook.io/docs/hosting/server-hosting/oracle-free-tier Commands to connect to the server directory, set execution permissions, and start the Unity server build. ```bash ssh -i c:\users\user\desktop\ssh-key-2021-09-15.key ubuntu@0.0.0.0 cd server chmod +x ./[your_project_name].x86_64 ./[your_project_name].x86_64 ``` -------------------------------- ### Implementing a Basic Command Source: https://mirror-networking.gitbook.io/docs/manual/guides/communications/remote-actions This example demonstrates how to define a Command that spawns a cube on the server when triggered by the local player on the client. The CmdDropCube method is invoked only when the 'X' key is pressed. ```csharp public class Player : NetworkBehaviour { // assigned in inspector public GameObject cubePrefab; void Update() { if (!isLocalPlayer) return; if (Input.GetKey(KeyCode.X)) CmdDropCube(); } [Command] void CmdDropCube() { if (cubePrefab != null) { Vector3 spawnPos = transform.position + transform.forward * 2; Quaternion spawnRot = transform.rotation; GameObject cube = Instantiate(cubePrefab, spawnPos, spawnRot); NetworkServer.Spawn(cube); } } } ``` -------------------------------- ### SyncList implementation example Source: https://mirror-networking.gitbook.io/docs/manual/guides/synchronization/synclists Demonstrates declaring a SyncList, wiring up event handlers in OnStartClient, and handling various list operations like Add, Insert, Set, Remove, and Clear. ```csharp using UnityEngine; using Mirror; public class SyncListExample : NetworkBehaviour { public readonly SyncList namesList = new SyncList(); public override void OnStartClient() { // Add handlers for SyncList Actions namesList.OnAdd += OnItemAdded; namesList.OnInsert += OnItemInserted; namesList.OnSet += OnItemChanged; namesList.OnRemove += OnItemRemoved; namesList.OnClear += OnListCleared; // OnChange is a catch-all event that is called for any change // to the list. It is called after the specific events above. // Strongly recommended to use the specific events above instead! namesList.OnChange += OnListChanged; // List is populated before handlers are wired up so we // need to manually invoke OnAdd for each element. for (int i = 0; i < namesList.Count; i++) namesList.OnAdd.Invoke(i); } public override void OnStopClient() { // Remove handlers when client stops namesList.OnAdd -= OnItemAdded; namesList.OnInsert -= OnItemInserted; namesList.OnSet -= OnItemChanged; namesList.OnRemove -= OnItemRemoved; namesList.OnClear -= OnListCleared; namesList.OnChange -= OnListChanged; } void OnItemAdded(int index) { Debug.Log($"Element added at index {index} {namesList[index]}"); } void OnItemInserted(int index) { Debug.Log($"Element inserted at index {index} {namesList[index]}"); } void OnItemChanged(int index, string oldValue) { Debug.Log($"Element changed at index {index} from {oldValue} to {namesList[index]}"); } void OnItemRemoved(int index, string oldValue) { Debug.Log($"Element removed at index {index} {oldValue}"); } void OnListCleared() { // OnListCleared is called before the list is actually cleared // so we can iterate the list to get the elements if needed. foreach (string name in namesList) Debug.Log($"Element cleared {name}"); } // OnListChanged is a catch-all event that is called for any change // to the list. It is called after the specific events above. // // NOTE: It's strongly recommended to use the specific events above instead! // // For OP_ADD and OP_INSERT, the value param is the NEW entry. // For OP_SET and OP_REMOVEAT, the value param is the OLD entry. // For OP_CLEAR, the param is null / default but you can iterate the list. void OnListChanged(SyncList.Operation op, int index, string value) { switch (op) { case SyncList.Operation.OP_ADD: // value is the NEW value of the entry Debug.Log($"Element added at index {index} {value}"); break; case SyncList.Operation.OP_INSERT: // value is the NEW value of the entry Debug.Log($"Element inserted at index {index} {value}"); break; case SyncList.Operation.OP_SET: // value is the OLD value of the entry Debug.Log($"Element changed at index {index} from {value} to {namesList[index]}"); break; case SyncList.Operation.OP_REMOVEAT: // value is the OLD value of the entry Debug.Log($"Element removed at index {index} was {value}"); break; case SyncList.Operation.OP_CLEAR: // value is null / default // we can iterate the list to get the elements if needed. foreach (string name in namesList) Debug.Log($"Element cleared {name}"); break; } } } ``` -------------------------------- ### NetworkBehaviour Callback Override Example Source: https://mirror-networking.gitbook.io/docs/manual/components/networkbehaviour Example showing how to override OnStartServer and OnStartClient in a custom NetworkBehaviour class. ```APIDOC ## NetworkBehaviour Callbacks ### Description Built-in virtual callback functions invoked on NetworkBehaviour scripts for various network events. These can be overridden in user code. ### Methods #### OnStartServer() Called on server when a game object spawns on the server, or when the server is started for game objects in the Scene. #### OnStopServer() Called on server when a game object is destroyed on the server, or when the server is stopped for game objects in the Scene. #### OnStartClient() Called on clients when the game object spawns on the client, or when the client connects to a server for game objects in the Scene. #### OnStopClient() Called on clients when the server destroys the game object. #### OnStartLocalPlayer() Called on clients after OnStartClient for the player game object on the local client. #### OnStopLocalPlayer() Called on clients before OnStopClient for the player game object on the local client. #### OnStartAuthority() Called on owner client when assigned authority by the server. isOwned will be true for such objects in client context. #### OnStopAuthority() Called on owner client when authority is removed by the server. ### Example ```csharp public class SpaceShip : NetworkBehaviour { public override void OnStartServer() { // disable client stuff } public override void OnStartClient() { // register client events, enable effects } } ``` ### Notes In a peer-hosted setup, when one of the clients is acting as both host and client, both OnStartServer and OnStartClient are called on the same game object. These functions are useful for actions specific to either the client or server, such as suppressing effects on a server, or setting up client-side events. ``` -------------------------------- ### Make server executable Source: https://mirror-networking.gitbook.io/docs/manual/transports/websockets-transport/ssl Set the server binary as executable. ```bash chmod +x demo_server.x86_64 ``` -------------------------------- ### CanvasHUD.cs starting template Source: https://mirror-networking.gitbook.io/docs/community-guides/unity-canvas-hud-guide The initial script for the CanvasHUD component, providing a button listener to start a Mirror host session. ```csharp using Mirror; using UnityEngine; using UnityEngine.UI; public class CanvasHUD : MonoBehaviour { public Button buttonHost; public void Start() { buttonHost.onClick.AddListener(ButtonHost); } public void ButtonHost() { NetworkManager.singleton.StartHost(); } } ``` -------------------------------- ### SyncHashSet Implementation Example Source: https://mirror-networking.gitbook.io/docs/manual/guides/synchronization/synchashset Demonstrates how to declare, initialize, and handle events for a SyncHashSet within a NetworkBehaviour. Note that handlers are wired up in OnStartClient, and manual invocation is required to process initial data. ```csharp using UnityEngine; using Mirror; public class SyncHashSetExample : NetworkBehaviour { public readonly SyncHashSet namesHashSet = new SyncHashSet(); public override void OnStartClient() { // Add handlers for SyncHashSet Actions namesHashSet.OnAdd += OnItemAdded; namesHashSet.OnRemove += OnItemRemoved; namesHashSet.OnClear += OnHashSetCleared; // OnChange is a catch-all event that is called for any change // to the hashset. It is called after the specific events above. // Strongly recommended to use the specific events above instead! namesHashSet.OnChange += OnHashSetChanged; // Hashset is populated before handlers are wired up so we // need to manually invoke OnAdd for each element. foreach (string value in namesHashSet) namesHashSet.OnAdd.Invoke(value); } public override void OnStopClient() { // Remove handlers when client stops namesHashSet.OnAdd -= OnItemAdded; namesHashSet.OnRemove -= OnItemRemoved; namesHashSet.OnClear -= OnHashSetCleared; namesHashSet.OnChange -= OnHashSetChanged; } void OnItemAdded(string value) { Debug.Log($"Element added {value}"); } void OnItemRemoved(string value) { Debug.Log($"Element removed {value}"); } void OnHashSetCleared() { // OnHashSetCleared is called before the hashset is actually cleared // so we can iterate the hashset to get the elements if needed. foreach (string value in namesHashSet) Debug.Log($"Element cleared {value}"); } // OnHashSetChanged is a catch-all event that is called for any change // to the hashset. It is called after the specific events above. // // NOTE: It's strongly recommended to use the specific events above instead! // // For OP_ADD, the value param is the NEW entry. // For OP_REMOVE, the value param is the OLD entry. // For OP_CLEAR, the value param is null / default. void OnHashSetChanged(SyncHashSet.Operation op, string value) { switch (op) { case SyncHashSet.Operation.OP_ADD: // value is the new entry Debug.Log($"Element added {value}"); break; case SyncHashSet.Operation.OP_REMOVE: // value is the old entry Debug.Log($"Element removed {value}"); break; case SyncHashSet.Operation.OP_CLEAR: // value is null / default // we can iterate the hashset to get the elements if needed. foreach (string name in namesHashSet) Debug.Log($"Element cleared {name}"); break; } } } ``` -------------------------------- ### Polymorphic data type limitation example Source: https://mirror-networking.gitbook.io/docs/guides/data-types This example demonstrates that Mirror does not automatically handle polymorphic serialization for inherited classes, which will result in unexpected behavior. ```csharp class Item { public string name; } class Weapon : Item { public int hitPoints; } class Armor : Item { public int hitPoints; public int level; } class Player : NetworkBehaviour { [Command] void CmdEquip(Item item) { // IMPORTANT: this does not work. Mirror will pass you an object of type item // even if you pass a weapon or an armor. if (item is Weapon weapon) { // The item is a weapon, // maybe you need to equip it in the hand } else if (item is Armor armor) { // you might want to equip armor in the body } } [Command] void CmdEquipArmor(Armor armor) { // IMPORTANT: this does not work either, you will receive an armor, but // the armor will not have a valid Item.name, even if you passed an armor with name } } ``` -------------------------------- ### Configure Ubuntu firewall rules Source: https://mirror-networking.gitbook.io/docs/hosting/server-hosting/oracle-free-tier Commands to install firewalld and open the necessary ports for your server on the Ubuntu instance. ```bash sudo apt install firewalld sudo firewall-cmd --zone=public --add-port=7777/tcp --permanent sudo firewall-cmd --zone=public --add-port=7777/udp --permanent sudo firewall-cmd --reload ```