### 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"
```