fishnet installed
This commit is contained in:
1
Assets/FishNet/Runtime/Managing/Object/DespawnWriter.cs
Normal file
1
Assets/FishNet/Runtime/Managing/Object/DespawnWriter.cs
Normal file
@ -0,0 +1 @@
|
||||
//Remove on 2023/06/01
|
11
Assets/FishNet/Runtime/Managing/Object/DespawnWriter.cs.meta
Normal file
11
Assets/FishNet/Runtime/Managing/Object/DespawnWriter.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b444a2a7364932340a4a3dede4a434a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
16
Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs
Normal file
16
Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using FishNet.Object;
|
||||
|
||||
namespace FishNet.Managing.Object
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// When using dual prefabs, defines which prefab to spawn for server, and which for clients.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct DualPrefab
|
||||
{
|
||||
public NetworkObject Server;
|
||||
public NetworkObject Client;
|
||||
}
|
||||
|
||||
}
|
11
Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs.meta
Normal file
11
Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76840b2b810d8fc45aeccef03122763c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,380 @@
|
||||
using FishNet.Connection;
|
||||
using FishNet.Managing.Logging;
|
||||
using FishNet.Managing.Server;
|
||||
using FishNet.Object;
|
||||
using FishNet.Serializing;
|
||||
using FishNet.Transporting;
|
||||
using FishNet.Utility.Extension;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Managing.Object
|
||||
{
|
||||
public abstract partial class ManagedObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads and outputs a transforms values.
|
||||
/// </summary>
|
||||
protected void ReadTransformProperties(Reader reader, out Vector3? localPosition, out Quaternion? localRotation, out Vector3? localScale)
|
||||
{
|
||||
//Read changed.
|
||||
ChangedTransformProperties ctp = (ChangedTransformProperties)reader.ReadByte();
|
||||
//Position.
|
||||
if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalPosition))
|
||||
localPosition = reader.ReadVector3();
|
||||
else
|
||||
localPosition = null;
|
||||
//Rotation.
|
||||
if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalRotation))
|
||||
localRotation = reader.ReadQuaternion(NetworkManager.ServerManager.SpawnPacking.Rotation);
|
||||
else
|
||||
localRotation = null;
|
||||
//Scale.
|
||||
if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalScale))
|
||||
localScale = reader.ReadVector3();
|
||||
else
|
||||
localScale = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a spawn to clients.
|
||||
/// </summary>
|
||||
internal void WriteSpawn_Server(NetworkObject nob, NetworkConnection connection, Writer everyoneWriter, Writer ownerWriter)
|
||||
{
|
||||
/* Using a number of writers to prevent rebuilding the
|
||||
* packets excessively for values that are owner only
|
||||
* vs values that are everyone. To save performance the
|
||||
* owner writer is only written to if owner is valid.
|
||||
* This makes the code a little uglier but will scale
|
||||
* significantly better with more connections.
|
||||
*
|
||||
* EG:
|
||||
* with this technique networkBehaviours are iterated
|
||||
* twice if there is an owner; once for data to send to everyone
|
||||
* and again for data only going to owner.
|
||||
*
|
||||
* The alternative would be to iterate the networkbehaviours
|
||||
* for every connection it's going to and filling a single
|
||||
* writer with values based on if owner or not. This would
|
||||
* result in significantly more iterations. */
|
||||
PooledWriter headerWriter = WriterPool.GetWriter();
|
||||
headerWriter.WritePacketId(PacketId.ObjectSpawn);
|
||||
headerWriter.WriteNetworkObjectForSpawn(nob);
|
||||
if (NetworkManager.ServerManager.ShareIds || connection == nob.Owner)
|
||||
headerWriter.WriteNetworkConnection(nob.Owner);
|
||||
else
|
||||
headerWriter.WriteInt16(-1);
|
||||
|
||||
bool nested = (nob.IsNested && nob.ParentNetworkObject != null);
|
||||
bool sceneObject = nob.IsSceneObject;
|
||||
//Write type of spawn.
|
||||
SpawnType st = SpawnType.Unset;
|
||||
if (sceneObject)
|
||||
st |= SpawnType.Scene;
|
||||
else
|
||||
st |= (nob.IsGlobal) ? SpawnType.InstantiatedGlobal : SpawnType.Instantiated;
|
||||
//Add on nested if needed.
|
||||
if (nested)
|
||||
st |= SpawnType.Nested;
|
||||
|
||||
headerWriter.WriteByte((byte)st);
|
||||
//ComponentIndex for the nob. 0 is root but more appropriately there's a IsNested boolean as shown above.
|
||||
headerWriter.WriteByte(nob.ComponentIndex);
|
||||
//Properties on the transform which diff from serialized value.
|
||||
WriteChangedTransformProperties(nob, sceneObject, nested, headerWriter);
|
||||
|
||||
/* When nested the parent nob needs to be written. */
|
||||
if (nested)
|
||||
headerWriter.WriteNetworkObjectId(nob.ParentNetworkObject);
|
||||
|
||||
/* Writing a scene object. */
|
||||
if (sceneObject)
|
||||
{
|
||||
headerWriter.WriteUInt64(nob.SceneId, AutoPackType.Unpacked);
|
||||
}
|
||||
/* Writing a spawned object. */
|
||||
else
|
||||
{
|
||||
//Check to write parent behaviour or nob.
|
||||
NetworkBehaviour parentNb;
|
||||
Transform t = nob.transform.parent;
|
||||
if (t != null)
|
||||
{
|
||||
parentNb = t.GetComponent<NetworkBehaviour>();
|
||||
/* Check for a NetworkObject if there is no NetworkBehaviour.
|
||||
* There is a small chance the parent object will only contain
|
||||
* a NetworkObject. */
|
||||
if (parentNb == null)
|
||||
{
|
||||
//If null check if there is a nob.
|
||||
NetworkObject parentNob = t.GetComponent<NetworkObject>();
|
||||
//ParentNob is null or not spawned.
|
||||
if (!ParentIsSpawned(parentNob))
|
||||
{
|
||||
headerWriter.WriteByte((byte)SpawnParentType.Unset);
|
||||
}
|
||||
else
|
||||
{
|
||||
headerWriter.WriteByte((byte)SpawnParentType.NetworkObject);
|
||||
headerWriter.WriteNetworkObjectId(parentNob);
|
||||
}
|
||||
}
|
||||
//NetworkBehaviour found on parent.
|
||||
else
|
||||
{
|
||||
//ParentNb is null or not spawned.
|
||||
if (!ParentIsSpawned(parentNb.NetworkObject))
|
||||
{
|
||||
headerWriter.WriteByte((byte)SpawnParentType.Unset);
|
||||
}
|
||||
else
|
||||
{
|
||||
headerWriter.WriteByte((byte)SpawnParentType.NetworkBehaviour);
|
||||
headerWriter.WriteNetworkBehaviour(parentNb);
|
||||
}
|
||||
}
|
||||
|
||||
//True if pNob is not null, and is spawned.
|
||||
bool ParentIsSpawned(NetworkObject pNob)
|
||||
{
|
||||
bool isNull = (pNob == null);
|
||||
if (isNull || !pNob.IsSpawned)
|
||||
{
|
||||
/* Only log if pNob exist. Otherwise this would print if the user
|
||||
* was parenting any object, which may not be desirable as they could be
|
||||
* simply doing it for organization reasons. */
|
||||
if (!isNull)
|
||||
NetworkManager.LogWarning($"Parent {t.name} is not spawned. {nob.name} will not have it's parent sent in the spawn message.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
//No parent.
|
||||
else
|
||||
{
|
||||
headerWriter.WriteByte((byte)SpawnParentType.Unset);
|
||||
}
|
||||
|
||||
headerWriter.WriteNetworkObjectId(nob.PrefabId);
|
||||
}
|
||||
|
||||
//Write headers first.
|
||||
everyoneWriter.WriteBytes(headerWriter.GetBuffer(), 0, headerWriter.Length);
|
||||
if (nob.Owner.IsValid)
|
||||
ownerWriter.WriteBytes(headerWriter.GetBuffer(), 0, headerWriter.Length);
|
||||
|
||||
/* Used to write latest data which must be sent to
|
||||
* clients, such as SyncTypes and RpcLinks. */
|
||||
PooledWriter tempWriter = WriterPool.GetWriter();
|
||||
//Send RpcLinks first.
|
||||
foreach (NetworkBehaviour nb in nob.NetworkBehaviours)
|
||||
nb.WriteRpcLinks(tempWriter);
|
||||
//Add to everyone/owner.
|
||||
everyoneWriter.WriteBytesAndSize(tempWriter.GetBuffer(), 0, tempWriter.Length);
|
||||
if (nob.Owner.IsValid)
|
||||
ownerWriter.WriteBytesAndSize(tempWriter.GetBuffer(), 0, tempWriter.Length);
|
||||
|
||||
//Add most recent sync type values.
|
||||
/* SyncTypes have to be populated for owner and everyone.
|
||||
* The data may be unique for owner if synctypes are set
|
||||
* to only go to owner. */
|
||||
WriteSyncTypes(everyoneWriter, tempWriter, SyncTypeWriteType.Observers);
|
||||
//If owner is valid then populate owner writer as well.
|
||||
if (nob.Owner.IsValid)
|
||||
WriteSyncTypes(ownerWriter, tempWriter, SyncTypeWriteType.Owner);
|
||||
|
||||
void WriteSyncTypes(Writer finalWriter, PooledWriter tWriter, SyncTypeWriteType writeType)
|
||||
{
|
||||
tWriter.Reset();
|
||||
foreach (NetworkBehaviour nb in nob.NetworkBehaviours)
|
||||
nb.WriteSyncTypesForSpawn(tWriter, writeType);
|
||||
finalWriter.WriteBytesAndSize(tWriter.GetBuffer(), 0, tWriter.Length);
|
||||
}
|
||||
|
||||
//Dispose of writers created in this method.
|
||||
headerWriter.Dispose();
|
||||
tempWriter.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes changed transform proeprties to writer.
|
||||
/// </summary>
|
||||
protected void WriteChangedTransformProperties(NetworkObject nob, bool sceneObject, bool nested, Writer headerWriter)
|
||||
{
|
||||
/* Write changed transform properties. */
|
||||
ChangedTransformProperties ctp;
|
||||
//If a scene object then get it from scene properties.
|
||||
if (sceneObject || nested)
|
||||
{
|
||||
ctp = nob.GetTransformChanges(nob.SerializedTransformProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrefabObjects po = NetworkManager.GetPrefabObjects<PrefabObjects>(nob.SpawnableCollectionId, false);
|
||||
ctp = nob.GetTransformChanges(po.GetObject(true, nob.PrefabId).gameObject);
|
||||
}
|
||||
|
||||
headerWriter.WriteByte((byte)ctp);
|
||||
//If properties have changed.
|
||||
if (ctp != ChangedTransformProperties.Unset)
|
||||
{
|
||||
//Write any changed properties.
|
||||
if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalPosition))
|
||||
headerWriter.WriteVector3(nob.transform.localPosition);
|
||||
if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalRotation))
|
||||
headerWriter.WriteQuaternion(nob.transform.localRotation, NetworkManager.ServerManager.SpawnPacking.Rotation);
|
||||
if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalScale))
|
||||
headerWriter.WriteVector3(nob.transform.localScale);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a despawn.
|
||||
/// </summary>
|
||||
/// <param name="nob"></param>
|
||||
protected void WriteDespawn(NetworkObject nob, DespawnType despawnType, Writer everyoneWriter)
|
||||
{
|
||||
everyoneWriter.WritePacketId(PacketId.ObjectDespawn);
|
||||
everyoneWriter.WriteNetworkObjectForDespawn(nob, despawnType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets transform properties by applying passed in values if they are not null, otherwise using transforms defaults.
|
||||
/// </summary>
|
||||
internal void GetTransformProperties(Vector3? readPos, Quaternion? readRot, Vector3? readScale, Transform defaultTransform, out Vector3 pos, out Quaternion rot, out Vector3 scale)
|
||||
{
|
||||
pos = (readPos == null) ? defaultTransform.localPosition : readPos.Value;
|
||||
rot = (readRot == null) ? defaultTransform.localRotation : readRot.Value;
|
||||
scale = (readScale == null) ? defaultTransform.localScale : readScale.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a scene NetworkObject and sets transform values.
|
||||
/// </summary>
|
||||
internal NetworkObject GetSceneNetworkObject(ulong sceneId)
|
||||
{
|
||||
NetworkObject nob;
|
||||
SceneObjects.TryGetValueIL2CPP(sceneId, out nob);
|
||||
//If found in scene objects.
|
||||
if (nob == null)
|
||||
NetworkManager.LogError($"SceneId of {sceneId} not found in SceneObjects. This may occur if your scene differs between client and server, if client does not have the scene loaded, or if networked scene objects do not have a SceneCondition. See ObserverManager in the documentation for more on conditions.");
|
||||
|
||||
return nob;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if a NetworkObject meets basic criteria for being predicted spawned.
|
||||
/// </summary>
|
||||
/// <param name="reader">If not null reader will be cleared on error.</param>
|
||||
/// <returns></returns>
|
||||
protected bool CanPredictedSpawn(NetworkObject nob, NetworkConnection spawner, NetworkConnection owner, bool asServer, Reader reader = null)
|
||||
{
|
||||
//Does not allow predicted spawning.
|
||||
if (!nob.AllowPredictedSpawning)
|
||||
{
|
||||
if (asServer)
|
||||
spawner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {spawner.ClientId} tried to spawn an object {nob.name} which does not support predicted spawning.");
|
||||
else
|
||||
NetworkManager.LogError($"Object {nob.name} does not support predicted spawning. Modify the NetworkObject component settings to allow predicted spawning.");
|
||||
|
||||
reader?.Clear();
|
||||
return false;
|
||||
}
|
||||
//Parenting is not yet supported.
|
||||
if (nob.transform.parent != null)
|
||||
{
|
||||
if (asServer)
|
||||
spawner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {spawner.ClientId} tried to spawn an object that is not root.");
|
||||
else
|
||||
NetworkManager.LogError($"Predicted spawning as a child is not supported.");
|
||||
|
||||
reader?.Clear();
|
||||
return false;
|
||||
}
|
||||
//Nested nobs not yet supported.
|
||||
if (nob.ChildNetworkObjects.Count > 0)
|
||||
{
|
||||
if (asServer)
|
||||
spawner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {spawner.ClientId} tried to spawn an object {nob.name} which has nested NetworkObjects.");
|
||||
else
|
||||
NetworkManager.LogError($"Predicted spawning prefabs which contain nested NetworkObjects is not yet supported but will be in a later release.");
|
||||
|
||||
reader?.Clear();
|
||||
return false;
|
||||
}
|
||||
//Blocked by PredictedSpawn settings or user logic.
|
||||
if ((asServer && !nob.PredictedSpawn.OnTrySpawnServer(spawner, owner))
|
||||
|| (!asServer && !nob.PredictedSpawn.OnTrySpawnClient()))
|
||||
{
|
||||
reader?.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if a NetworkObject meets basic criteria for being predicted despawned.
|
||||
/// </summary>
|
||||
/// <param name="reader">If not null reader will be cleared on error.</param>
|
||||
/// <returns></returns>
|
||||
protected bool CanPredictedDespawn(NetworkObject nob, NetworkConnection despawner, bool asServer, Reader reader = null)
|
||||
{
|
||||
//Does not allow predicted spawning.
|
||||
if (!nob.AllowPredictedDespawning)
|
||||
{
|
||||
if (asServer)
|
||||
despawner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {despawner.ClientId} tried to despawn an object {nob.name} which does not support predicted despawning.");
|
||||
else
|
||||
NetworkManager.LogError($"Object {nob.name} does not support predicted despawning. Modify the PredictedSpawn component settings to allow predicted despawning.");
|
||||
|
||||
reader?.Clear();
|
||||
return false;
|
||||
}
|
||||
//Parenting is not yet supported.
|
||||
if (nob.transform.parent != null)
|
||||
{
|
||||
if (asServer)
|
||||
despawner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {despawner.ClientId} tried to despawn an object that is not root.");
|
||||
else
|
||||
NetworkManager.LogError($"Predicted despawning as a child is not supported.");
|
||||
|
||||
reader?.Clear();
|
||||
return false;
|
||||
}
|
||||
//Nested nobs not yet supported.
|
||||
if (nob.ChildNetworkObjects.Count > 0)
|
||||
{
|
||||
if (asServer)
|
||||
despawner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {despawner.ClientId} tried to despawn an object {nob.name} which has nested NetworkObjects.");
|
||||
else
|
||||
NetworkManager.LogError($"Predicted despawning prefabs which contain nested NetworkObjects is not yet supported but will be in a later release.");
|
||||
|
||||
reader?.Clear();
|
||||
return false;
|
||||
}
|
||||
//Blocked by PredictedSpawn settings or user logic.
|
||||
if (
|
||||
(asServer && !nob.PredictedSpawn.OnTryDepawnServer(despawner))
|
||||
|| (!asServer && !nob.PredictedSpawn.OnTryDespawnClient())
|
||||
)
|
||||
{
|
||||
reader?.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1fcb759226359ad48926ff17cbf0ec6d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
414
Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs
Normal file
414
Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs
Normal file
@ -0,0 +1,414 @@
|
||||
using FishNet.Component.Observing;
|
||||
using FishNet.Connection;
|
||||
using FishNet.Managing.Logging;
|
||||
using FishNet.Object;
|
||||
using FishNet.Serializing;
|
||||
using FishNet.Transporting;
|
||||
using FishNet.Utility.Extension;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace FishNet.Managing.Object
|
||||
{
|
||||
public abstract partial class ManagedObjects
|
||||
{
|
||||
#region Public.
|
||||
/// <summary>
|
||||
/// NetworkObjects which are currently active.
|
||||
/// </summary>
|
||||
public Dictionary<int, NetworkObject> Spawned = new Dictionary<int, NetworkObject>();
|
||||
/// <summary>
|
||||
/// NetworkObjects which are currently active on the local client.
|
||||
/// //TODO Move this to ClientObjects.
|
||||
/// </summary>
|
||||
internal List<NetworkObject> LocalClientSpawned = new List<NetworkObject>();
|
||||
#endregion
|
||||
|
||||
#region Protected.
|
||||
/// <summary>
|
||||
/// Returns the next ObjectId to use.
|
||||
/// </summary>
|
||||
protected internal virtual int GetNextNetworkObjectId(bool errorCheck = true) => NetworkObject.UNSET_OBJECTID_VALUE;
|
||||
/// <summary>
|
||||
/// NetworkManager handling this.
|
||||
/// </summary>
|
||||
protected NetworkManager NetworkManager { get; private set; }
|
||||
/// <summary>
|
||||
/// Objects in currently loaded scenes. These objects can be active or inactive.
|
||||
/// Key is the objectId while value is the object. Key is not the same as NetworkObject.ObjectId.
|
||||
/// </summary>
|
||||
protected Dictionary<ulong, NetworkObject> SceneObjects = new Dictionary<ulong, NetworkObject>();
|
||||
#endregion
|
||||
|
||||
protected void Initialize(NetworkManager manager)
|
||||
{
|
||||
NetworkManager = manager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to SceneManager.SceneLoaded event.
|
||||
/// </summary>
|
||||
/// <param name="subscribe"></param>
|
||||
internal void SubscribeToSceneLoaded(bool subscribe)
|
||||
{
|
||||
if (subscribe)
|
||||
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
|
||||
else
|
||||
SceneManager.sceneLoaded -= SceneManager_sceneLoaded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a scene is loaded.
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <param name="arg1"></param>
|
||||
protected internal virtual void SceneManager_sceneLoaded(Scene s, LoadSceneMode arg1) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called when a NetworkObject runs Deactivate.
|
||||
/// </summary>
|
||||
/// <param name="nob"></param>
|
||||
internal virtual void NetworkObjectUnexpectedlyDestroyed(NetworkObject nob, bool asServer)
|
||||
{
|
||||
if (nob == null)
|
||||
return;
|
||||
|
||||
RemoveFromSpawned(nob, true, asServer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a NetworkedObject from spawned.
|
||||
/// </summary>
|
||||
private void RemoveFromSpawned(NetworkObject nob, bool unexpectedlyDestroyed, bool asServer)
|
||||
{
|
||||
Spawned.Remove(nob.ObjectId);
|
||||
if (!asServer)
|
||||
LocalClientSpawned.Remove(nob);
|
||||
//Do the same with SceneObjects.
|
||||
if (unexpectedlyDestroyed && nob.IsSceneObject)
|
||||
RemoveFromSceneObjects(nob);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Despawns a NetworkObject.
|
||||
/// </summary>
|
||||
internal virtual void Despawn(NetworkObject nob, DespawnType despawnType, bool asServer)
|
||||
{
|
||||
if (nob == null)
|
||||
{
|
||||
NetworkManager.LogWarning($"Cannot despawn a null NetworkObject.");
|
||||
return;
|
||||
}
|
||||
|
||||
//True if should be destroyed, false if deactivated.
|
||||
bool destroy = false;
|
||||
/* Only modify object state if asServer,
|
||||
* or !asServer and not host. This is so clients, when acting as
|
||||
* host, don't destroy objects they lost observation of. */
|
||||
|
||||
/* Nested prefabs can never be destroyed. Only check to
|
||||
* destroy if not nested. By nested prefab, this means the object
|
||||
* despawning is part of another prefab that is also a spawned
|
||||
* network object. */
|
||||
if (!nob.IsNested)
|
||||
{
|
||||
//If as server.
|
||||
if (asServer)
|
||||
{
|
||||
//Scene object.
|
||||
if (!nob.IsSceneObject)
|
||||
{
|
||||
/* If client-host has visibility
|
||||
* then disable and wait for client-host to get destroy
|
||||
* message. Otherwise destroy immediately. */
|
||||
if (nob.Observers.Contains(NetworkManager.ClientManager.Connection))
|
||||
NetworkManager.ServerManager.Objects.AddToPending(nob);
|
||||
else
|
||||
destroy = true;
|
||||
}
|
||||
}
|
||||
//Not as server.
|
||||
else
|
||||
{
|
||||
bool isServer = NetworkManager.IsServer;
|
||||
//Only check to destroy if not a scene object.
|
||||
if (!nob.IsSceneObject)
|
||||
{
|
||||
/* If was removed from pending then also destroy.
|
||||
* Pending objects are ones that exist on the server
|
||||
* side only to await destruction from client side.
|
||||
* Objects can also be destroyed if server is not
|
||||
* active. */
|
||||
destroy = (!isServer || NetworkManager.ServerManager.Objects.RemoveFromPending(nob.ObjectId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Deinitialize to invoke callbacks.
|
||||
nob.Deinitialize(asServer);
|
||||
//Remove from match condition only if server.
|
||||
if (asServer)
|
||||
MatchCondition.RemoveFromMatchWithoutRebuild(nob, NetworkManager);
|
||||
RemoveFromSpawned(nob, false, asServer);
|
||||
|
||||
//If to destroy.
|
||||
if (destroy)
|
||||
{
|
||||
if (despawnType == DespawnType.Destroy)
|
||||
MonoBehaviour.Destroy(nob.gameObject);
|
||||
else
|
||||
NetworkManager.StorePooledInstantiated(nob, asServer);
|
||||
}
|
||||
/* If to potentially disable instead of destroy.
|
||||
* This is such as something is despawning server side
|
||||
* but a clientHost is present, or if a scene object. */
|
||||
else
|
||||
{
|
||||
//If as server.
|
||||
if (asServer)
|
||||
{
|
||||
//If not clientHost then the object can be disabled.
|
||||
if (!NetworkManager.IsClient)
|
||||
nob.gameObject.SetActive(false);
|
||||
}
|
||||
//Not as server.
|
||||
else
|
||||
{
|
||||
//If the server is not active then the object can be disabled.
|
||||
if (!NetworkManager.IsServer)
|
||||
{
|
||||
nob.gameObject.SetActive(false);
|
||||
}
|
||||
//If also server then checks must be done.
|
||||
else
|
||||
{
|
||||
/* Object is still spawned on the server side. This means
|
||||
* the clientHost likely lost visibility. When this is the case
|
||||
* update clientHost renderers. */
|
||||
if (NetworkManager.ServerManager.Objects.Spawned.ContainsKey(nob.ObjectId))
|
||||
nob.SetRenderersVisible(false);
|
||||
/* No longer spawned on the server, can
|
||||
* deactivate on the client. */
|
||||
else
|
||||
nob.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Also despawn child objects.
|
||||
* This only must be done when not destroying
|
||||
* as destroying would result in the despawn being
|
||||
* forced.
|
||||
*
|
||||
* Only run if asServer as well. The server will send
|
||||
* individual despawns for each child. */
|
||||
if (asServer)
|
||||
{
|
||||
foreach (NetworkObject childNob in nob.ChildNetworkObjects)
|
||||
{
|
||||
if (childNob != null && !childNob.IsDeinitializing)
|
||||
Despawn(childNob, despawnType, asServer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates NetworkBehaviours on nob.
|
||||
/// </summary>
|
||||
/// <param name="asServer"></param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void UpdateNetworkBehavioursForSceneObject(NetworkObject nob, bool asServer)
|
||||
{
|
||||
//Would have already been done on server side.
|
||||
if (!asServer && NetworkManager.IsServer)
|
||||
return;
|
||||
|
||||
InitializePrefab(nob, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a prefab, not to be mistaken for initializing a spawned object.
|
||||
/// </summary>
|
||||
/// <param name="prefab">Prefab to initialize.</param>
|
||||
/// <param name="index">Index within spawnable prefabs.</param>
|
||||
public static void InitializePrefab(NetworkObject prefab, int index, ushort? collectionId = null)
|
||||
{
|
||||
if (prefab == null)
|
||||
return;
|
||||
/* Only set the Id if not -1.
|
||||
* A value of -1 would indicate it's a scene
|
||||
* object. */
|
||||
if (index != -1)
|
||||
{
|
||||
//Use +1 because 0 indicates unset.
|
||||
prefab.PrefabId = (ushort)index;
|
||||
if (collectionId != null)
|
||||
prefab.SpawnableCollectionId = collectionId.Value;
|
||||
}
|
||||
|
||||
byte componentIndex = 0;
|
||||
prefab.UpdateNetworkBehaviours(null, ref componentIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Despawns Spawned NetworkObjects. Scene objects will be disabled, others will be destroyed.
|
||||
/// </summary>
|
||||
internal virtual void DespawnWithoutSynchronization(bool asServer)
|
||||
{
|
||||
foreach (NetworkObject nob in Spawned.Values)
|
||||
DespawnWithoutSynchronization(nob, asServer, nob.GetDefaultDespawnType(), false);
|
||||
|
||||
Spawned.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Despawns a network object.
|
||||
/// </summary>
|
||||
/// <param name="nob"></param>
|
||||
internal virtual void DespawnWithoutSynchronization(NetworkObject nob, bool asServer, DespawnType despawnType, bool removeFromSpawned)
|
||||
{
|
||||
//Null can occur when running as host and server already despawns such as when stopping.
|
||||
if (nob == null)
|
||||
return;
|
||||
|
||||
nob.Deinitialize(asServer);
|
||||
/* Only run if asServer, or not
|
||||
* asServer and server isn't running. This
|
||||
* prevents objects from affecting the server
|
||||
* as host when being modified client side. */
|
||||
if (asServer || (!asServer && !NetworkManager.IsServer))
|
||||
{
|
||||
if (removeFromSpawned)
|
||||
RemoveFromSpawned(nob, false, asServer);
|
||||
if (nob.IsSceneObject)
|
||||
{
|
||||
nob.gameObject.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (despawnType == DespawnType.Destroy)
|
||||
MonoBehaviour.Destroy(nob.gameObject);
|
||||
else
|
||||
NetworkManager.StorePooledInstantiated(nob, asServer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a NetworkObject to Spawned.
|
||||
/// </summary>
|
||||
/// <param name="nob"></param>
|
||||
internal void AddToSpawned(NetworkObject nob, bool asServer)
|
||||
{
|
||||
Spawned[nob.ObjectId] = nob;
|
||||
if (!asServer)
|
||||
{
|
||||
LocalClientSpawned.Add(nob);
|
||||
//If being added as client and is also server.
|
||||
if (NetworkManager.IsServer)
|
||||
nob.SetRenderersVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a NetworkObject to SceneObjects.
|
||||
/// </summary>
|
||||
/// <param name="nob"></param>
|
||||
protected internal void AddToSceneObjects(NetworkObject nob)
|
||||
{
|
||||
SceneObjects[nob.SceneId] = nob;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a NetworkObject from SceneObjects.
|
||||
/// </summary>
|
||||
/// <param name="nob"></param>
|
||||
protected internal void RemoveFromSceneObjects(NetworkObject nob)
|
||||
{
|
||||
SceneObjects.Remove(nob.SceneId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a NetworkObject from SceneObjects.
|
||||
/// </summary>
|
||||
/// <param name="nob"></param>
|
||||
protected internal void RemoveFromSceneObjects(ulong sceneId)
|
||||
{
|
||||
SceneObjects.Remove(sceneId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a NetworkObject within Spawned.
|
||||
/// </summary>
|
||||
/// <param name="objectId"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected internal NetworkObject GetSpawnedNetworkObject(int objectId)
|
||||
{
|
||||
NetworkObject r;
|
||||
if (!Spawned.TryGetValueIL2CPP(objectId, out r))
|
||||
NetworkManager.LogError($"Spawned NetworkObject not found for ObjectId {objectId}.");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to skip data length for a packet.
|
||||
/// </summary>
|
||||
/// <param name="packetId"></param>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="dataLength"></param>
|
||||
protected internal void SkipDataLength(ushort packetId, PooledReader reader, int dataLength, int rpcLinkObjectId = -1)
|
||||
{
|
||||
/* -1 means length wasn't set, which would suggest a reliable packet.
|
||||
* Object should never be missing for reliable packets since spawns
|
||||
* and despawns are reliable in order. */
|
||||
if (dataLength == (int)MissingObjectPacketLength.Reliable)
|
||||
{
|
||||
string msg;
|
||||
bool isRpcLink = (packetId >= NetworkManager.StartingRpcLinkIndex);
|
||||
if (isRpcLink)
|
||||
{
|
||||
msg = (rpcLinkObjectId == -1) ?
|
||||
$"RPCLink of Id {(PacketId)packetId} could not be found. Remaining data will be purged." :
|
||||
$"ObjectId {rpcLinkObjectId} for RPCLink {(PacketId)packetId} could not be found.";
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = $"NetworkBehaviour could not be found for packetId {(PacketId)packetId}. Remaining data will be purged.";
|
||||
}
|
||||
|
||||
/* Default logging for server is errors only. Use error on client and warning
|
||||
* on servers to reduce chances of allocation attacks. */
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR || !UNITY_SERVER
|
||||
NetworkManager.LogError(msg);
|
||||
#else
|
||||
if (NetworkManager.CanLog(LoggingType.Warning))
|
||||
Debug.LogWarning(msg);
|
||||
#endif
|
||||
reader.Clear();
|
||||
}
|
||||
/* If length is known then is unreliable packet. It's possible
|
||||
* this packetId arrived before or after the object was spawned/destroyed.
|
||||
* Skip past the data for this packet and use rest in reader. With non-linked
|
||||
* RPCs length is sent before object information. */
|
||||
else if (dataLength >= 0)
|
||||
{
|
||||
reader.Skip(Math.Min(dataLength, reader.Remaining));
|
||||
}
|
||||
/* -2 indicates the length is very long. Don't even try saving
|
||||
* the packet, user shouldn't be sending this much data over unreliable. */
|
||||
else if (dataLength == (int)MissingObjectPacketLength.PurgeRemaiming)
|
||||
{
|
||||
reader.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1363007244792145846afddc31ac12c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
31
Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs
Normal file
31
Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using FishNet.Documenting;
|
||||
|
||||
namespace FishNet.Managing.Object
|
||||
{
|
||||
internal enum SpawnType : byte
|
||||
{
|
||||
Unset = 0,
|
||||
Nested = 1,
|
||||
Scene = 2,
|
||||
Instantiated = 4,
|
||||
InstantiatedGlobal = 8,
|
||||
}
|
||||
|
||||
[APIExclude]
|
||||
internal static partial class SpawnTypeEnum
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns if whole contains part.
|
||||
/// </summary>
|
||||
/// <param name="whole"></param>
|
||||
/// <param name="part"></param>
|
||||
/// <returns></returns>
|
||||
public static bool Contains(SpawnType whole, SpawnType part)
|
||||
{
|
||||
return (whole & part) == part;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed15edf5a1a100d45b05f6adace574cd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 681483aaaf105014b93c3c89c7f43fda
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,104 @@
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Object.Helping;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using FishNet.Editing;
|
||||
using UnityEditor;
|
||||
#endif
|
||||
using FishNet.Object;
|
||||
|
||||
namespace FishNet.Managing.Object
|
||||
{
|
||||
|
||||
[APIExclude]
|
||||
//[CreateAssetMenu(fileName = "New DefaultPrefabObjects", menuName = "FishNet/Spawnable Prefabs/Default Prefab Objects")]
|
||||
public class DefaultPrefabObjects : SinglePrefabObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets asset path hashes for prefabs starting at index, or if missing.
|
||||
/// </summary
|
||||
/// <return>Returns true if one or more NetworkObjects were updated.</return>
|
||||
internal bool SetAssetPathHashes(int index)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
bool dirtied = false;
|
||||
int count = base.GetObjectCount();
|
||||
|
||||
if (count == 0)
|
||||
return false;
|
||||
if (index < 0 || index >= count)
|
||||
{
|
||||
Debug.LogError($"Index {index} is out of range when trying to set asset path hashes. Collection length is {count}. Defaulf prefabs may need to be rebuilt.");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
NetworkObject n = base.Prefabs[i];
|
||||
if (i < index)
|
||||
continue;
|
||||
|
||||
string pathAndName = $"{AssetDatabase.GetAssetPath(n.gameObject)}{n.gameObject.name}";
|
||||
ulong hashcode = Hashing.GetStableHash64(pathAndName);
|
||||
//Already set.
|
||||
if (n.AssetPathHash == hashcode)
|
||||
continue;
|
||||
|
||||
n.SetAssetPathHash(hashcode);
|
||||
EditorUtility.SetDirty(n);
|
||||
dirtied = true;
|
||||
}
|
||||
|
||||
return dirtied;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sorts prefabs by name and path hashcode.
|
||||
/// </summary>
|
||||
internal void Sort()
|
||||
{
|
||||
if (base.GetObjectCount() == 0)
|
||||
return;
|
||||
|
||||
Dictionary<ulong, NetworkObject> hashcodesAndNobs = new Dictionary<ulong, NetworkObject>();
|
||||
List<ulong> hashcodes = new List<ulong>();
|
||||
|
||||
bool error = false;
|
||||
foreach (NetworkObject n in base.Prefabs)
|
||||
{
|
||||
hashcodes.Add(n.AssetPathHash);
|
||||
//If hashcode is 0 something is wrong
|
||||
if (n.AssetPathHash == 0)
|
||||
{
|
||||
error = true;
|
||||
Debug.LogError($"AssetPathHash is not set for GameObject {n.name}.");
|
||||
|
||||
}
|
||||
hashcodesAndNobs.Add(n.AssetPathHash, n);
|
||||
}
|
||||
//An error occured, no reason to continue.
|
||||
if (error)
|
||||
{
|
||||
Debug.LogError($"One or more NetworkObject prefabs did not have their AssetPathHash set. This usually occurs when a prefab cannot be saved. Check the specified prefabs for missing scripts or serialization errors and correct them, then use Fish-Networking -> Refresh Default Prefabs.");
|
||||
return;
|
||||
}
|
||||
|
||||
//Once all hashes have been made re-add them to prefabs sorted.
|
||||
hashcodes.Sort();
|
||||
//Build to a new list using sorted hashcodes.
|
||||
List<NetworkObject> sortedNobs = new List<NetworkObject>();
|
||||
foreach (ulong hc in hashcodes)
|
||||
sortedNobs.Add(hashcodesAndNobs[hc]);
|
||||
|
||||
base.Clear();
|
||||
base.AddObjects(sortedNobs, false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ad70174b079c2f4ebc7931d3dd1af6f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,136 @@
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Object;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Managing.Object
|
||||
{
|
||||
|
||||
//document
|
||||
[APIExclude]
|
||||
[CreateAssetMenu(fileName = "New DualPrefabObjects", menuName = "FishNet/Spawnable Prefabs/Dual Prefab Objects")]
|
||||
public class DualPrefabObjects : PrefabObjects
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Tooltip("Prefabs which may be spawned.")]
|
||||
[SerializeField]
|
||||
private List<DualPrefab> _prefabs = new List<DualPrefab>();
|
||||
/// <summary>
|
||||
/// Prefabs which may be spawned.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<DualPrefab> Prefabs => _prefabs;
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
_prefabs.Clear();
|
||||
}
|
||||
public override int GetObjectCount()
|
||||
{
|
||||
return _prefabs.Count;
|
||||
}
|
||||
|
||||
public override NetworkObject GetObject(bool asServer, int id)
|
||||
{
|
||||
if (id < 0 || id >= _prefabs.Count)
|
||||
{
|
||||
NetworkManager.StaticLogError($"PrefabId {id} is out of range.");
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
DualPrefab dp = _prefabs[id];
|
||||
NetworkObject nob = (asServer) ? dp.Server : dp.Client;
|
||||
if (nob == null)
|
||||
{
|
||||
string lookupSide = (asServer) ? "server" : "client";
|
||||
NetworkManager.StaticLogError($"Prefab for {lookupSide} on id {id} is null ");
|
||||
}
|
||||
|
||||
return nob;
|
||||
}
|
||||
}
|
||||
|
||||
public override void RemoveNull()
|
||||
{
|
||||
for (int i = 0; i < _prefabs.Count; i++)
|
||||
{
|
||||
if (_prefabs[i].Server == null || _prefabs[i].Client == null)
|
||||
{
|
||||
_prefabs.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (Application.isPlaying)
|
||||
InitializePrefabRange(0);
|
||||
}
|
||||
|
||||
public override void AddObject(DualPrefab dualPrefab, bool checkForDuplicates = false)
|
||||
{
|
||||
AddObjects(new DualPrefab[] { dualPrefab }, checkForDuplicates);
|
||||
}
|
||||
|
||||
public override void AddObjects(List<DualPrefab> dualPrefabs, bool checkForDuplicates = false)
|
||||
{
|
||||
AddObjects(dualPrefabs.ToArray(), checkForDuplicates);
|
||||
}
|
||||
|
||||
public override void AddObjects(DualPrefab[] dualPrefabs, bool checkForDuplicates = false)
|
||||
{
|
||||
if (!checkForDuplicates)
|
||||
{
|
||||
_prefabs.AddRange(dualPrefabs);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (DualPrefab dp in dualPrefabs)
|
||||
AddUniqueNetworkObjects(dp);
|
||||
}
|
||||
|
||||
if (Application.isPlaying)
|
||||
InitializePrefabRange(0);
|
||||
}
|
||||
|
||||
private void AddUniqueNetworkObjects(DualPrefab dp)
|
||||
{
|
||||
for (int i = 0; i < _prefabs.Count; i++)
|
||||
{
|
||||
if (_prefabs[i].Server == dp.Server && _prefabs[i].Client == dp.Client)
|
||||
return;
|
||||
}
|
||||
|
||||
_prefabs.Add(dp);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void InitializePrefabRange(int startIndex)
|
||||
{
|
||||
for (int i = startIndex; i < _prefabs.Count; i++)
|
||||
{
|
||||
ManagedObjects.InitializePrefab(_prefabs[i].Server, i, CollectionId);
|
||||
ManagedObjects.InitializePrefab(_prefabs[i].Client, i, CollectionId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Unused.
|
||||
public override void AddObject(NetworkObject networkObject, bool checkForDuplicates = false)
|
||||
{
|
||||
NetworkManager.StaticLogError($"Single prefabs are not supported with DualPrefabObjects. Make a SinglePrefabObjects asset instead.");
|
||||
}
|
||||
|
||||
public override void AddObjects(List<NetworkObject> networkObjects, bool checkForDuplicates = false)
|
||||
{
|
||||
NetworkManager.StaticLogError($"Single prefabs are not supported with DualPrefabObjects. Make a SinglePrefabObjects asset instead.");
|
||||
}
|
||||
|
||||
public override void AddObjects(NetworkObject[] networkObjects, bool checkForDuplicates = false)
|
||||
{
|
||||
NetworkManager.StaticLogError($"Single prefabs are not supported with DualPrefabObjects. Make a SinglePrefabObjects asset instead.");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4b890523e001c74a9a2bf0d6340e5f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,36 @@
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Object;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Managing.Object
|
||||
{
|
||||
//document
|
||||
[APIExclude]
|
||||
public abstract class PrefabObjects : ScriptableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// CollectionId for this PrefabObjects.
|
||||
/// </summary>
|
||||
public ushort CollectionId { get; private set; }
|
||||
/// <summary>
|
||||
/// Sets CollectionIdValue.
|
||||
/// </summary>
|
||||
internal void SetCollectionId(ushort id) => CollectionId = id;
|
||||
|
||||
public abstract void Clear();
|
||||
public abstract int GetObjectCount();
|
||||
public abstract NetworkObject GetObject(bool asServer, int id);
|
||||
public abstract void RemoveNull();
|
||||
public abstract void AddObject(NetworkObject networkObject, bool checkForDuplicates = false);
|
||||
public abstract void AddObjects(List<NetworkObject> networkObjects, bool checkForDuplicates = false);
|
||||
public abstract void AddObjects(NetworkObject[] networkObjects, bool checkForDuplicates = false);
|
||||
public abstract void AddObject(DualPrefab dualPrefab, bool checkForDuplicates = false);
|
||||
public abstract void AddObjects(List<DualPrefab> dualPrefab, bool checkForDuplicates = false);
|
||||
public abstract void AddObjects(DualPrefab[] dualPrefab, bool checkForDuplicates = false);
|
||||
public abstract void InitializePrefabRange(int startIndex);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5a7beb0d6ee75a4fb1f058eb3e2640a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,128 @@
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Object;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Managing.Object
|
||||
{
|
||||
//document
|
||||
[APIExclude]
|
||||
[CreateAssetMenu(fileName = "New SinglePrefabObjects", menuName = "FishNet/Spawnable Prefabs/Single Prefab Objects")]
|
||||
public class SinglePrefabObjects : PrefabObjects
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Tooltip("Prefabs which may be spawned.")]
|
||||
[SerializeField]
|
||||
private List<NetworkObject> _prefabs = new List<NetworkObject>();
|
||||
/// <summary>
|
||||
/// Prefabs which may be spawned.
|
||||
/// </summary>
|
||||
public IReadOnlyList<NetworkObject> Prefabs => _prefabs;
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
_prefabs.Clear();
|
||||
}
|
||||
public override int GetObjectCount()
|
||||
{
|
||||
return _prefabs.Count;
|
||||
}
|
||||
public override NetworkObject GetObject(bool asServer, int id)
|
||||
{
|
||||
if (id < 0 || id >= _prefabs.Count)
|
||||
{
|
||||
NetworkManager.StaticLogError($"PrefabId {id} is out of range.");
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
NetworkObject nob = _prefabs[id];
|
||||
if (nob == null)
|
||||
NetworkManager.StaticLogError($"Prefab on id {id} is null.");
|
||||
|
||||
return nob;
|
||||
}
|
||||
}
|
||||
|
||||
public override void RemoveNull()
|
||||
{
|
||||
for (int i = 0; i < _prefabs.Count; i++)
|
||||
{
|
||||
if (_prefabs[i] == null)
|
||||
{
|
||||
_prefabs.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (Application.isPlaying)
|
||||
InitializePrefabRange(0);
|
||||
}
|
||||
|
||||
public override void AddObject(NetworkObject networkObject, bool checkForDuplicates = false)
|
||||
{
|
||||
if (!checkForDuplicates)
|
||||
_prefabs.Add(networkObject);
|
||||
else
|
||||
AddUniqueNetworkObject(networkObject);
|
||||
|
||||
if (Application.isPlaying)
|
||||
InitializePrefabRange(0);
|
||||
}
|
||||
|
||||
public override void AddObjects(List<NetworkObject> networkObjects, bool checkForDuplicates = false)
|
||||
{
|
||||
if (!checkForDuplicates)
|
||||
{
|
||||
_prefabs.AddRange(networkObjects);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (NetworkObject nob in networkObjects)
|
||||
AddUniqueNetworkObject(nob);
|
||||
}
|
||||
|
||||
if (Application.isPlaying)
|
||||
InitializePrefabRange(0);
|
||||
}
|
||||
public override void AddObjects(NetworkObject[] networkObjects, bool checkForDuplicates = false)
|
||||
{
|
||||
AddObjects(networkObjects.ToList(), checkForDuplicates);
|
||||
}
|
||||
|
||||
private void AddUniqueNetworkObject(NetworkObject nob)
|
||||
{
|
||||
if (!_prefabs.Contains(nob))
|
||||
_prefabs.Add(nob);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void InitializePrefabRange(int startIndex)
|
||||
{
|
||||
for (int i = startIndex; i < _prefabs.Count; i++)
|
||||
ManagedObjects.InitializePrefab(_prefabs[i], i, CollectionId);
|
||||
}
|
||||
|
||||
|
||||
#region Unused.
|
||||
public override void AddObject(DualPrefab dualPrefab, bool checkForDuplicates = false)
|
||||
{
|
||||
NetworkManager.StaticLogError($"Dual prefabs are not supported with SinglePrefabObjects. Make a DualPrefabObjects asset instead.");
|
||||
}
|
||||
|
||||
public override void AddObjects(List<DualPrefab> dualPrefab, bool checkForDuplicates = false)
|
||||
{
|
||||
NetworkManager.StaticLogError($"Dual prefabs are not supported with SinglePrefabObjects. Make a DualPrefabObjects asset instead.");
|
||||
}
|
||||
|
||||
public override void AddObjects(DualPrefab[] dualPrefab, bool checkForDuplicates = false)
|
||||
{
|
||||
NetworkManager.StaticLogError($"Dual prefabs are not supported with SinglePrefabObjects. Make a DualPrefabObjects asset instead.");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4489d77032a81ef42b0067acf2737d4d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
10
Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs
Normal file
10
Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace FishNet.Managing.Object
|
||||
{
|
||||
public enum SpawnParentType : byte
|
||||
{
|
||||
Unset = 0,
|
||||
NetworkObject = 1,
|
||||
NetworkBehaviour = 2
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbace351ced9ff94eb294dbb2e1d6a75
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
1
Assets/FishNet/Runtime/Managing/Object/SpawnWriter.cs
Normal file
1
Assets/FishNet/Runtime/Managing/Object/SpawnWriter.cs
Normal file
@ -0,0 +1 @@
|
||||
//Remove on 2023/06/01
|
11
Assets/FishNet/Runtime/Managing/Object/SpawnWriter.cs.meta
Normal file
11
Assets/FishNet/Runtime/Managing/Object/SpawnWriter.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd305e51107fc3441a6f52636c27298f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user