### Example .Net Runner Command Line Execution Source: https://doc.photonengine.com/quantum/current/manual/replay An example command line to start the .Net Quantum runner, specifying paths for the replay and LUT files. ```bash Quantum.Dotnet\Quantum.Runner.Dotnet\bin\Debug>.\Quantum.Runner.exe --replay-path ..\..\..\..\Assets\QuantumUser\Replays\MapTestNavMeshAgents-2024-06-17-14-19-46.json --lut-path ..\..\..\..\Assets\Photon\Quantum\Resources\LUT ``` -------------------------------- ### Start Quantum Game with Replay Arguments Source: https://doc.photonengine.com/quantum/current/manual/replay Starts the Quantum game session using the configured `SessionRunner.Arguments` for a replay. ```csharp _runner = QuantumRunner.StartGame(arguments); ``` -------------------------------- ### Quantum Reconnection GUI Example Source: https://doc.photonengine.com/quantum/current/manual/game-session/reconnecting This C# script demonstrates connecting to a Quantum cloud, starting a game session, and handling disconnections and reconnections. Ensure RuntimeConfig, RuntimePlayers are set and QuantumStats prefab is added. Press 'Connect' to start, 'Disconnect' to stop, then 'Reconnect' to resume an ongoing session. ```csharp namespace Quantum.Demo { using System; using System.Collections.Generic; using Photon.Deterministic; using Photon.Realtime; using UnityEngine; using UnityEngine.SceneManagement; /// /// A Unity script that demonstrates how to connect to a Quantum cloud and start a Quantum game session. /// public class QuantumSimpleReconnectionGUI : QuantumMonoBehaviour { /// /// The RuntimeConfig to use for the Quantum game session. The RuntimeConfig describes custom game properties. /// public RuntimeConfig RuntimeConfig; /// /// The RuntimePlayers to add to the Quantum game session. The RuntimePlayers describe individual custom player properties. /// public List RuntimePlayers; /// /// Room keep alive time. /// public int EmptyRoomTtlInSeconds = 20; RealtimeClient _client; string _loadedScene; QuantumReconnectInformation _reconnectInformation; int _disconnectedTick; byte[] _disconnectedFrame; bool CanReconnect => _reconnectInformation != null && _reconnectInformation.HasTimedOut == false; async void OnGUI() { if (_client != null && _client.IsConnectedAndReady) { if (GUI.Button(new Rect(10, 60, 160, 40), "Disconnect")) { await Stop(); } } else { if (GUI.Button(new Rect(10, 60, 160, 40), CanReconnect ? "Reconnect" : "Connect")) { await Run(); } } } async System.Threading.Tasks.Task Run() { var connectionArguments = new MatchmakingArguments { PhotonSettings = PhotonServerSettings.Global.AppSettings, PluginName = "QuantumPlugin", MaxPlayers = Quantum.Input.MAX_COUNT, // Keep the client connection object, it has cached authentication information NetworkClient = _client, // Keep an empty room open for a time EmptyRoomTtlInSeconds = EmptyRoomTtlInSeconds, // Set the stored reconnection information ReconnectInformation = _reconnectInformation, // Don't let random matchmaking get into this room IsRoomVisible = false }; if (CanReconnect) { // Switch to reconnecting mode _client = await MatchmakingExtensions.ReconnectToRoomAsync(connectionArguments); } else { _client = await MatchmakingExtensions.ConnectToRoomAsync(connectionArguments); // Remove the disconnect information, it would break a new room _disconnectedTick = 0; _disconnectedFrame = null; } // Load the map if AutoLoadSceneFromMap is not set if (QuantumUnityDB.TryGetGlobalAsset(RuntimeConfig.SimulationConfig, out Quantum.SimulationConfig simulationConfigAsset) && simulationConfigAsset.AutoLoadSceneFromMap == SimulationConfig.AutoLoadSceneFromMapMode.Disabled) { if (QuantumUnityDB.TryGetGlobalAsset(RuntimeConfig.Map, out Quantum.Map map) == false) { throw new Exception("Map not found"); } using (new ConnectionServiceScope(_client)) { await SceneManager.LoadSceneAsync(map.Scene, LoadSceneMode.Additive); SceneManager.SetActiveScene(SceneManager.GetSceneByName(map.Scene)); _loadedScene = map.Scene; } } var sessionRunnerArguments = new SessionRunner.Arguments { RunnerFactory = QuantumRunnerUnityFactory.DefaultFactory, GameParameters = QuantumRunnerUnityFactory.CreateGameParameters, ClientId = _client.UserId, RuntimeConfig = new QuantumUnityJsonSerializer().CloneConfig(RuntimeConfig), SessionConfig = QuantumDeterministicSessionConfigAsset.DefaultConfig, GameMode = DeterministicGameMode.Multiplayer, PlayerCount = Quantum.Input.MAX_COUNT, Communicator = new QuantumNetworkCommunicator(_client), // Set the initial tick InitialTick = _disconnectedTick, // Set the serialized frame FrameData = _disconnectedFrame }; // Add a player to the game var runner = (QuantumRunner)await SessionRunner.StartAsync(sessionRunnerArguments); for (int i = 0; i < RuntimePlayers.Count; i++) { runner.Game.AddPlayer(i, RuntimePlayers[i]); } } async System.Threading.Tasks.Task Stop() { // Save the serialized frame _disconnectedTick = QuantumRunner.DefaultGame.Frames.Verified.Number; ``` -------------------------------- ### Adding Runtime Static Assets Source: https://doc.photonengine.com/quantum/current/manual/assets/assets-simulation Provides an example of adding a new asset to the database at runtime before the simulation starts. Ensures a deterministic GUID is generated for consistency. ```csharp // create any asset var assetObject = AssetObject.Create(); // set its name assetObject.name = "My Unique Asset Object Name"; // get a deterministic GUID var guid = QuantumUnityDB.CreateRuntimeDeterministicGuid(assetObject); // add the asset to the asset database QuantumUnityDB.Global.AddAsset(assetObject); // set the GUID assetObject.Guid = guid; ``` -------------------------------- ### Programmatically Disable System Start Source: https://doc.photonengine.com/quantum/current/manual/quantum-ecs/systems Example of how to configure a system to start in a disabled state by overriding the `StartEnabled` property. ```csharp public override bool StartEnabled => false; ``` -------------------------------- ### Start Quantum Game Session Async Source: https://doc.photonengine.com/quantum/current/getting-started/whats-new Initiate a Quantum game session asynchronously using SessionRunner.StartAsync. This method requires a configured SessionRunner.Arguments object, including the communicator and game parameters. Awaiting the start resumes when the Quantum start protocol has finished and snapshots are received. ```csharp SessionRunner.Arguments sessionRunnerArguments = new SessionRunner.Arguments { RunnerFactory = QuantumRunnerUnityFactory.DefaultFactory, GameParameters = QuantumRunnerUnityFactory.CreateGameParameters, ClientId = client.UserId, RuntimeConfig = runtimeConfig, SessionConfig = QuantumDeterministicSessionConfigAsset.DefaultConfig, GameMode = DeterministicGameMode.Multiplayer, PlayerCount = 8, StartGameTimeoutInSeconds = 10, Communicator = new QuantumNetworkCommunicator(client), }; QuantumRunner runner = (QuantumRunner)await SessionRunner.StartAsync(sessionRunnerArguments); ``` -------------------------------- ### ReplayStart Request JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks An example JSON payload for the ReplayStart webhook request. This includes game session details. ```json { "AppId": "d1f67eec-51fb-45c1", "AppVersion": "1.0-live", "Region": "eu", "Cloud": "0:", "RoomName": "1.2-party-2349535735", "GameId": "0:eu:e472a861-a1e2-49f7", "SessionConfig": { }, "RuntimeConfig": "H4sIAAAAAAAACnWNPQvCMBCG/4ocjkWuye X6sXZycNCCe8AogSYtNBlK6X/3UHQR4Zb35X2eW2GflslBC+ds Y8rhcMkx+eC6Md79o9h96t6HPNjkxwgF9M7doMUCTnaCdoWjpB WudshiUkxYsVHacE11KWe2TZiv4K3+4YjQkECKNJcVMuoXtszJ hfkPI1tUVc1sqFGN1g3Kr+0J+3ktedUAAAA=" } ``` -------------------------------- ### Sample System for HFSM Initialization and Update Source: https://doc.photonengine.com/quantum/current/addons/bot-sdk/hfsm A complete system example demonstrating how to initialize and update HFSMAgent components for entities. ```csharp namespace Quantum { public unsafe class AISystem : SystemMainThreadFilter, ISignalOnComponentAdded { public struct Filter { public EntityRef Entity; public HFSMAgent* HFSMAgent; } public void OnAdded(Frame frame, EntityRef entity, HFSMAgent* component) { HFSMRoot hfsmRoot = frame.FindAsset(component->Data.Root.Id); HFSMManager.Init(frame, entity, hfsmRoot); } public override void Update(Frame frame, ref Filter filter) { HFSMManager.Update(frame, frame.DeltaTime, filter.Entity); } } } ``` -------------------------------- ### Add Player on Game Start Callback Source: https://doc.photonengine.com/quantum/current/manual/player/player Register to the CallbackGameStarted event to add players once the game has started. This example demonstrates adding multiple players. ```csharp public class QuantumAddRuntimePlayers : QuantumMonoBehaviour { public RuntimePlayer[] Players; public void Awake() { QuantumCallback.Subscribe(this, (CallbackGameStarted c) => OnGameStarted(c.Game, c.IsResync), game => game == QuantumRunner.Default.Game); } public void OnGameStarted(QuantumGame game, bool isResync) { for (int i = 0; i < Players.Length; i++) { game.AddPlayer(i, Players[i]); } } } ``` -------------------------------- ### Load and Start Quantum Game from Snapshot Source: https://doc.photonengine.com/quantum/current/manual/game-session/starting-from-snapshot Connects to a Photon room with a unique, non-visible name, loads snapshot data from a file, and starts a new Quantum game session. It includes steps for adding players and handling confirmations, and logs an invitation message for other players. ```csharp using Photon.Deterministic; using Photon.Realtime; using Quantum; using System; using System.IO; using UnityEngine; public class Foo : QuantumMonoBehaviour { [EditorButton("LoadAndStart")] public async void LoadAndStart() { // Delete the debug runner if it exists, it interferes with adding players. var debugRunner = FindAnyObjectByType(); if (debugRunner != null) { Destroy(debugRunner); } // Connect to a room with a secret room name. var arguments = new MatchmakingArguments { PhotonSettings = PhotonServerSettings.Global.AppSettings, MaxPlayers = Quantum.Input.MAX_COUNT, // This name has be unique and must be shared with the other players that are expected to join this room RoomName = "my secret room name", PluginName = "QuantumPlugin", AuthValues = new AuthenticationValues(), // Don't use for random matchmaking IsRoomVisible = false }; var client = await MatchmakingExtensions.ConnectToRoomAsync(arguments); // Load the snapshot from file, or just from a member variable. byte[] snapshot = default; if (File.Exists(Path.Combine(Application.dataPath, "savegame.quantum"))) { snapshot = File.ReadAllBytes(Path.Combine(Application.dataPath, "savegame.quantum")); } // Use global session config var sessionConfig = QuantumDeterministicSessionConfigAsset.DefaultConfig; // Set and use custom runtime config instead var runtimeConfig = JsonUtility.FromJson(File.ReadAllText(Path.Combine(Application.dataPath, "config.quantum"))); // Start and wait for the game, if snapshot is set, it will start from that snapshot. // Initial tick does not have to be set explicitly, the simulation will always restart from the beginning. var sessionRunnerArguments = new SessionRunner.Arguments { FrameData = snapshot, RunnerFactory = QuantumRunnerUnityFactory.DefaultFactory, GameParameters = QuantumRunnerUnityFactory.CreateGameParameters, ClientId = "client secret", PlayerCount = Quantum.Input.MaxCount, GameMode = DeterministicGameMode.Multiplayer, RuntimeConfig = runtimeConfig, SessionConfig = sessionConfig, Communicator = new QuantumNetworkCommunicator(client), }; var runner = (QuantumRunner)await SessionRunner.StartAsync(sessionRunnerArguments); // Add player back to the game and wait for the confirmation. var completionSource = new System.Threading.Tasks.TaskCompletionSource(); using (QuantumCallback.SubscribeManual(c => completionSource.TrySetResult(true))) using (QuantumCallback.SubscribeManual(c => completionSource.TrySetException(new Exception(c.Message)))) { runner.Game.AddPlayer(0, new RuntimePlayer()); await completionSource.Task; } // Invite other player to the room Debug.Log($"Invite to room {client.CurrentRoom.Name} on region {client.CurrentRegion}"); } } ``` -------------------------------- ### Start Session and Add Player (Async) Source: https://doc.photonengine.com/quantum/current/manual/player/player Use the async version of SessionRunner.StartAsync to wait for the connection logic to complete before adding a player. ```csharp // this will return once the connection logic is complete (e.g. received snapshot if needed) var runner = (QuantumRunner)await SessionRunner.StartAsync(sessionRunnerArguments); // adding player to the online simulation var runtimePlayer = new RuntimePlayer { PlayerNickname = "whiskeyjack29" }; runner.Game.AddPlayer(runtimePlayer); ``` -------------------------------- ### Initialize Wave Spawning on System Start Source: https://doc.photonengine.com/quantum/current/tutorials/asteroids/6-asteroids Overrides the `OnInit` function to call `SpawnAsteroidWave` when the system is initialized, ensuring the first wave spawns at simulation start. ```csharp public override void OnInit(Frame frame) { SpawnAsteroidWave(frame); } ``` -------------------------------- ### Start Online Quantum Session Source: https://doc.photonengine.com/quantum/current/manual/game-session/starting-session Configure and start an online Quantum session. Ensure `DeterministicGameMode.Multiplayer` is set for online games and provide necessary parameters like `ClientId`, `RuntimeConfig`, and `SessionConfig`. The `StartAsync` method completes when the client successfully joins. ```csharp var sessionRunnerArguments = new SessionRunner.Arguments { // The runner factory is the glue between the Quantum.Runner and Unity RunnerFactory = QuantumRunnerUnityFactory.DefaultFactory, // Creates a default version of `QuantumGameStartParameters` GameParameters = QuantumRunnerUnityFactory.CreateGameParameters, // A secret user id that is for example used to reserved player slots to reconnect into a running session ClientId = Client.UserId, // The player data RuntimeConfig = runtimeConfig, // The session config loaded from the Unity asset tagged as `QuantumDefaultGlobal` SessionConfig = QuantumDeterministicSessionConfigAsset.DefaultConfig, // GameMode has to be multiplayer for online sessions GameMode = DeterministicGameMode.Multiplayer, // The number of player that the session is running for, in this case we use the code-generated max possible players for the Quantum simulation PlayerCount = Input.MAX_COUNT, // A timeout to fail the connection logic and Quantum protocol StartGameTimeoutInSeconds = 10, // The communicator will take over the network handling after the simulation has started Communicator = new QuantumNetworkCommunicator(Client), }; // This method completes when the client has successfully joined the online session QuantumRunner runner = (QuantumRunner)await SessionRunner.StartAsync(sessionRunnerArguments); ``` -------------------------------- ### ReplayStart Response JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks An example JSON payload for the ReplayStart webhook response. Setting 'Skip' to true disables replay streaming for the session. ```json { "Skip": true } ``` -------------------------------- ### OnDeterministicStartSession Source: https://doc.photonengine.com/quantum/current/addons/plugin-sdk/quantum-server-api Is called when the Quantum simulation is started. ```APIDOC ## OnDeterministicStartSession ### Description Is called when the Quantum simulation is started. ### Method Signature ```csharp void OnDeterministicStartSession() ``` ``` -------------------------------- ### FadeTo Transition Example Source: https://doc.photonengine.com/quantum/current/addons/animator/manual Use FadeTo to transition to another state independent of conditions. Ensure AllowFadeToTransitions is enabled. ```csharp // public void FadeTo(Frame frame, AnimatorComponent* animatorComponent, string stateName, bool setIgnoreTransitions, FP deltaTime) //usage example if (input->Run.WasPressed) { graph.FadeTo(frame, filter.AnimatorComponent, "Running", true, 0); } ``` -------------------------------- ### SystemSetup.cs - Old Method Source: https://doc.photonengine.com/quantum/current/getting-started/whats-new This is the old method for creating systems in Quantum 2.1. It is considered deprecated and will log a warning. ```csharp namespace Quantum { public static class SystemSetup { public static SystemBase[] CreateSystems(RuntimeConfig gameConfig, SimulationConfig simulationConfig) { return new SystemBase[] { // .. } } } } ``` -------------------------------- ### System Enable/Disable and Query Example Source: https://doc.photonengine.com/quantum/current/manual/quantum-ecs/systems Demonstrates disabling, re-enabling, and querying the enabled state of a system within the OnInit method. Ensure the system type is correctly specified. ```csharp public override void OnInit(Frame frame) { // deactivates MySystem, so no updates (or signals) are called in it frame.SystemDisable(); // (re)activates MySystem frame.SystemEnable(); // query if any system of type MySystem is currently enabled var enabled = frame.SystemAnyEnabledInHierarchy(); } ``` -------------------------------- ### Toggling Regions Source: https://doc.photonengine.com/quantum/current/manual/navigation/regions Example of how to get a region ID and toggle it off globally or for individual agents. ```APIDOC ## Toggling Regions ### Description Get the region `Foo` and disable it globally or for individual agents. ### Example (Global Toggle) ```csharp var regionId = frame.Map.RegionMap["Foo"]; frame.NavMeshRegionMask->ToggleRegion(regionId, false); ``` ### Example (Agent Specific Toggle) ```csharp var regionId = frame.Map.RegionMap["Foo"]; var agent = f.Unsafe.GetPointer(entity); agent->RegionMask.ToggleRegion(regionId, false); ``` ``` -------------------------------- ### DeterministicSystemSetup.User.cs - New Method Source: https://doc.photonengine.com/quantum/current/getting-started/whats-new This is the new method for creating systems in Quantum, utilizing a data-driven approach with SystemsConfig. The old SystemSetup.CreateSystems() is deprecated. ```csharp namespace Quantum { using System.Collections.Generic; public static partial class DeterministicSystemSetup { static partial void AddSystemsUser(ICollection systems, RuntimeConfig gameConfig, SimulationConfig simulationConfig, SystemsConfig systemsConfig) { systems.Add(new TestSystemMainThreadGroup("TestSystemsGroup", new SystemMainThread[] { new TestSystemImmediateRemoveDestroy(), })); systems.Add(new TasksTestSystem()); ``` -------------------------------- ### Set Health Component Example Source: https://doc.photonengine.com/quantum/current/manual/quantum-ecs/components This example demonstrates how to get a component, modify its value, and then set the updated component back to the entity. Ensure the Health component and its Value property exist. ```csharp private void SetHealth(Frame frame, EntityRef entity, FP value){ var health = frame.Get(entity); health.Value = value; frame.Set(entity, health); } ``` -------------------------------- ### Replace Runtime Navmesh Before Start Source: https://doc.photonengine.com/quantum/current/manual/navigation/customized-navmesh Procedurally generate BakeData, bake a new navmesh, and then replace an existing NavMeshAsset before the simulation starts. This involves copying GUID and Path, and invalidating the DataAsset to prevent deserialization of the old data. ```csharp // BakeData needs to be procedurally generated var bakeData = default(NavmeshBakeData); var newNavmesh = NavmeshBaker.BakeNavMesh(map, bakeData); // Load navmesh asset to replace var navmeshAsset = UnityEngine.Resources.Load("DB/TestNavMeshAgents/NavMeshToReplace"); // Replace the navmesh content newNavmesh.Guid = navmeshAsset.Settings.Guid; newNavmesh.Path = navmeshAsset.Settings.Path; navmeshAsset.Settings = newNavmesh; // Invalidate the navmesh data (because it has been generated during runtime it's already loaded) navmeshAsset.Settings.DataAsset.Id = AssetGuid.Invalid; navmeshAsset.Settings.Name = "MyNavmesh"; // QuantumRunner.StartGame() ``` -------------------------------- ### Subscribe to Simulation Callbacks Source: https://doc.photonengine.com/quantum/current/addons/plugin-sdk/setup Subscribe to callbacks from the simulation by obtaining the CallbackDispatcher from the DotNetSessionRunner. This example logs a message when the game starts. ```csharp private void SubscribeToCallbacks(DotNetSessionRunner sessionRunner) { var callbackDispatcher = (CallbackDispatcher)sessionRunner.CallbackDispatcher; callbackDispatcher.Subscribe(this, c => Log.Info("Game Started")); } ``` -------------------------------- ### .Net Runner Command Line Help Source: https://doc.photonengine.com/quantum/current/manual/replay Displays the help information for the .Net Quantum runner executable, outlining available command-line options for starting a session. ```bash Quantum.Dotnet\Quantum.Runner.Dotnet\bin\Debug> .\Quantum.Runner.exe --help Description: Main method to start a Quantum runner. Usage: Quantum.Runner [options] Options: --replay-path Path to the Quantum replay json file. --lut-path Path to the LUT folder. --db-path Optionally an extra path to the Quantum database json file. --checksum-path Optionally an extra path to the checksum file. --version Show version information -?, -h, --help Show help and usage information ``` -------------------------------- ### Get Next Waypoint for Agent Movement Source: https://doc.photonengine.com/quantum/current/technical-samples/tilemap-pathfinder Retrieves the next waypoint index from the TilePathfinder component and converts it to a world position using the TileMapData asset. This is essential for guiding agent movement. ```csharp // gets the asset reference var asset = f.GetSingleton().Asset(f); // gets the next point var pathfinder = f.Get(entity); var index = pathfinder.Waypoints[pathfinder.CurrentWaypoint]; var nextPoint = asset.IndexToPosition(index); ``` -------------------------------- ### Initialize DynamicAssetDB with Assets Source: https://doc.photonengine.com/quantum/current/manual/assets/assets-simulation Create and populate a DynamicAssetDB instance with initial assets before starting the simulation. This ensures deterministic asset creation across clients. ```csharp var initialAssets = new DynamicAssetDB(); initialAssets.AddAsset(mageSpec); initialAssets.AddAsset(warriorSpec); ... ``` -------------------------------- ### Generate Deterministic GUID for Runtime Asset Source: https://doc.photonengine.com/quantum/current/technical-samples/brick-builder Use this method to create a deterministic GUID for an asset that is added at runtime. Ensure the asset's name is set before generating the GUID. The generated GUID is then assigned to the asset. ```csharp var assetObject = AssetObject.Create(); assetObject.name = "Generated Map"; var guid = QuantumUnityDB.CreateRuntimeDeterministicGuid(assetObject); QuantumUnityDB.Global.AddAsset(assetObject); assetObject.Guid = guid; ``` -------------------------------- ### Get Player Input by ID Source: https://doc.photonengine.com/quantum/current/tutorials/asteroids/5-player-spawning This snippet shows the original method of getting input specifically for player 0. ```csharp var input = frame.GetPlayerInput(0); ``` -------------------------------- ### Connect to Name Server (C#) Source: https://doc.photonengine.com/quantum/current/reference/regions-quantum3 Call this to fetch the list of available Photon Cloud regions. Implement `OnRegionListReceived` to handle the results. ```csharp loadBalancingClient.ConnectToNameServer() ``` -------------------------------- ### Instant Replays Demo Source: https://doc.photonengine.com/quantum/current/manual/replay Demonstrates how to implement instant replays (like kill-cam replays) using an auxiliary QuantumRunner. ```APIDOC ## Instant Replays We provide a script which demonstrates how to do instant replays, like kill-cam replays which happen during the game, in an auxiliary QuantumRunner, and then gets back to the default runner once the instant replay is over. To use it, just add the Unity component called `QuantumInstantReplayDemo` to a Game Object, do the setup (set playback speed, replay length, etc.) and then, during gameplay, hit the Start and Stop buttons. ``` -------------------------------- ### Starting QuantumRunner with ClientId Source: https://doc.photonengine.com/quantum/current/manual/game-session/reconnecting Pass the ClientId when starting the QuantumRunner. This ClientId is secret and used to identify the client on the server. ```csharp var sessionRunnerArguments = new SessionRunner.Arguments { ClientId = Client.UserId, //Other arguments are needed }; var runner = (QuantumRunner)await SessionRunner.StartAsync(sessionRunnerArguments); ``` -------------------------------- ### Sample System Implementing IKCCCallbacks3D Source: https://doc.photonengine.com/quantum/current/manual/physics/kcc Example of a system that implements the IKCCCallbacks3D interface to handle 3D collisions and demonstrates how to pass the callback object to the KCC Move method. ```APIDOC ## SampleSystem with IKCCCallbacks3D ### Description This sample system demonstrates how to implement the `IKCCCallbacks3D` interface to handle 3D character collisions. It also shows how to pass the system itself as the callback handler to the `CharacterController3D.Move` method. ### Implementation Details - Implements `IKCCCallbacks3D`. - Provides implementations for `OnCharacterCollision3D` and `OnCharacterTrigger3D`. - In the `Update` method, it calls `filter.KCC->Move` passing `this` system as the callback object. ### Code Snippet ```csharp namespace Quantum { using Quantum.Core; using Quantum.Physics3D; public unsafe class SampleSystem : SystemMainThreadFilter, IKCCCallbacks3D { public struct Filter { public EntityRef EntityRef; public CharacterController3D* KCC; } public bool OnCharacterCollision3D(FrameBase f, EntityRef character, Hit3D hit) { // Collision logic here return true; // Or false to ignore collision } public void OnCharacterTrigger2D(FrameBase f, EntityRef character, Hit3D hit) { // Trigger logic here } public override void Update(Frame frame, ref Filter filter) { // Example of calling Move with callbacks filter.KCC->Move(frame, filter.EntityRef, input->Direction, this); } } } ``` ``` -------------------------------- ### Connect to Photon Room with Async Extensions Source: https://doc.photonengine.com/quantum/current/getting-started/whats-new Use MatchmakingExtensions.ConnectToRoomAsync to establish a connection to a Photon room. Ensure network client type is set to ClientAppType.Quantum. All errors are thrown as exceptions and must be handled with try/catch blocks. ```csharp MatchmakingArguments connectionArguments = new MatchmakingArguments { PhotonSettings = PhotonServerSettings.Default.AppSettings, PluginName = "QuantumPlugin", MaxPlayers = 8, UserId = Guid.NewGuid().ToString(), NetworkClient = new RealtimeClient { ClientType = ClientAppType.Quantum } }; RealtimeClient client = await MatchmakingExtensions.ConnectToRoomAsync(connectionArguments); ``` -------------------------------- ### ReplayChunk Request JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks An example JSON payload for the ReplayChunk webhook request. This includes chunk details and input data. ```json { "AppId": "d1f67eec-51fb-45c1", "GameId": ":eu:e472a861-a1e2-49f7", "ChunkNumber": 0, "IsLast": false, "LastTick": 302, "TickCount": 243, "TickCountTotal": 243, "IsCompressed": false, "Input": "JQAAADwAAAAIAAMKFsCUwYDggOB/UCAAAPgfDMhgEoAHA////4PRBwATAAAAPQAAAAgAA2PaSK" } ``` -------------------------------- ### Connect and Join Room Asynchronously Source: https://doc.photonengine.com/quantum/current/manual/game-session/async-extensions Demonstrates establishing a connection and joining a random or creating a room using asynchronous methods. Ensure you have initialized AppSettings and RealtimeClient. ```csharp var appSettings = new new AppSettings(); var client = new RealtimeClient(); await client.ConnectUsingSettingsAsync(appSettings); var joinRandomRoomParams = new JoinRandomRoomArgs(); var enterRoomArgs = new EnterRoomArgs(); var result = await client.JoinRandomOrCreateRoomAsync(joinRandomRoomParams, enterRoomArgs); ``` -------------------------------- ### Initialize AsteroidsShipSystem Source: https://doc.photonengine.com/quantum/current/tutorials/asteroids/4-player This is the basic structure for the AsteroidsShipSystem. Ensure you are using the Photon.Deterministic namespace. ```csharp using Photon.Deterministic; namespace Quantum.Asteroids { public unsafe class AsteroidsShipSystem { } } ``` -------------------------------- ### PlayerRemoved Webhook Request JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the PlayerRemoved webhook. This includes player details and the reason for removal. ```json { "AppId": "d1f67eec-51fb-45c1", "GameId": "0:eu:db757806-8570-45aa", "UserId": "db757806-8570-45aa", "ActorNr": 1, "PlayerSlot": 0, "Player": 21, "Reason": 0, "AuthCookie": { "Secret": "**********" } } ``` -------------------------------- ### PlayerAdded Webhook Request JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the PlayerAdded webhook. This includes details about the player and game session. ```json { "AppId": "d1f67eec-51fb-45c1", "GameId": "0:eu:db757806-8570-45aa", "UserId": "db757806-8570-45aa", "ActorNr": 1, "PlayerSlot": 0, "Player": 21, "AuthCookie": { "Secret": "**********" } } ``` -------------------------------- ### Send HTTP GET Request Asynchronously Source: https://doc.photonengine.com/quantum/current/addons/plugin-sdk/setup Use this to perform an asynchronous HTTP GET request to an external API. Ensure the PluginHost is available. ```csharp HttpExample.SendAsync(host, result => { if (result) { Log.Info("HTTP Asyncronous Response: Success"); } else { Log.Info("HTTP Asyncronous Response: FAILED"); } }); ``` -------------------------------- ### GameResult JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example of a GameResult object serialized to JSON. Includes the '$type' property for deserialization and fields like Frame and Winner. ```json { "$type":"Quantum.GameResult, Quantum.Simulation", "Frame": 200, "Winner": 2 } ``` -------------------------------- ### Initialize Compound Agents and Blackboards (C#) Source: https://doc.photonengine.com/quantum/current/addons/bot-sdk/snippets Initialize HFSM agents and AI Blackboards for compound agents using ISignalOnComponentAdded. Ensure assets are found and initialization methods are called. ```csharp public void OnAdded(Frame frame, EntityRef entity, CompoundAgents* compoundAgents) { // Initialise Agents HFSMRoot hfsmRoot = frame.FindAsset(compoundAgents->MovementHFSMAgent.Data.Root.Id); HFSMManager.Init(frame, &compoundAgents->MovementHFSMAgent.Data, entity, hfsmRoot); hfsmRoot = frame.FindAsset(compoundAgents->AttackHFSMAgent.Data.Root.Id); HFSMManager.Init(frame, &compoundAgents->AttackHFSMAgent.Data, entity, hfsmRoot); // Initialise Blackboards AIBlackboardInitializer initializer = frame.FindAsset(compoundAgents->MovementBBInitializer.Id); AIBlackboardInitializer.InitializeBlackboard(frame, &compoundAgents->MovementBlackboard, initializer); initializer = frame.FindAsset(compoundAgents->AttackBBInitializer.Id); AIBlackboardInitializer.InitializeBlackboard(frame, &compoundAgents->AttackBlackboard, initializer); } ``` -------------------------------- ### Initialize DotNetSessionRunner Source: https://doc.photonengine.com/quantum/current/addons/plugin-sdk/quantum-server-api Set up the `DotNetSessionRunner` for the Quantum server simulation, including the asset serializer. Ensure Quantum dependencies, simulation DLL, and assets are in sync with client builds. ```csharp var sessionRunner = new DotNetSessionRunner { AssetSerializer = new QuantumJsonSerializer() }; ``` -------------------------------- ### RuntimePlayer JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example of a RuntimePlayer object serialized to JSON. It includes the '$type' property for deserialization and fields like Loadout and PlayerNickname. ```json { "$type":"Quantum.RuntimePlayer, Quantum.Simulation", "Loadout": { "Id": { "Value": 440543562436170603 } }, "PlayerAvatar": { "Id": { "Value": 2430278665492933905 } }, "PlayerNickname": "foo" } ``` -------------------------------- ### Initialize Quantum Profiler Source: https://doc.photonengine.com/quantum/current/manual/profiling Initialize the Quantum Host Profiler. This is typically done within the QuantumRunner script. ```csharp Quantum.Profiling.HostProfiler.Init(..) ``` -------------------------------- ### RuntimeConfig JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example of a RuntimeConfig object serialized to JSON. Includes the required '$type' property for deserialization and various configuration fields. ```json { "$type": "Quantum.RuntimeConfig, Quantum.Simulation", "GameMode": 1, "Seed": 0, "Map": { "Id": { "Value":2640765235684814815 } }, "SimulationConfig": { "Id": { "Value": 440543562436170603 } }, "SystemsConfig": { "Id": { "Value": 2430278665492933905 } } } ``` -------------------------------- ### GameResult JSON Payload Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks An example of the JSON payload for the GameResult webhook. Note the required `$type` property for deserialization and that dictionaries are not supported. ```json { "AppId": "d1f67eec-51fb-45c1", "GameId": "0:eu:db757806-8570-45aa", "Results": [ { "Clients": [ { "UserId": "FJEH43FL56FSDR", "ActorNr": 0, "Players": [ 0 ], "GameTime": 63.3636703, "AuthCookie": { "Secret": "**********" } }, { "UserId": "8FMEINF3845BSD", "ActorNr": 3, "Players": [ 1 ], "GameTime": 62.9345634, "AuthCookie": { "Secret": "**********" } } ], "Result": { "$type": "Quantum.GameResult, Quantum.Simulation", "Frame": 12010, "Winner": 0 }, "IsServerResult": false }, { "Clients": [ { "UserId": "LUDHBS7Z3F55", "Players": [ 2 ], "GameTime": 63.1234567 } ], "Result": { "$type": "Quantum.GameResult, Quantum.Simulation", "Frame": 12010, "Winner": 2 }, "IsServerResult": false } ] } ``` -------------------------------- ### Connect to Photon Room with Matchmaking Arguments Source: https://doc.photonengine.com/quantum/current/manual/game-session/starting-session Use this snippet to connect to the Photon cloud and perform matchmaking to enter a room. Ensure Photon application settings are configured and specify the Quantum plugin. The UserId should be replaced with custom authentication details. ```csharp var connectionArguments = new MatchmakingArguments { // The Photon application settings include information about the app. PhotonSettings = PhotonServerSettings.Global.AppSettings, // The plugin to request from the Photon cloud set to the Quantum plugin. PluginName = "QuantumPlugin" // Setting an explicit room name will try to create or join the room based on how CanOnlyJoin is set. The RoomName can be null to create a unique name on create. RoomName = "My Room Name", // The maximum number of clients that can connect to the room, it most cases this is equal to the max number of players in the Quantum simulation. MaxPlayers = Input.MAX_COUNT, // Configure if the connect request can also create rooms or if it only tries to join CanOnlyJoin = false, // This sets the AuthValues and should be replaced with custom authentication and setting AuthValues explicitly UserId = Guid.NewGuid().ToString(), }; // This line connects to the Photon cloud and performs matchmaking based on the arguments to finally enter a room. RealtimeClient Client = await MatchmakingExtensions.ConnectToRoomAsync(connectionArguments); ``` -------------------------------- ### Quantum Logging Example Source: https://doc.photonengine.com/quantum/current/manual/quantum-project Use the static Quantum.Log class to log messages from simulation code. Log.Debug() and Log.Trace() messages are only output in debug builds. ```csharp namespace Quantum { public unsafe class MyQuantumSystem : SystemMainThread public override void Update(Frame frame) { Log.Debug($"Updating MyQuantumSystem tick {frame.Number}"); } } } ``` -------------------------------- ### SessionConfig JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example of a SessionConfig object serialized to JSON. This represents deterministic session configuration and requires complete serialization when used in webhooks. ```json { "PlayerCount": 8, "ChecksumCrossPlatformDeterminism": false, "LockstepSimulation": false, "InputDeltaCompression": true, "UpdateFPS": 60, "ChecksumInterval": 60, "RollbackWindow": 60, "InputHardTolerance": 8, "InputRedundancy": 3, "InputRepeatMaxDistance": 10, "SessionStartTimeout": 1, "TimeCorrectionRate": 4, "MinTimeCorrectionFrames": 1, "MinOffsetCorrectionDiff": 1, "TimeScaleMin": 100, "TimeScalePingMin": 100, "TimeScalePingMax": 300, "InputDelayMin": 0, "InputDelayMax": 60, "InputDelayPingStart": 100, "InputFixedSizeEnabled": true, "InputFixedSize": 24 } ``` -------------------------------- ### CloseGame Webhook Request Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks This JSON example shows the request payload for the CloseGame webhook. It includes the game ID and the reason for closing the session. ```json { "GameId": "0:eu:db757806-8570-45aa", "CloseReason": 0 } ``` -------------------------------- ### CreateGame Webhook Request JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks An example of the JSON payload sent to the CreateGame webhook. It includes details about the game session and the initiating user. ```json { "AppId": "d1f67eec-51fb-45c1", "AppVersion": "1.0-live", "Region": "eu", "Cloud": "1", "UserId": "db757806-8570-45aa", "AuthCookie": { "Secret": "**********" }, "RoomName": "e472a861-a1e2-49f7", "GameId": "0:eu:e472a861-a1e2-49f7", "EnterRoomParams": { "RoomOptions": { "IsVisible": true, "IsOpen": true } } } ``` -------------------------------- ### Create Deterministic Plugin Source: https://doc.photonengine.com/quantum/current/addons/plugin-sdk/quantum-server-api Implement this method in your `DeterministicPluginFactory` to instantiate Quantum plugins and server objects for individual rooms. ```csharp public override DeterministicPlugin CreateDeterministicPlugin(IPluginHost gameHost, String pluginName, Dictionary config, ref String errorMsg) ``` -------------------------------- ### Powershell Argument Example Source: https://doc.photonengine.com/quantum/current/getting-started/migration-guide Powershell arguments must be passed with a single '-' and not '--'. This example shows the correct format for specifying the Unity Editor path. ```powershell -UnityEditorPath "C:\\Program Files\\Unity\\Hub\\Editor\\2021.3.0f1\\Editor\\Unity.exe" ```