This document is about: QUANTUM 2
SWITCH TO

Miscellaneous

SDK Modifications

The FPS Template handles several features differently than the default Quantum SDK.

All of code paths where the default Quantum and FPS Template implementations diverge are marked with a QUANTUM_DEFAULT if-else define.

C#

#if QUANTUM_DEFAULT
    var matrix = Matrix4x4.TRS(t.position, t.rotation, t.localScale);
#else
    var matrix = Matrix4x4.TRS(t.position, t.rotation, t.lossyScale);
#endif

Quantum SDK

The changes made to the Quantum simulation in the FPS Template compared to the Quantum SDK are:

  • redefinition of the FrameContextUser to support a constructor with more parameters and custom processing.
  • increase of the RuntimeConfig size limit from 1kB to 8kB.
  • disabling all systems in OnGameEnded().
  • added profiling timers for systems (configurable and enabled by default) and certain code paths.
  • To support all features, entities have to be created and destroyed using frame.CreateEntity() and frame.DestroyEntity() (except a very special cases)
  • Added several methods to get components and controllers to frame API

Unity Integrations

The changes made to the Unity-side in the FPS Template compared to the ones included in the Quantum SDK are:

  • an Extended Entity inspector to support child and owner entities.
  • an additional option to Bake Prefabs via the MapData component inspector and the Quantum > Bake > Prefabs menu.
  • bypassed some QuantumRunner functionalitoes (except the Init() method) and replaced with the Simulator because of the different architecture; N.B.: it is still used to bridge QuantumGame with other tools such as the state inspector.
  • bypassed EntityViewUpdater and replaced with Entities because of the different architecture.
  • bypassed EntityView and replaced with the Entity component because of different architecture.
  • EntityComponentBase inherits from a custom EntityComponent script.
  • disabled QuantumRunnerLocalDebug and QuantumRunnerLocalReplay.
  • replaced all default replay scripts by custom implementation because of the different architecture.
  • QuantumStaticBoxCollider3D and QuantumStaticSphereCollider3D use lossyScale instead of localScale.
  • added more transform synchronization variants for various scenarios.
  • entities are always spawned on predicted frame by default; spawning in verified frame must be handled directly in the simulation.
  • used Newtonsoft.Json instead of Unity JsonUtility to support serialization of fields with [SerializeReference] attribute to preserver compatibility with server-side deserialization.

Workflow Recommendations

The following workflow recommendations are to facilitate updating and upgrading the FPS Template by preventing conflicts.

  • Always try to keep your code and assets organized according to their respective core environment (Framework / FPS / YOUR_PROJECT). This is applicable for both Unity and Quantum solutions.
  • If modifying or extending the core Framework code, use partial implementation whenever possible. Most of the classes and structs are marked as partial or have partial methods for exactly that purpose.
  • Always prefer AssetRefEntityPrototype fields over hard-coded strings to create entities.
  • All controllers are stateless - do NOT modify their fields at runtime as they cannot be rolled back. Caching non-serialized static properties is possible; however, the assignment must be an atomic operation or a race condition will appear in multi-threaded environments.

Using Unity Engine

It is not allowed to link the UnityEngine.dll with the Quantum solution as this could negatively impact server performance. It is, however, possible to mimic its API by using data structures and attributes through DLL hijacking.

A solution on how to build a custom UnityEngine.dll with the same signature can be found in REPOSITORY/extras/UnityEngine. It contains data structures as well as attributes, and can be modified as needed. After building the solution, UnityEngine.dll is copied to REPOSITORY/assemblies. This library is NOT referenced in Unity, but its original UnityEngine.dll is.

This custom UnityEngine.dll has to be included when uploading a custom Quantum plugin to the server (only available on an Enterprise server, please contact developer@photonengine.com for more information on the subject).

Framework Performance

This is target configuration and statistics for low performance mobile devices:

  • Simulation rate: 30 Hz
  • Target refresh rate: 30 FPS
  • Hard tolerance: 7 frames (~230ms)
  • Supported ping: ~250ms
  • Primitive colliders: 500 (75% box)
  • Players: 2 + 6 AI
  • Active projectiles: 50
  • Other actors with custom controllers: 50
  • CPU budget: 16.667ms (50% of frame)
  • GPU budget: 16.667ms (50% of frame)
  • Simulation budget: 4.167ms
  • Simulation time of 1 verified frame with all features enabled: ~2.8ms
  • Simulation time of 1 predicted frame with all features enabled: ~1.1ms

During 1 Unity frame, the game computes on average 1 verified Quantum frame and 3 - 5 predicted Quantum frames; this takes around 7-8ms. The final performance numbers heavily depend on the actual feature set used. A lot of performance can be saved by implementing simpler AI, not using simulation based autofire, and / or only enabling systems which are absolutely needed.

The number of players is an important factor as well. The fewer players are in the same gameplay scene, the more expensive the logic can be and vice versa; this is assuming the empty player spots are filled up with AI actors.

Network conditions resulting in a spike of predicted frames (up to 40) are extremely rare. However, these kind of occurence are not easily avoided on low performance devices and is a key-consideration for the settings in performance profiles.

Debugging

Logging

Some subsystems of the FPS Template contain logs. These logs can be enabled to see more information on what is happening at runtime.

Global level of logging can be changed via the Severity property in the GameSettings asset. You can also specify log group overrides.

game settings
Game Settings

Alternatively, the level of logging can be changed at runtime via the following APIs:

  1. globally: Log.SetSeverity(ELogSeverity.Warning);
  2. per log group: Log.SetSeverity(ELogGroup.Matchmaking, ELogSeverity.Info);

Desynchronization

Debugging state desynchronization is not a straightforward process; especially when there are lot of one-time actions.

These are our recommendations for facilitating the debugging process:

  1. Try to find a situation with stable and quickly reproducible desynchronization.
  2. Calculate and log checksums inside the Quantum simulation in the first, middle and last system (only on verified frames!). This will provide a baseline.
  3. Depending on the checksum differences, a proceed with a new interval division. The new first system will be:
    • the center system if the checksum was different between it and the last system; or,
    • the last system if the checksum was different between it and the center system.
  4. Repeat step 3. until the system causing the desynchronization has been identified.
  5. Apply whole process on an entity basis to isolate the component, controller or other aspect of the simulation causing desynchronization.

Common Issues

  • Despawning/Respawning predicted entity due to resimulation: toggle the silent boolean in the Entity.OnDeinitialize(bool silent) and EntityComponent.OnDeinitialize(bool silent)methods on the Unity side. It is advised to resolve this situation by fading or scaling effects make their disappearance less noticeable until the entity is has been verified.
  • Unknown managed type referenced: [quantum.code] Quantum.XYZController: this error is caused by an existing serialized instance of type Quantum.XYZController in a prefab or scene game object. However, the script is now missing; this is usually tied to having removed or renamed the script. To resolve this issue, find the object / serialized file which triggered the error and manually remove the serialized data from it - N.B.: It has to be done from outside Unity as it does not include any tooling to identify and resolve this issue. Alternatively, temporarily adding the missing class back might allow to re-edit the necessary objects before re-baking them.
Back to top