### Quantum Engine Reconnection GUI Example Source: https://doc.photonengine.com/quantum/current/manual/game-session/reconnecting This C# script demonstrates how to connect to a Quantum cloud, start a game session, and handle disconnections and reconnections. Ensure RuntimeConfig, RuntimePlayers, and QuantumStats are set up correctly. Press 'Connect' to start, 'Disconnect' to stop, and '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 Static Assets at Runtime with Deterministic GUID Source: https://doc.photonengine.com/quantum/current/manual/assets/assets-simulation Provides an example of how to create a new asset object at runtime, assign it a deterministic GUID using QuantumUnityDB.CreateRuntimeDeterministicGuid, and add it to the asset database before the simulation starts. ```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; ``` -------------------------------- ### Start Quantum Game with Replay Arguments in Unity Source: https://doc.photonengine.com/quantum/current/manual/replay Starts the Quantum game session using the configured arguments. This is the final step to initiate the replay. ```csharp _runner = QuantumRunner.StartGame(arguments); ``` -------------------------------- ### .Net Runner Example Command Line Execution Source: https://doc.photonengine.com/quantum/current/manual/replay Example command line execution for the .Net Quantum runner, specifying paths for the replay file and LUT folder. ```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 ``` -------------------------------- ### ReplayStart Request JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks 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=" } ``` -------------------------------- ### Load and Start Quantum Game from Snapshot Source: https://doc.photonengine.com/quantum/current/manual/game-session/starting-from-snapshot Connects to a private room, loads a snapshot from a file, and starts a Quantum game session from that snapshot. Ensure the snapshot file and config file exist in the Application.dataPath. The initial tick is automatically managed by the simulation. ```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}"); } } ``` -------------------------------- ### System Start Disabled Configuration Source: https://doc.photonengine.com/quantum/current/manual/quantum-ecs/systems Configures a Quantum system to start in a disabled state by overriding the StartEnabled property. ```C# public override bool StartEnabled => false; ``` -------------------------------- ### Starting QuantumRunner with ClientId Source: https://doc.photonengine.com/quantum/current/manual/game-session/reconnecting When starting a QuantumRunner, provide the `ClientId` to uniquely identify the client. This `ClientId` must be the same for reconnecting players. ```csharp var sessionRunnerArguments = new SessionRunner.Arguments { ClientId = Client.UserId, //Other arguments are needed }; var runner = (QuantumRunner)await SessionRunner.StartAsync(sessionRunnerArguments); ``` -------------------------------- ### Quantum Demo Input Top Down Example Use Case Source: https://doc.photonengine.com/quantum/current/game-samples/sports-arena-brawler Example of how to retrieve and use player input within a Quantum frame. Assumes input structure is defined. ```C# // Example use case public override void Update(Frame frame, ref Filter filter) { QuantumDemoInputTopDown input = *frame.GetPlayerInput(filter.PlayerStatus->PlayerRef); // . . . } ``` -------------------------------- ### Invoke Map Initialization in System Source: https://doc.photonengine.com/quantum/current/addons/flow-fields/flow-field-map Example of triggering the initialization of multiple maps from within a Quantum system. ```C# // Initialize it in any system public override void OnInit(Frame frame) { var threadCount = frame.SimulationConfig.ThreadCount; frame.Context.InitFlowFieldMaps(frame, frame.RuntimeConfig.TileMapSetup, threadCount); } ``` -------------------------------- ### ReplayStart Response JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the ReplayStart webhook response. Setting 'Skip' to true disables replay streaming for the session. ```json { "Skip": true } ``` -------------------------------- ### AISystem with BTAgent Initialization Source: https://doc.photonengine.com/quantum/current/addons/bot-sdk/bt Example of an AISystem that initializes the BTAgent when added to an entity. ```csharp namespace Quantum { public unsafe class AISystem : SystemMainThread, SystemMainThreadFilter { public struct Filter { public EntityRef Entity; public BTAgent* BTAgent; } public void OnAdded(Frame frame, EntityRef entity, BTAgent* component) { var btRootAsset = frame.FindAsset(btReference.Id); BTManager.Init(frame, myEntity, btRoot); } public override void Update(Frame frame, ref Filter filter) { BTManager.Update(frame, filter.Entity); } } } ``` -------------------------------- ### Start an Online Quantum Session Source: https://doc.photonengine.com/quantum/current/manual/game-session/starting-session Configures and initiates a Quantum session runner using SessionRunner.Arguments. ```C# 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); ``` -------------------------------- ### Add Player After Session Start (Async) Source: https://doc.photonengine.com/quantum/current/manual/player/player Adds a player to an online simulation after the session has started asynchronously. Ensure the session runner is awaited before adding players. ```csharp var runner = (QuantumRunner)await SessionRunner.StartAsync(sessionRunnerArguments); // adding player to the online simulation var runtimePlayer = new RuntimePlayer { PlayerNickname = "whiskeyjack29" }; runner.Game.AddPlayer(runtimePlayer); ``` -------------------------------- ### Add Start UI To Scene Source: https://doc.photonengine.com/quantum/current/getting-started/release-notes Adds a new and simpler graphical connection menu UI. Access this through the Tools menu. ```csharp Tools > Quantum > Setup > Add Start UI To Scene ``` -------------------------------- ### Initialize and Update BTAgent Source: https://doc.photonengine.com/quantum/current/addons/bot-sdk/bt Initializes the BTManager with the root asset and entity, then updates the agent on each frame. This starts the BT execution. ```csharp BTManager.Init(frame, myEntity, btRoot); BTManager.Update(frame, filter.Entity); ``` -------------------------------- ### Sample System for HFSM Initialization and Update Source: https://doc.photonengine.com/quantum/current/addons/bot-sdk/hfsm A sample system demonstrating how to initialize and update HFSMAgents for entities. It implements ISignalOnComponentAdded for initialization and overrides Update for regular updates. ```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); } } } ``` -------------------------------- ### .Net Runner Command Line Help Source: https://doc.photonengine.com/quantum/current/manual/replay Displays help information for the .Net Quantum runner command line interface. Shows available options for starting a runner with replay and other configurations. ```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 world position of the next waypoint in an agent's path. This is used to guide the agent's 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); ``` -------------------------------- ### Generate Deterministic GUID for Runtime Asset Source: https://doc.photonengine.com/quantum/current/technical-samples/brick-builder Use this method to generate a deterministic GUID for an asset created at runtime. Ensure the asset's name is set before generating the GUID. The generated GUID is then used to add the asset to the asset database. ```csharp var assetObject = AssetObject.Create(); assetObject.name = "Generated Map"; var guid = QuantumUnityDB.CreateRuntimeDeterministicGuid(assetObject); QuantumUnityDB.Global.AddAsset(assetObject); assetObject.Guid = guid; ``` -------------------------------- ### Connect to Photon Room with Realtime Client Source: https://doc.photonengine.com/quantum/current/getting-started/whats-new Establishes a connection to a Photon room using the Realtime client and MatchmakingExtensions. Ensure PhotonServerSettings are configured and a RealtimeClient instance is created with ClientAppType.Quantum. ```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); ``` -------------------------------- ### SessionConfig JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks An example of the JSON representation for a SessionConfig, used for game session settings. ```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 } ``` -------------------------------- ### RuntimePlayer JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks An example of the JSON representation for a RuntimePlayer, including the $type property for deserialization. ```json { "$type":"Quantum.RuntimePlayer, Quantum.Simulation", "Loadout": { "Id": { "Value": 440543562436170603 } }, "PlayerAvatar": { "Id": { "Value": 2430278665492933905 } }, "PlayerNickname": "foo" } ``` -------------------------------- ### Initialize DynamicAssetDB with Assets Source: https://doc.photonengine.com/quantum/current/manual/assets/assets-simulation Create a DynamicAssetDB instance and populate it with initial assets before starting the simulation. This ensures deterministic initialization across clients. ```csharp var initialAssets = new DynamicAssetDB(); initialAssets.AddAsset(mageSpec); initialAssets.AddAsset(warriorSpec); ... ``` -------------------------------- ### RuntimeConfig JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks An example of the JSON representation for a RuntimeConfig, including the $type property for deserialization. ```json { "$type": "Quantum.RuntimeConfig, Quantum.Simulation", "GameMode": 1, "Seed": 0, "Map": { "Id": { "Value":2640765235684814815 } }, "SimulationConfig": { "Id": { "Value":440543562436170603 } }, "SystemsConfig": { "Id": { "Value":2430278665492933905 } } } ``` -------------------------------- ### GameResult JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks An example of the JSON representation for a GameResult, including the $type property and winner information. ```json { "$type":"Quantum.GameResult, Quantum.Simulation", "Frame": 200, "Winner": 2 } ``` -------------------------------- ### New SystemSetup.User.cs for Custom Systems Source: https://doc.photonengine.com/quantum/current/getting-started/whats-new This snippet shows the new way to add custom systems using DeterministicSystemSetup.AddSystemsUser in SystemSetup.User.cs. It allows for more flexible and data-driven system configuration. ```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()); ``` -------------------------------- ### Get Player Command by Index Source: https://doc.photonengine.com/quantum/current/manual/player/player Iterates through all players to get their respective commands using their player index. ```csharp var command = f.GetPlayerCommand(p); ``` -------------------------------- ### WebhookError JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks An example of the JSON structure for a WebhookError, including status code, error name, and message. ```json { "Status": 400, "Error": "PlayerNotAllowed", "Message": "LoremIpsum" } ``` -------------------------------- ### Create and Configure NavMesh Agent Source: https://doc.photonengine.com/quantum/current/manual/navigation/workflow-agents This C# snippet demonstrates how to create a Quantum system that initializes an entity with a transform, a NavMeshPathfinder, and a NavMeshSteeringAgent. It sets a target position on a specified navmesh and activates the agent. ```csharp namespace Quantum { using Photon.Deterministic; using UnityEngine.Scripting; [Preserve] public unsafe class NavMeshAgentTestSystem : SystemMainThread { public override void OnInit(Frame frame) { base.OnInit(frame); var entity = frame.Create(); // Add a transform 3d component or 2d component frame.Set(entity, new Transform3D() { Position = FPVector3.Zero, Rotation = FPQuaternion.Identity }); // Create the pathfinder component using the factory method, optionally pass a NavMeshAgentConfig var pathfinder = NavMeshPathfinder.Create(frame, entity, null); // Find the navmesh by name and set a target before adding the component var navmesh = frame.Map.NavMeshes["Navmesh"]; pathfinder.SetTarget(frame, new FPVector3(12, 0, 0), navmesh); // Add the pathfinder and steering components to the entity frame.Set(entity, pathfinder); frame.Set(entity, new NavMeshSteeringAgent()); } public override void Update(Frame frame) { } } } ``` -------------------------------- ### PlayerRemoved Webhook JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the PlayerRemoved webhook. Includes the reason for player 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": "**********" } } ``` -------------------------------- ### Multithreaded System Sample with Quantum Tasks Source: https://doc.photonengine.com/quantum/current/manual/multithreading Demonstrates a system that registers and schedules array and threaded tasks using Quantum's task system. It shows how to manage task dependencies and process data in parallel. ```C# // The System using Quantum.Task; namespace Quantum { public unsafe class MultithreadedSystemSample : SystemBase { private TaskDelegateHandle _arrayTaskDelegateHandle; private TaskDelegateHandle _threadedTaskDelegateHandle; public override void OnInit(Frame frame) { frame.Context.TaskContext.RegisterDelegate(ArrayTaskMethod, "Array Task", ref _arrayTaskDelegateHandle); frame.Context.TaskContext.RegisterDelegate(ThreadedTaskMethod, "Threaded Task", ref _threadedTaskDelegateHandle); } protected override TaskHandle Schedule(Frame frame, TaskHandle taskHandle) { // the size of an array task must be known in Schedule-time var arrayCount = frame.Unsafe.GetPointerSingleton()->UsedCount; var arrayTaskHandle = frame.Context.TaskContext.AddArrayTask(_arrayTaskDelegateHandle, taskArg: null, taskSize: arrayCount, dependancy: taskHandle); // get indexer to be used for slicing the data in the threaded task // optional: instead of temp-allocating, the indexer could be persisted in a partial declaration of Frame (use AllocUser and FreeUser) var threadedTaskIndexer = frame.Context.TempAllocateAndClear(sizeof(AtomicInt)); var threadedTaskHandle = frame.Context.TaskContext.AddThreadedTask(_threadedTaskDelegateHandle, taskArg: threadedTaskIndexer, dependancy: arrayTaskHandle); // we return the LAST task here, so next SYSTEM scheduler DEPENDS on it... return threadedTaskHandle; } public void ArrayTaskMethod(FrameThreadSafe frame, int start, int count, void* taskArg) { var arrayOfFoo = frame.GetPointerSingleton()->MyStructFooArray; // start/count are only meaningful in an array task for (int i = start; i < start + count; i++) { var foo = arrayOfFoo.GetPointer(i); // do work on foo ... } } public void ThreadedTaskMethod(FrameThreadSafe frame, int start, int count, void* userData) { // the task indexer was passed in as task argument var taskIndexer = (AtomicInt*)userData; // get array and its count var myStructFooData = frame.GetPointerSingleton(); var arrayOfFoo = myStructFooData->MyStructFooArray; var arrayCount = myStructFooData->UsedCount; // start/count are meaningless for a threaded task. // as the size the data was NOT known in schedule-time, it must be sliced now var slices = stackalloc TaskSlice[frame.Context.TaskContext.SlicePerThreadMaxCount]; var slicer = frame.Context.TaskContext.SlicePerThread(taskIndexer, arrayCount, slices); while (slicer.Next(out var slice)) { for (var i = slice.StartInclusive; i < slice.EndExclusive; ++i) { var foo = arrayOfFoo.GetPointer(i); // do work on foo ... } } } } } ``` -------------------------------- ### ReplayStart Source: https://doc.photonengine.com/quantum/current/manual/webhooks The ReplayStart webhook is sent when the simulation and input recording begin on the server. It allows for capturing game replays directly from the server. A response must be received before the first replay slice is sent, or recording will be canceled. ```APIDOC ## POST https://{WebHookBaseUrl}/replay/start ### Description This webhook is sent when the simulation and input recording starts on the server. It's a trusted source for capturing the game replay directly from the server. ### Method POST ### Endpoint https://{WebHookBaseUrl}/replay/start ### Parameters #### Request Body - **AppId** (string) - Required - The Photon AppId. - **AppVersion** (string) - Required - The AppVersion used when creating the room/game session. - **Region** (string) - Required - The Region code of the Game Server that the room/game session was created in. - **Cloud** (string) - Required - The `Cloud Id` of that the Game Server is running on. - **RoomName** (string) - Required - The room/game session Name. - **GameId** (string) - Required - A unique `GameId` which is composed of `{Cloud:}{Region:}RoomName`. - **SessionConfig** (SessionConfig) - Required - The `SessionConfig` that the simulation started with. - **RuntimeConfig** (byte[]) - Required - The GZipped Json of the `RuntimeConfig` that the simulation started with. ### Request Example ```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\nX6sXZycNCCe8AogSYtNBlK6X/3UHQR4Zb35X2eW2GflslBC+ds\nY8rhcMkx+eC6Md79o9h96t6HPNjkxwgF9M7doMUCTnaCdoWjpB\nWudshiUkxYsVHacE11KWe2TZiv4K3+4YjQkECKNJcVMuoXtszJ\nhfkPI1tUVc1sqFGN1g3Kr+0J+3ktedUAAAA=" } ``` ### Response #### Success Response (200 OK) - **Skip** (bool) - The replay streaming is disabled for this game session. #### Response Example ```json { "Skip": true } ``` ``` -------------------------------- ### Deprecated SystemSetup.CreateSystems Source: https://doc.photonengine.com/quantum/current/getting-started/whats-new The old SystemSetup.CreateSystems method is still functional but deprecated. It's recommended to migrate to the new SystemSetup.User.cs approach. ```csharp namespace Quantum { public static class SystemSetup { public static SystemBase[] CreateSystems(RuntimeConfig gameConfig, SimulationConfig simulationConfig) { return new SystemBase[] { // .. } } } } ``` -------------------------------- ### EnterRoomParams JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON structure for EnterRoomParams, used in CreateGame webhooks. All members are optional and can be null or omitted. ```json { "RoomOptions": { "IsVisible": true, "IsOpen": true, "MaxPlayers": 8, "PlayerTtl": null, "EmptyRoomTtl": 10000, "CustomRoomProperties": { "Foo": "bar", "PlayerClass": 1 }, "CustomRoomPropertiesForLobby": [ "Foo" ], "SuppressRoomEvents": null, "SuppressPlayerInfo": null, "PublishUserId": null, "DeleteNullProperties": null, "BroadcastPropsChangeToAll": null, "CleanupCacheOnLeave": null, "CheckUserOnJoin": null }, "ExpectedUsers": [ "A", "B", "C" ] } ``` -------------------------------- ### PlayerAdded Webhook JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the PlayerAdded webhook. 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": "**********" } } ``` -------------------------------- ### Initialize Compound Agents and Blackboards Source: https://doc.photonengine.com/quantum/current/addons/bot-sdk/snippets Initialize HFSM agents and AIBlackboard components for movement and attack using ISignalOnComponentAdded. ```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); } ``` -------------------------------- ### CloseGame Webhook Request JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the CloseGame webhook request. Includes GameId and CloseReason. ```json { "GameId": "0:eu:db757806-8570-45aa", "CloseReason": 0 } ``` -------------------------------- ### Quantum Project Structure Source: https://doc.photonengine.com/quantum/current/addons/plugin-sdk/setup Illustrates the expected folder structure after unzipping the Quantum Unity project and Plugin SDK. ```text MyQuantumProject ├─Assets ├─Library ├─.. └─PluginSDK ``` -------------------------------- ### Define, Trigger, and Subscribe to a Simple Event Source: https://doc.photonengine.com/quantum/current/manual/quantum-ecs/game-events Demonstrates the basic workflow of defining an event in Quantum DSL, triggering it from the simulation, and subscribing to it in Unity. ```qtn event MyEvent { int Foo; } ``` ```csharp f.Events.MyEvent(2023); ``` ```csharp QuantumEvent.Subscribe(listener: this, handler: (EventMyEvent e) => Debug.Log($"MyEvent {e.Foo}")); ``` -------------------------------- ### PlayFab Webhook Response Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON response body for PlayFab webhook integration. The webhook will fail if ResultCode is not 0. ```json { "ResultCode": 0, "Message": "success" } ``` -------------------------------- ### LeaveGame Webhook Response JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the LeaveGame webhook response. Typically an empty object for confirmation. ```json { } ``` -------------------------------- ### JoinGame Webhook Response JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the JoinGame webhook response. Allows specifying RuntimePlayer and MaxPlayerSlots. ```json { "RuntimePlayer": { "Name": "player1" }, "MaxPlayerSlots": 1 } ``` -------------------------------- ### Connect and Join Room Async Source: https://doc.photonengine.com/quantum/current/manual/game-session/async-extensions Establishes a connection using app settings and joins or creates a room asynchronously. Ensure RealtimeClient and AppSettings are properly initialized. ```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); ``` -------------------------------- ### Get Player Input (Fixed) Source: https://doc.photonengine.com/quantum/current/tutorials/asteroids/5-player-spawning This snippet shows the original method of getting input from a fixed player index (player 0). ```csharp var input = frame.GetPlayerInput(0); ``` -------------------------------- ### ReplayChunk Request JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the ReplayChunk webhook request. This includes input history data for a simulation segment. ```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" } ``` -------------------------------- ### Initialize Host Profiler Source: https://doc.photonengine.com/quantum/current/manual/profiling Initializes the Quantum Host Profiler. This is typically done within the QuantumRunner script. ```csharp Quantum.Profiling.HostProfiler.Init(..) ``` -------------------------------- ### AddPlayer Response JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the AddPlayer webhook response. Can include a RuntimePlayer object to overwrite the client-sent value. ```json { "RuntimePlayer": { "Name": "player1" } } ``` -------------------------------- ### Start Quantum Game Session Asynchronously Source: https://doc.photonengine.com/quantum/current/getting-started/whats-new Initiates a Quantum game session asynchronously using the SessionRunner class. This requires configuring SessionRunner.Arguments with a RunnerFactory, GameParameters, ClientId, RuntimeConfig, SessionConfig, GameMode, PlayerCount, StartGameTimeoutInSeconds, and a Communicator. ```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); ``` -------------------------------- ### GameConfigs Response JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the GameConfigs webhook response. Can include RuntimeConfig and SessionConfig to overwrite client-sent values. ```json { "RuntimeConfig": { "Map": { "Id": { "Value": 94358348534 } } }, "SessionConfig": { "PlayerCount": 8, "ChecksumCrossPlatformDeterminism": false, "LockstepSimulation": false, "//..": "" } } ``` -------------------------------- ### Initialize HFSMAgent using OnComponentAdded Callback Source: https://doc.photonengine.com/quantum/current/addons/bot-sdk/hfsm Example of initializing an HFSMAgent using the OnComponentAdded signal. This is useful when the HFSMRoot asset reference is set on the EntityPrototype. ```csharp // At any system... public unsafe class AISystem : SystemMainThread, ISignalOnComponentAdded { public void OnAdded(Frame frame, EntityRef entity, HFSMAgent* component) { // Get the HFSMRoot from the component set on the Entity Prototype HFSMRoot hfsmRoot = frame.FindAsset(component->Data.Root.Id); // Initialize HFSMManager.Init(frame, entityRef, hfsmRoot); } // ... } ``` -------------------------------- ### CreateGame Request JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks An example of the JSON payload sent to the CreateGame webhook. This 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 } } } ``` -------------------------------- ### Quantum DSL Compiler Options Source: https://doc.photonengine.com/quantum/current/manual/quantum-ecs/dsl Use pragmas to define maximum players, component counts, and custom constants within Quantum's DSL files. ```qtn #pragma max_players 16 // increase the component count from 256 to 512 #pragma max_components 512 // numeric constants (useable inside the DSL by MY_NUMBER and useable in code by Constants.MY_NUMBER) #define MY_NUMBER 10 // overriding the base class name for the generated constants (default is "Constants") #pragma constants_class_name MyFancyConstants ``` -------------------------------- ### Create Custom Deterministic Plugin Source: https://doc.photonengine.com/quantum/current/addons/plugin-sdk/setup This code demonstrates how to create a custom DeterministicPlugin with a custom DeterministicServer that utilizes a DotNetSessionRunner. This is useful for running Quantum simulations outside of Unity. ```csharp public override DeterministicPlugin CreateDeterministicPlugin(IPluginHost gameHost, String pluginName, Dictionary config, IPluginLogger logger, ref String errorMsg) { var sessionRunner = new DotNetSessionRunner { AssetSerializer = new QuantumJsonSerializer() }; return new DeterministicPlugin(new DeterministicServer(sessionRunner)); } ``` -------------------------------- ### JoinGame Webhook Request JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the JoinGame webhook request. Includes AppId, GameId, UserId, and an optional AuthCookie. ```json { "AppId": "*******************", "GameId": "0:eu:db757806-8570-45aa", "UserId": "db757806-8570-45aa", "AuthCookie": { "Secret": "**********" } } ``` -------------------------------- ### KCC Processor OnEnter Method Source: https://doc.photonengine.com/quantum/current/addons/kcc/processors Invoked when the KCC starts colliding with a collider. The return value controls the start of the interaction, allowing for deferred registration. ```csharp public virtual bool OnEnter(KCCContext context, KCCProcessorInfo processorInfo, KCCOverlapHit overlapHit) => true; ``` -------------------------------- ### OnDeterministicStartSession Callback Source: https://doc.photonengine.com/quantum/current/addons/plugin-sdk/quantum-server-api Callback invoked when the Quantum simulation begins. ```csharp void OnDeterministicStartSession() ``` -------------------------------- ### AddPlayer Request JSON Example Source: https://doc.photonengine.com/quantum/current/manual/webhooks Example JSON payload for the AddPlayer webhook request. Includes AppId, GameId, UserId, ActorNr, PlayerSlot, RuntimePlayer, and AuthCookie. ```json { "AppId": "d1f67eec-51fb-45c1", "GameId": "0:eu:db757806-8570-45aa", "UserId": "db757806-8570-45aa", "ActorNr": 1, "PlayerSlot": 0, "RuntimePlayer": { "Name": "player1" }, "AuthCookie": { "Secret": "**********" } } ```