daily update

This commit is contained in:
2026-06-13 00:07:35 +07:00
parent d731f962a3
commit 20f9010787
371 changed files with 731 additions and 15 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3b587f29c2d8e6e40b54d6735fb38981
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,120 @@
using System.Collections.Generic;
using Fusion.Analyzer;
using UnityEngine;
namespace Fusion {
#if UNITY_EDITOR
using UnityEditor;
#endif
/// <summary>
/// Automatically adds a <see cref="RunnerVisibilityLink"/> for each indicated component.
/// These indicated components will be limited to no more than one enabled instance when running in Multi-Peer mode.
/// </summary>
[AddComponentMenu("Fusion/Enable On Single Runner")]
public class EnableOnSingleRunner : Fusion.Behaviour {
/// <summary>
/// If more than one runner instance is visible, this indicates which peer's clone of this entity should be visible.
/// </summary>
[InlineHelp]
[SerializeField]
#pragma warning disable IDE0044 // Add readonly modifier
public RunnerVisibilityLink.PreferredRunners PreferredRunner;
#pragma warning restore IDE0044 // Add readonly modifier
/// <summary>
/// Collection of components that will be marked for Multi-Peer mode as objects that should only have one enabled instance.
/// </summary>
[InlineHelp]
public UnityEngine.Component[] Components = new Component[0];
/// <summary>
/// Prefix for the GUIDs of <see cref="RunnerVisibilityLink"/> components which are added at runtime.
/// </summary>
[HideInInspector]
[SerializeField]
private string _guid = System.Guid.NewGuid().ToString().Substring(0, 19);
/// <summary>
/// At runtime startup, this adds a <see cref="RunnerVisibilityLink"/> for each component reference to this GameObject.
/// </summary>
internal void AddNodes(List<RunnerVisibilityLink> existingNodes) {
for(int i = 0; i < Components.Length; ++i) {
var found = false;
foreach (var existingNode in existingNodes) {
if (existingNode.Component == Components[i]) {
found = true;
break;
}
}
if (found) continue;
var node = Components[i].gameObject.AddComponent<RunnerVisibilityLink>();
node.Guid = _guid + i;
node.Component = Components[i];
node.SetupOnSingleRunnerLink(PreferredRunner);
existingNodes.Add(node);
}
}
/// <summary>
/// Finds visual/audio components on this GameObject, and adds them to the Components collection.
/// </summary>
[EditorButton("Find on GameObject", EditorButtonVisibility.EditMode, dirtyObject: true)]
public void FindRecognizedTypes() {
Components = FindRecognizedComponentsOnGameObject(gameObject);
}
/// <summary>
/// Finds visual/audio nested components on this GameObject and its children, and adds them to the Components collection.
/// </summary>
[EditorButton("Find in Nested Children", EditorButtonVisibility.EditMode, dirtyObject: true)]
public void FindNestedRecognizedTypes() {
Components = FindRecognizedNestedComponents(gameObject);
}
[StaticField(StaticFieldResetMode.None)]
private static readonly List<Component> reusableComponentsList = new List<UnityEngine.Component>();
[StaticField(StaticFieldResetMode.None)]
private static readonly List<Component> reusableComponentsList2 = new List<UnityEngine.Component>();
private static Component[] FindRecognizedComponentsOnGameObject(GameObject go) {
try {
go.GetComponents(reusableComponentsList);
reusableComponentsList2.Clear();
foreach (var comp in reusableComponentsList) {
var type = comp.GetType();
if (type.IsRecognizedByRunnerVisibility()) {
reusableComponentsList2.Add(comp);
}
}
return reusableComponentsList2.ToArray();
} finally {
reusableComponentsList.Clear();
reusableComponentsList2.Clear();
}
}
private static Component[] FindRecognizedNestedComponents(GameObject go) {
try {
go.transform.GetNestedComponentsInChildren<UnityEngine.Component, NetworkObject>(reusableComponentsList, true);
reusableComponentsList2.Clear();
foreach (var comp in reusableComponentsList) {
var type = comp.GetType();
if (type.IsRecognizedByRunnerVisibility()) {
reusableComponentsList2.Add(comp);
}
}
return reusableComponentsList2.ToArray();
} finally {
reusableComponentsList.Clear();
reusableComponentsList2.Clear();
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d73b0a95be81246699cf0d9ecdc01863
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 267958
packageName: Photon Fusion
packageVersion: 2.0.12
assetPath: Assets/Photon/Fusion/Runtime/Utilities/RunnerVisibility/EnableOnSingleRunner.cs
uploadId: 888248

View File

@@ -0,0 +1,85 @@
namespace Fusion {
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Add this Component to the NetworkRunner Prefab or GameObject. If Interest Management is enabled in NetworkProjectConfig ReplicationFeatures,
/// gizmos will be shown that indicate active Area Of Interest cells. These gizmos are currently NOT applicable to Shared Mode and will only
/// render for the Server/Host peer.
/// </summary>
[RequireComponent(typeof(NetworkRunner))]
[ScriptHelp(BackColor = ScriptHeaderBackColor.Sand)]
[DisallowMultipleComponent]
public class RunnerAOIGizmos : SimulationBehaviour {
#if UNITY_EDITOR
[Flags]
public enum GizmoOptionsEnum {
ShowActiveServerZones = 1,
ShowPlayerInterest = 2,
}
[System.Serializable]
public struct CustomOptions {
public Color ServerZonesColor;
public Color PlayerInterestColor;
}
[ExpandableEnum(AlwaysExpanded = true)]
public GizmoOptionsEnum GizmoOptions = GizmoOptionsEnum.ShowActiveServerZones | GizmoOptionsEnum.ShowPlayerInterest;
public CustomOptions Customization = new CustomOptions() {
ServerZonesColor = new Color(0.25f, 0.25f, 0.25f, 0.75f),
PlayerInterestColor = new Color(255f / 255f, 21f / 255f, 21 / 255f, 0.2f),
};
private List<(Vector3 center, Vector3 size, int playerCount, int objectCount)> _reusableGizmoData;
private void OnEnabled() {
}
private void OnDrawGizmos() {
if (enabled == false) {
return;
}
if (GizmoOptions == 0) {
return;
}
var runner = Runner;
if ((object)runner == null || runner.IsRunning == false) {
return;
}
var datas = _reusableGizmoData ??= new List<(Vector3 center, Vector3 size, int playerCount, int objectCount)>();
var colors = Customization;
runner.GetAreaOfInterestGizmoData(datas);
for (int i = 0; i < datas.Count; i++) {
var data = datas[i];
var c = datas[i].center;
var s = datas[i].size;
// Draw server actives zone boxes
if (data.objectCount > 0) {
Gizmos.color = colors.ServerZonesColor;
Gizmos.DrawWireCube(data.center, data.size);
}
// Draw player interest regions
if (data.playerCount > 0) {
Gizmos.color = colors.PlayerInterestColor;
Gizmos.DrawCube(c, s);
}
}
Gizmos.color = Color.white;
}
#endif
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a09b7c3c2f0604fef93beb7057001dbb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 267958
packageName: Photon Fusion
packageVersion: 2.0.12
assetPath: Assets/Photon/Fusion/Runtime/Utilities/RunnerVisibility/RunnerAOIGizmos.cs
uploadId: 888248

View File

@@ -0,0 +1,85 @@
namespace Fusion {
using System;
using System.Collections.Generic;
using Sockets;
using UnityEngine;
/// <summary>
/// When running in Multi-Peer mode, this component automatically will register the associated
/// <see cref="NetworkRunner" /> with <see cref="NetworkRunnerVisibilityExtensions" />,
/// and will automatically attach loaded scene objects and spawned objects with the peers visibility handling.
/// </summary>
[ScriptHelp(BackColor = ScriptHeaderBackColor.Sand)]
[DisallowMultipleComponent]
public class RunnerEnableVisibility : Behaviour, INetworkRunnerCallbacks {
private void Awake() {
var runner = GetComponentInParent<NetworkRunner>();
if (runner) {
// Optimistically register this as if we are running multi-peer (can't know yet)
runner.EnableVisibilityExtension();
// Just to be safe against double registration.
runner.ObjectAcquired -= RunnerOnObjectAcquired;
runner.ObjectAcquired += RunnerOnObjectAcquired;
}
}
private void OnDestroy() {
if (TryGetComponent<NetworkRunner>(out var runner)) {
runner.DisableVisibilityExtension();
runner.RemoveCallbacks(this);
runner.ObjectAcquired -= RunnerOnObjectAcquired;
}
}
private void RunnerOnObjectAcquired(NetworkRunner runner, NetworkObject obj) {
if (runner.IsRunning == false) return;
if (runner.Config.PeerMode == NetworkProjectConfig.PeerModes.Single) {
Destroy(this);
return;
}
runner.AddVisibilityNodes(obj.gameObject);
}
public void OnReliableDataProgress(NetworkRunner runner, PlayerRef player, ReliableKey key, float progress) {
}
void INetworkRunnerCallbacks.OnSceneLoadDone(NetworkRunner runner) {
if (runner.IsRunning == false) return;
if (runner.Config.PeerMode == NetworkProjectConfig.PeerModes.Single) {
Destroy(this);
return;
}
var scene = runner.SimulationUnityScene;
if (scene.IsValid())
foreach (var obj in scene.GetRootGameObjects())
runner.AddVisibilityNodes(obj);
}
#region Unused
void INetworkRunnerCallbacks.OnObjectExitAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { }
void INetworkRunnerCallbacks.OnObjectEnterAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { }
void INetworkRunnerCallbacks.OnPlayerJoined(NetworkRunner runner, PlayerRef player) { }
void INetworkRunnerCallbacks.OnPlayerLeft(NetworkRunner runner, PlayerRef player) { }
void INetworkRunnerCallbacks.OnInput(NetworkRunner runner, NetworkInput input) { }
void INetworkRunnerCallbacks.OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input) { }
void INetworkRunnerCallbacks.OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { }
void INetworkRunnerCallbacks.OnConnectedToServer(NetworkRunner runner) { }
void INetworkRunnerCallbacks.OnDisconnectedFromServer(NetworkRunner runner, NetDisconnectReason reason) { }
void INetworkRunnerCallbacks.OnConnectRequest(NetworkRunner runner, NetworkRunnerCallbackArgs.ConnectRequest request, byte[] token) { }
void INetworkRunnerCallbacks.OnConnectFailed(NetworkRunner runner, NetAddress remoteAddress, NetConnectFailedReason reason) { }
void INetworkRunnerCallbacks.OnUserSimulationMessage(NetworkRunner runner, SimulationMessagePtr message) { }
void INetworkRunnerCallbacks.OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList) { }
void INetworkRunnerCallbacks.OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary<string, object> data) { }
void INetworkRunnerCallbacks.OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken) { }
void INetworkRunnerCallbacks.OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment<byte> data) { }
void INetworkRunnerCallbacks.OnSceneLoadStart(NetworkRunner runner) { }
#endregion
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b8edbe7108d8a43ab838cf8b8d9453df
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 267958
packageName: Photon Fusion
packageVersion: 2.0.12
assetPath: Assets/Photon/Fusion/Runtime/Utilities/RunnerVisibility/RunnerEnableVisibility.cs
uploadId: 888248

View File

@@ -0,0 +1,80 @@
namespace Fusion
{
using LagCompensation;
using UnityEngine;
[ScriptHelp(BackColor = ScriptHeaderBackColor.Sand)]
[DisallowMultipleComponent]
public class RunnerLagCompensationGizmos : Behaviour
{
public bool DrawSnapshotHistory;
public bool DrawBroadphaseNodes;
public Color StateAuthHitboxCollor = Color.green;
public Color NonStateAuthHitboxCollor = Color.cyan;
private NetworkRunner _runner;
private void Awake()
{
_runner = GetComponentInParent<NetworkRunner>();
if (_runner == null) {
Debug.LogWarning($"{this} was not able to find the NetworkRunner reference. Destroying the component.");
Destroy(this);
}
}
private void OnDrawGizmos()
{
if (_runner == null || _runner.IsRunning == false || _runner.GetVisible() == false || _runner.LagCompensation?.DrawInfo == default) return;
if (DrawBroadphaseNodes)
{
RenderBHVBroadphase();
}
if (DrawSnapshotHistory)
{
RenderHitboxHistory();
}
}
private void RenderHitboxHistory()
{
Gizmos.color = _runner.IsServer ? StateAuthHitboxCollor : NonStateAuthHitboxCollor;
foreach (var snapshotDrawInfo in _runner.LagCompensation.DrawInfo.SnapshotHistoryDraw) {
foreach (var colliderDrawInfo in snapshotDrawInfo) {
Gizmos.matrix = colliderDrawInfo.LocalToWorldMatrix;
switch (colliderDrawInfo.Type) {
case HitboxTypes.Box:
Gizmos.DrawWireCube(colliderDrawInfo.Offset, colliderDrawInfo.BoxExtents * 2);
break;
case HitboxTypes.Sphere:
Gizmos.DrawWireSphere(colliderDrawInfo.Offset, colliderDrawInfo.Radius);
break;
case HitboxTypes.Capsule:
LagCompensationDraw.GizmosDrawWireCapsule(colliderDrawInfo.CapsuleTopCenter, colliderDrawInfo.CapsuleBottomCenter, colliderDrawInfo.Radius);
break;
default:
Debug.LogWarning($"HitboxType {colliderDrawInfo.Type} not supported to draw.");
break;
}
}
}
Gizmos.matrix = Matrix4x4.identity;
}
private void RenderBHVBroadphase()
{
var initialColor = Color.green;
foreach (var nodeDrawInfo in _runner.LagCompensation.DrawInfo.BVHDraw) {
Gizmos.color = initialColor + Color.red * nodeDrawInfo.Depth / nodeDrawInfo.MaxDepth;
Gizmos.DrawWireCube(nodeDrawInfo.Bounds.center, nodeDrawInfo.Bounds.size);
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7551cd937fd7ae84eb38a9dbbcd67c6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 267958
packageName: Photon Fusion
packageVersion: 2.0.12
assetPath: Assets/Photon/Fusion/Runtime/Utilities/RunnerVisibility/RunnerLagCompensationGizmos.cs
uploadId: 888248

View File

@@ -0,0 +1,245 @@
namespace Fusion {
using System;
using System.Collections.Generic;
using Fusion.Analyzer;
using UnityEngine;
/// <summary>
/// Flags a MonoBehaviour class as a RunnerVisibilityControl recognized type.
/// Will be included in runner visibility handling, and will be found by <see cref="EnableOnSingleRunner"/> component finds.
/// </summary>
public interface IRunnerVisibilityRecognizedType {
}
/// <summary>
/// Identifies visible/audible components (such as renderers, canvases, lights) that should be enabled/disabled by runner visibility handling.
/// Automatically added to scene objects and spawned objects during play if running in <see cref="NetworkProjectConfig.PeerModes.Multiple"/>.
/// Additionally this component can be added manually at development time to identify specific Behaviours or Renderers you would like to restrict to one enabled copy at a time.
/// </summary>
[AddComponentMenu("")]
public sealed class RunnerVisibilityLink : MonoBehaviour {
/// <summary>
/// The peer runner that will be used if more than one runner is visible, and this node was manually added by developer (indicating only one instance should be visible at a time).
/// </summary>
public enum PreferredRunners {
/// <summary>
/// The first visible runner will be used.
/// </summary>
Auto,
/// <summary>
/// The server peer/runner will be used if visible.
/// </summary>
Server,
/// <summary>
/// The first client peer/runner will be used if visible.
/// </summary>
Client,
/// <summary>
/// The components will only be enabled on the instance that has input authority over the NetworkObject. Unlike the other options, this expects a NetworkObject to work and it will search its children and parents for it.
/// </summary>
InputAuthority,
}
private enum ComponentType {
None,
Renderer,
Behaviour
}
/// <summary>
/// If more than one runner instance is visible, this indicates which peer's clone of this entity should be visible.
/// </summary>
[SerializeField]
#pragma warning disable IDE0044 // Add readonly modifier
public PreferredRunners PreferredRunner;
#pragma warning restore IDE0044 // Add readonly modifier
/// <summary>
/// The associated component with this node. This Behaviour or Renderer will be enabled/disabled when its NetworkRunner.IsVisible value is changed.
/// </summary>
public Component Component;
public bool IsOnSingleRunner { get; private set; }
/// <summary>
/// Guid is used for common objects (user flagged components that should only run in one instance), to identify matching clones.
/// </summary>
[SerializeField]
[ReadOnly]
internal string Guid;
// TODO: This can be removed later. Here for backwards compat for the short term as users may still be using this component.
// Ultimately this component will always be invisible.
[SerializeField]
[HideInInspector]
internal bool _showAtRuntime;
// cached runtime
internal NetworkRunner _runner;
private ComponentType _componentType;
private NetworkObject _networkObject;
private bool _originalState;
/// <summary>
/// Set to false to indicate that this object should remain disabled even when <see cref="NetworkRunner.IsVisible"/> is set to true.
/// </summary>
public bool DefaultState {
get {
return _originalState;
}
set {
_originalState = value;
}
}
// internal LinkedListNode<RunnerVisibilityNode> _node;
internal bool Enabled {
get { return _componentType == ComponentType.Renderer ? (Component as Renderer).enabled : (Component as UnityEngine.Behaviour).enabled; }
set {
if (Component == null) {
return;
}
if (_componentType == ComponentType.Renderer)
(Component as Renderer).enabled = value;
else {
(Component as UnityEngine.Behaviour).enabled = value;
}
}
}
// TODO: Can be removed most likely now that Node is not user accessible.
// Reset finds the first viable component and automatically adds it
private void Reset() {
_showAtRuntime = true;
Guid = System.Guid.NewGuid().ToString();
}
private bool AssociateComponent(Component component) {
Component = component;
var type = component.GetType();
if (component as Renderer != null) {
_componentType = ComponentType.Renderer;
return true;
} else if (component as UnityEngine.Behaviour != null) {
_componentType = ComponentType.Behaviour;
return true;
}
return false;
}
private void OnValidate() {
if (Component != null) {
if (Component.transform != transform) {
Debug.LogWarning($"{nameof(RunnerVisibilityLink)} can only be associated with components on the same GameObject.");
Component = null;
return;
}
if (AssociateComponent(Component))
return;
Debug.LogWarning($"{nameof(RunnerVisibilityLink)} can only be associated with Components that can be enabled/disabled.");
Component = null;
}
}
private void Awake() {
// TODO: once deprecated, make this flag always the case and remove the bool check.
if (!_showAtRuntime)
this.hideFlags = HideFlags.HideInInspector;
}
private void OnDestroy() {
this.UnregisterNode();
}
internal void Initialize(UnityEngine.Component comp, NetworkRunner runner) {
_runner = runner;
// First look into children
_networkObject = GetComponentInChildren<NetworkObject>();
if (!_networkObject)
_networkObject = GetComponentInParent<NetworkObject>();
if (!_networkObject && PreferredRunner == PreferredRunners.InputAuthority)
Log.Warn($"No NetworkObject found for RunnerVisibilityLink on {gameObject.name} with preferred runner as Input Authority. EnableOnSingleRunner will always disable it.");
if (comp is Renderer renderer) {
_componentType = ComponentType.Renderer;
_originalState = renderer.enabled;
renderer.enabled = runner.GetVisible() && _originalState;
//_node = node;
Component = comp;
} else if (comp is UnityEngine.Behaviour behaviour) {
_componentType = ComponentType.Behaviour;
_originalState = behaviour.enabled;
behaviour.enabled = runner.GetVisible() && _originalState;
// _node = node;
Component = comp;
}
}
/// <summary>
/// Sets the visibility state of this node.
/// </summary>
/// <param name="enabled"></param>
public void SetEnabled(bool enabled) {
if (enabled) {
// If this object was originally disabled, we will want to keep it that way, unless it looks like the user enabled the object directly since the last time this was called.
if (_originalState == false) {
// TODO: These only partially work
// User has directly enabled this object - assume it is meant to be enabled
if (Enabled) {
_originalState = true;
} else {
// original state was disabled, so leave it that way.
return;
}
}
Enabled = true;
} else {
// TODO: These only partially work
// Detect/store if user has manually disabled the component
//if (_originalState == true && Enabled == false) {
// _originalState = false;
//}
Enabled = false;
}
}
internal bool IsInputAuth() {
if (_networkObject && _networkObject.IsValid) {
return _networkObject.HasInputAuthority;
}
return false;
}
internal void SetupOnSingleRunnerLink(PreferredRunners preferredRunner) {
PreferredRunner = preferredRunner;
IsOnSingleRunner = true;
}
internal void InvokeRefreshCommonObjectVisibilities(float time) {
StopAllCoroutines();
Invoke(nameof(RetryRefreshCommonLinks), time);
}
private void RetryRefreshCommonLinks() {
NetworkRunnerVisibilityExtensions.RetryRefreshCommonLinks();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 972a7cfee50c64022a98777550cfb7d6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 267958
packageName: Photon Fusion
packageVersion: 2.0.12
assetPath: Assets/Photon/Fusion/Runtime/Utilities/RunnerVisibility/RunnerVisibilityLink.cs
uploadId: 888248

View File

@@ -0,0 +1,15 @@
namespace Fusion {
using UnityEngine;
/// <summary>
/// Flag component which indicates a NetworkObject has already been factored into a Runner's VisibilityNode list.
/// </summary>
[AddComponentMenu("")]
internal class RunnerVisibilityLinksRoot : MonoBehaviour {
private void Awake() {
this.hideFlags = HideFlags.HideInInspector;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c370cac09f73d42fab172498cc96e499
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 267958
packageName: Photon Fusion
packageVersion: 2.0.12
assetPath: Assets/Photon/Fusion/Runtime/Utilities/RunnerVisibility/RunnerVisibilityLinksRoot.cs
uploadId: 888248