diff --git a/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs
index e8c0c23f..92ee6ff3 100644
--- a/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs
+++ b/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs
@@ -15,6 +15,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices.ComTypes;
+using GameKit.Dependencies.Utilities.Types;
using UnityEngine;
using SR = System.Reflection;
@@ -28,6 +29,7 @@ internal class GeneralHelper : CodegenBase
public MethodReference Extension_Attribute_Ctor_MethodRef;
public MethodReference BasicQueue_Clear_MethodRef;
public TypeReference List_TypeRef;
+ public TypeReference RingBuffer_TypeRef;
public MethodReference List_Clear_MethodRef;
public MethodReference List_get_Item_MethodRef;
public MethodReference List_get_Count_MethodRef;
@@ -160,6 +162,9 @@ public override bool ImportReferences()
//Lists.
tmpType = typeof(List<>);
List_TypeRef = base.ImportReference(tmpType);
+ tmpType = typeof(RingBuffer<>);
+ RingBuffer_TypeRef = base.ImportReference(tmpType);
+
SR.MethodInfo lstMi;
lstMi = tmpType.GetMethod("Add");
List_Add_MethodRef = base.ImportReference(lstMi);
@@ -231,8 +236,6 @@ void GeneratedComparers()
return true;
}
-
-
#region Resolves.
///
/// Adds a typeRef to TypeReferenceResolves.
@@ -288,7 +291,6 @@ public MethodDefinition GetMethodReferenceResolve(MethodReference methodRef)
return result;
}
-
///
/// Adds a fieldRef to FieldReferenceResolves.
///
@@ -317,7 +319,6 @@ public FieldDefinition GetFieldReferenceResolve(FieldReference fieldRef)
}
#endregion
-
///
/// Makes a method an extension method.
///
@@ -419,10 +420,10 @@ public bool HasNotSerializableAttribute(SR.MethodInfo methodInfo)
if (item.AttributeType.FullName == NotSerializerAttribute_FullName)
return true;
}
-
+
return false;
}
-
+
///
/// Returns if type uses CodegenExcludeAttribute.
///
@@ -433,7 +434,7 @@ public bool HasNotSerializableAttribute(MethodDefinition methodDef)
if (item.AttributeType.FullName == NotSerializerAttribute_FullName)
return true;
}
-
+
return false;
}
#endregion
@@ -450,7 +451,6 @@ public void CallCopiedMethod(MethodDefinition md, MethodDefinition copiedMd)
MethodReference mr = copiedMd.GetMethodReference(base.Session);
processor.Emit(OpCodes.Call, mr);
-
}
///
@@ -461,15 +461,14 @@ public List ListRemoveRange(MethodDefinition methodDef, FieldDefini
/* Remove entries which exceed maximum buffer. */
//Method references for uint/data list:
//get_count, RemoveRange. */
- GenericInstanceType dataListGit;
- GetGenericList(dataTr, out dataListGit);
+ GenericInstanceType dataListGit = GetGenericList(dataTr);
MethodReference lstDataRemoveRangeMr = base.GetClass().List_RemoveRange_MethodRef.MakeHostInstanceGeneric(base.Session, dataListGit);
List insts = new();
ILProcessor processor = methodDef.Body.GetILProcessor();
//Index 1 is the uint, 0 is the data.
- insts.Add(processor.Create(OpCodes.Ldarg_0));//this.
+ insts.Add(processor.Create(OpCodes.Ldarg_0)); //this.
insts.Add(processor.Create(OpCodes.Ldfld, dataFd));
insts.Add(processor.Create(OpCodes.Ldc_I4_0));
insts.Add(processor.Create(OpCodes.Ldloc, countVd));
@@ -477,21 +476,51 @@ public List ListRemoveRange(MethodDefinition methodDef, FieldDefini
return insts;
}
+
+ ///
+ /// Outputs generic lists for dataTr.
+ ///
+ public GenericInstanceType GetGenericList(TypeReference dataTr)
+ {
+ TypeReference typeTr = base.ImportReference(typeof(List<>));
+ return typeTr.MakeGenericInstanceType(new TypeReference[] { dataTr });
+ }
+
+
+ ///
+ /// Outputs generic Dictionary for keyTr and valueTr.
+ ///
+ public GenericInstanceType GetGenericDictionary(TypeReference keyTr, TypeReference valueTr)
+ {
+ TypeReference typeTr = base.ImportReference(typeof(Dictionary<,>));
+ return typeTr.MakeGenericInstanceType(new TypeReference[] { keyTr, valueTr });
+ }
+
///
- /// Outputs generic lists for dataTr and uint.
+ /// Outputs generic RingBuffer for dataTr.
///
- public void GetGenericList(TypeReference dataTr, out GenericInstanceType lstData)
+ public GenericInstanceType GetGenericRingBuffer(TypeReference dataTr)
{
- TypeReference listDataTr = base.ImportReference(typeof(List<>));
- lstData = listDataTr.MakeGenericInstanceType(new TypeReference[] { dataTr });
+ TypeReference typeTr = base.ImportReference(typeof(RingBuffer<>));
+ return typeTr.MakeGenericInstanceType(new TypeReference[] { dataTr });
}
+
///
- /// Outputs generic lists for dataTr and uint.
+ /// Gets a generic instance of any type with optional arguments.
///
- public void GetGenericBasicQueue(TypeReference dataTr, out GenericInstanceType queueData)
+ public GenericInstanceType GetGenericType(Type type, params TypeReference[] datasTr)
{
- TypeReference queueDataTr = base.ImportReference(typeof(BasicQueue<>));
- queueData = queueDataTr.MakeGenericInstanceType(new TypeReference[] { dataTr });
+ TypeReference typeTr = base.ImportReference(type);
+ return typeTr.MakeGenericInstanceType(datasTr);
+ }
+
+ ///
+ /// Outputs generic BasicQueue for dataTr.
+ ///
+ public GenericInstanceType GetGenericBasicQueue(TypeReference dataTr)
+ {
+ TypeReference typeTr = base.ImportReference(typeof(BasicQueue<>));
+ return typeTr.MakeGenericInstanceType(new TypeReference[] { dataTr });
}
///
@@ -536,7 +565,6 @@ public MethodDefinition CopyMethodSignature(MethodDefinition originalMd, string
return md;
}
-
///
/// Creates the RuntimeInitializeOnLoadMethod attribute for a method.
///
@@ -609,7 +637,6 @@ public MethodDefinition GetOrCreateMethod(TypeDefinition typeDef, out bool creat
return result;
}
-
///
/// Gets a class within moduleDef or creates and returns the class if it does not already exist.
///
@@ -629,8 +656,7 @@ public TypeDefinition GetOrCreateClass(out bool created, TypeAttributes typeAttr
else
{
created = true;
- type = new(namespaceName, className,
- typeAttr, base.ImportReference(typeof(object)));
+ type = new(namespaceName, className, typeAttr, base.ImportReference(typeof(object)));
//Add base class if specified.
if (baseTypeRef != null)
type.BaseType = base.ImportReference(baseTypeRef);
@@ -657,6 +683,7 @@ public bool HasNonSerializableAttribute(FieldDefinition fieldDef)
//Fall through, no matches.
return false;
}
+
///
/// Returns if typeDef has a NonSerialized attribute.
///
@@ -731,16 +758,12 @@ public MethodDefinition GetOrCreateConstructor(TypeDefinition typeDef, out bool
else
{
created = true;
- MethodAttributes methodAttr = (MonoFN.Cecil.MethodAttributes.HideBySig |
- MonoFN.Cecil.MethodAttributes.SpecialName |
- MonoFN.Cecil.MethodAttributes.RTSpecialName);
+ MethodAttributes methodAttr = (MonoFN.Cecil.MethodAttributes.HideBySig | MonoFN.Cecil.MethodAttributes.SpecialName | MonoFN.Cecil.MethodAttributes.RTSpecialName);
if (makeStatic)
methodAttr |= MonoFN.Cecil.MethodAttributes.Static;
//Create a constructor.
- constructorMethodDef = new(".ctor", methodAttr,
- typeDef.Module.TypeSystem.Void
- );
+ constructorMethodDef = new(".ctor", methodAttr, typeDef.Module.TypeSystem.Void);
typeDef.Methods.Add(constructorMethodDef);
@@ -875,6 +898,7 @@ public ParameterDefinition CreateParameter(MethodDefinition methodDef, TypeRefer
methodDef.Parameters.Insert(index, parameterDef);
return parameterDef;
}
+
///
/// Creates a parameter within methodDef and returns it's ParameterDefinition.
///
@@ -885,6 +909,7 @@ public ParameterDefinition CreateParameter(MethodDefinition methodDef, Type para
{
return CreateParameter(methodDef, GetTypeReference(parameterType), name, attributes, index);
}
+
///
/// Creates a variable type within the body and returns it's VariableDef.
///
@@ -897,6 +922,7 @@ public VariableDefinition CreateVariable(MethodDefinition methodDef, TypeReferen
methodDef.Body.Variables.Add(variableDef);
return variableDef;
}
+
/// Creates a variable type within the body and returns it's VariableDef.
///
///
@@ -974,6 +1000,7 @@ public void SetVariableDefinitionFromInt(ILProcessor processor, VariableDefiniti
processor.Emit(OpCodes.Ldc_I4, value);
processor.Emit(OpCodes.Stloc, variableDef);
}
+
///
/// Assigns value to a VariableDef.
///
@@ -1007,7 +1034,6 @@ public bool IsCallToMethod(Instruction instruction, out MethodDefinition calledM
}
}
-
///
/// Returns if a serializer and deserializer exist for typeRef.
///
@@ -1060,11 +1086,10 @@ public MethodDefinition CreateEqualityComparer(TypeReference dataTr)
MethodDefinition comparerMd;
if (!_comparerDelegates.TryGetValue(dataTr.FullName, out comparerMd))
{
- comparerMd = GetOrCreateMethod(GeneratedComparer_ClassTypeDef, out created, WriterProcessor.GENERATED_METHOD_ATTRIBUTES,
- $"Comparer___{dataTr.FullName}", base.Module.TypeSystem.Boolean);
+ comparerMd = GetOrCreateMethod(GeneratedComparer_ClassTypeDef, out created, WriterProcessor.GENERATED_METHOD_ATTRIBUTES, $"Comparer___{dataTr.FullName}", base.Module.TypeSystem.Boolean);
/* Nullables are not yet supported for automatic
- * comparers. Let user know they must make their own. */
+ * comparers. Let user know they must make their own. */
if (dataTr.IsGenericInstance)
{
base.LogError($"Equality comparers cannot be automatically generated for generic types. Create a custom comparer for {dataTr.FullName}.");
@@ -1091,7 +1116,7 @@ void CreateComparerMethod()
ILProcessor processor = comparerMd.Body.GetILProcessor();
comparerMd.Body.InitLocals = true;
- /* If type is a Unity type do not try to
+ /* If type is a Unity type do not try to
* create a comparer other than ref comparer, as Unity will have built in ones. */
if (dataTr.CachedResolve(base.Session).Module.Name.Contains("UnityEngine"))
{
@@ -1147,15 +1172,13 @@ void CreateGenericInstanceComparer()
}
-
void CreateClassOrStructComparer()
{
//Class or struct.
Instruction exitMethodInst = processor.Create(OpCodes.Ldc_I4_0);
//Fields.
- foreach (FieldDefinition fieldDef in dataTr.FindAllSerializableFields(base.Session
- , null, WriterProcessor.EXCLUDED_ASSEMBLY_PREFIXES))
+ foreach (FieldDefinition fieldDef in dataTr.FindAllSerializableFields(base.Session, null, WriterProcessor.EXCLUDED_ASSEMBLY_PREFIXES))
{
FieldReference fr = base.ImportReference(fieldDef);
MethodDefinition recursiveMd = CreateEqualityComparer(fieldDef.FieldType);
@@ -1169,8 +1192,7 @@ void CreateClassOrStructComparer()
}
//Properties.
- foreach (PropertyDefinition propertyDef in dataTr.FindAllSerializableProperties(base.Session
- , null, WriterProcessor.EXCLUDED_ASSEMBLY_PREFIXES))
+ foreach (PropertyDefinition propertyDef in dataTr.FindAllSerializableProperties(base.Session, null, WriterProcessor.EXCLUDED_ASSEMBLY_PREFIXES))
{
MethodReference getMr = base.Module.ImportReference(propertyDef.GetMethod);
MethodDefinition recursiveMd = CreateEqualityComparer(getMr.ReturnType);
@@ -1229,7 +1251,6 @@ void FinishTypeReferenceCompare(TypeReference tr)
{
processor.Emit(OpCodes.Bne_Un, exitMethodInst);
}
-
}
}
@@ -1241,9 +1262,7 @@ void CreateValueOrReferenceComparer()
processor.Emit(OpCodes.Ceq);
processor.Emit(OpCodes.Ret);
}
-
}
-
}
///
@@ -1255,6 +1274,7 @@ public void RegisterComparerDelegate(MethodDefinition methodDef, TypeReference d
{
_comparerDelegates.Add(dataTr.FullName, methodDef);
}
+
///
/// Creates a delegate for GeneratedComparers.
///
@@ -1280,8 +1300,6 @@ public void CreateComparerDelegate(MethodDefinition comparerMd, TypeReference da
processor.InsertFirst(insts);
}
-
-
///
/// Returns an OpCode for loading a parameter.
///
@@ -1308,8 +1326,7 @@ public void CreateIsDefaultComparer(TypeReference dataTr, MethodDefinition compa
{
GeneralHelper gh = base.GetClass();
- MethodDefinition isDefaultMd = gh.GetOrCreateMethod(GeneratedComparer_ClassTypeDef, out bool created, WriterProcessor.GENERATED_METHOD_ATTRIBUTES,
- $"IsDefault___{dataTr.FullName}", base.Module.TypeSystem.Boolean);
+ MethodDefinition isDefaultMd = gh.GetOrCreateMethod(GeneratedComparer_ClassTypeDef, out bool created, WriterProcessor.GENERATED_METHOD_ATTRIBUTES, $"IsDefault___{dataTr.FullName}", base.Module.TypeSystem.Boolean);
//Already done. This can happen if the same replicate data is used in multiple places.
if (!created)
return;
@@ -1344,8 +1361,6 @@ void CreateIsDefaultMethod()
processor.Emit(OpCodes.Call, compareMr);
processor.Emit(OpCodes.Ret);
-
-
}
//Creates a delegate to compare two of replicateTr.
@@ -1369,7 +1384,6 @@ void CreateIsDefaultDelegate()
insts.Add(processor.Create(OpCodes.Call, isDefaultMr));
processor.InsertFirst(insts);
}
-
}
#endregion
}
diff --git a/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs
index 6f0952ee..c1bc89c4 100644
--- a/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs
+++ b/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs
@@ -33,6 +33,7 @@ internal class NetworkBehaviourHelper : CodegenBase
public MethodReference Replicate_NonAuthoritative_MethodRef;
public MethodReference Replicate_Authortative_MethodRef;
public MethodReference Reconcile_Client_MethodRef;
+ public MethodReference Reconcile_Client_Local_MethodRef;
public MethodReference Replicate_Replay_MethodRef;
public MethodReference Reconcile_Reader_MethodRef;
public MethodReference RegisterReplicateRpc_MethodRef;
@@ -44,7 +45,6 @@ internal class NetworkBehaviourHelper : CodegenBase
public MethodReference SendServerRpc_MethodRef;
public MethodReference SendObserversRpc_MethodRef;
public MethodReference SendTargetRpc_MethodRef;
- public MethodReference DirtySyncType_MethodRef;
public MethodReference RegisterServerRpc_MethodRef;
public MethodReference RegisterObserversRpc_MethodRef;
public MethodReference RegisterTargetRpc_MethodRef;
@@ -117,11 +117,11 @@ public override bool ImportReferences()
Reconcile_Server_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Client))
Reconcile_Client_MethodRef = base.ImportReference(mi);
+ else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Client_Local))
+ Reconcile_Client_Local_MethodRef = base.ImportReference(mi);
//Misc.
else if (mi.Name == nameof(NetworkBehaviour.OwnerMatches))
OwnerMatches_MethodRef = base.ImportReference(mi);
- else if (mi.Name == nameof(NetworkBehaviour.DirtySyncType))
- DirtySyncType_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.NetworkInitializeIfDisabled))
NetworkInitializeIfDisabled_MethodRef = base.ImportReference(mi);
//Prediction
@@ -178,7 +178,6 @@ internal MethodDefinition GetAwakeMethodDefinition(TypeDefinition typeDef)
return typeDef.GetMethod(AWAKE_METHOD_NAME);
}
-
///
/// Creates a replicate delegate.
///
@@ -205,8 +204,6 @@ internal void CreateReplicateDelegate(MethodDefinition originalMethodDef, Method
processor.InsertLast(insts);
}
-
-
///
/// Creates a RPC delegate for rpcType.
///
@@ -277,9 +274,7 @@ internal Instruction CreateLocalClientIsOwnerCheck(MethodDefinition methodDef, L
if (loggingType != LoggingType.Off)
{
string disableLoggingText = (notifyMessageCanBeDisabled) ? DISABLE_LOGGING_TEXT : string.Empty;
- string msg = (retIfOwner) ?
- $"Cannot complete action because you are the owner of this object. {disableLoggingText}." :
- $"Cannot complete action because you are not the owner of this object. {disableLoggingText}.";
+ string msg = (retIfOwner) ? $"Cannot complete action because you are the owner of this object. {disableLoggingText}." : $"Cannot complete action because you are not the owner of this object. {disableLoggingText}.";
instructions.AddRange(base.GetClass().LogMessage(methodDef, msg, loggingType));
}
@@ -387,8 +382,8 @@ internal void CreateIsClientCheck(MethodDefinition methodDef, LoggingType loggin
internal void CreateIsServerCheck(MethodDefinition methodDef, LoggingType loggingType, bool useStatic, bool insertFirst, bool checkIsNetworked)
{
/* This is placed after the if check.
- * Should the if check pass then code
- * jumps to this instruction. */
+ * Should the if check pass then code
+ * jumps to this instruction. */
ILProcessor processor = methodDef.Body.GetILProcessor();
Instruction endIf = processor.Create(OpCodes.Nop);
@@ -445,7 +440,6 @@ private List CreateIsNetworkedCheck(MethodDefinition methodDef, Ins
return insts;
}
-
///
/// Creates a return using the ReturnType for methodDef.
///
diff --git a/Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs
index 133b3a54..da7ff101 100644
--- a/Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs
+++ b/Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs
@@ -15,6 +15,7 @@
using MonoFN.Cecil.Rocks;
using System.Collections.Generic;
using System.Linq;
+using GameKit.Dependencies.Utilities.Types;
using UnityEngine;
using SR = System.Reflection;
@@ -23,7 +24,6 @@ namespace FishNet.CodeGenerating.Processing
internal class PredictionProcessor : CodegenBase
{
#region Types.
-
private class PredictionAttributedMethods
{
public MethodDefinition ReplicateMethod;
@@ -70,6 +70,10 @@ private class CreatedPredictionFields
///
public readonly FieldDefinition ReconcileData;
///
+ /// Reconcile data cached locally from the local client.
+ ///
+ public readonly FieldDefinition ReconcileDatasHistory;
+ ///
/// A buffer to read replicates into.
///
public readonly FieldDefinition ServerReplicateReaderBuffer;
@@ -78,8 +82,7 @@ private class CreatedPredictionFields
///
public readonly FieldDefinition LastReadReplicate;
- public CreatedPredictionFields(TypeReference replicateDataTypeRef, FieldDefinition replicateULDelegate, FieldDefinition reconcileULDelegate, FieldDefinition replicateDatasQueue, FieldDefinition replicateDatasHistory, FieldDefinition reconcileData,
- FieldDefinition serverReplicateReaderBuffer, FieldDefinition lastReadReplicate)
+ public CreatedPredictionFields(TypeReference replicateDataTypeRef, FieldDefinition replicateULDelegate, FieldDefinition reconcileULDelegate, FieldDefinition replicateDatasQueue, FieldDefinition replicateDatasHistory, FieldDefinition reconcileData, FieldDefinition reconcileDatasHistory, FieldDefinition serverReplicateReaderBuffer, FieldDefinition lastReadReplicate)
{
ReplicateDataTypeRef = replicateDataTypeRef;
ReplicateULDelegate = replicateULDelegate;
@@ -87,6 +90,7 @@ public CreatedPredictionFields(TypeReference replicateDataTypeRef, FieldDefiniti
ReplicateDatasQueue = replicateDatasQueue;
ReplicateDatasHistory = replicateDatasHistory;
ReconcileData = reconcileData;
+ ReconcileDatasHistory = reconcileDatasHistory;
ServerReplicateReaderBuffer = serverReplicateReaderBuffer;
LastReadReplicate = lastReadReplicate;
}
@@ -103,11 +107,9 @@ public PredictionReaders(MethodReference replicateReader, MethodReference reconc
ReconcileReader = reconcileReader;
}
}
-
#endregion
#region Public.
-
public string IReplicateData_FullName = typeof(IReplicateData).FullName;
public string IReconcileData_FullName = typeof(IReconcileData).FullName;
public TypeReference ReplicateULDelegate_TypeRef;
@@ -124,6 +126,7 @@ public PredictionReaders(MethodReference replicateReader, MethodReference reconc
public override bool ImportReferences()
{
System.Type locType;
+ SR.MethodInfo locMi;
base.ImportReference(typeof(BasicQueue<>));
ReplicateULDelegate_TypeRef = base.ImportReference(typeof(ReplicateUserLogicDelegate<>));
@@ -154,7 +157,6 @@ public override bool ImportReferences()
}
#region Setup and checks.
-
///
/// Gets number of predictions by checking for prediction attributes. This does not perform error checking.
///
@@ -301,7 +303,6 @@ bool AlreadyFound(MethodDefinition md)
else
return true;
}
-
#endregion
internal bool Process(TypeDefinition typeDef)
@@ -320,7 +321,7 @@ internal bool Process(TypeDefinition typeDef)
RpcProcessor rp = base.GetClass();
uint predictionRpcCount = GetPredictionCountInParents(typeDef) + rp.GetRpcCountInParents(typeDef);
-
+
//If replication methods found but this hierarchy already has max.
if (predictionRpcCount >= NetworkBehaviourHelper.MAX_RPC_ALLOWANCE)
{
@@ -374,7 +375,7 @@ internal bool Process(TypeDefinition typeDef)
MethodDefinition replicateULMd;
MethodDefinition reconcileULMd;
CreatePredictionMethods(typeDef, replicateMd, reconcileMd, predictionFields, predictionRpcCount, out predictionReaders, out replicateULMd, out reconcileULMd);
- InitializeCollections(typeDef, replicateMd, predictionFields);
+ InitializeCollections(typeDef, replicateMd, reconcileMd, predictionFields);
InitializeULDelegates(typeDef, predictionFields, replicateMd, reconcileMd, replicateULMd, reconcileULMd);
RegisterPredictionRpcs(typeDef, predictionRpcCount, predictionReaders);
@@ -478,29 +479,37 @@ void Register(MethodDefinition readerMd, bool replicate)
/// Initializes collection fields made during this process.
///
///
- private void InitializeCollections(TypeDefinition typeDef, MethodDefinition replicateMd, CreatedPredictionFields predictionFields)
+ private void InitializeCollections(TypeDefinition typeDef, MethodDefinition replicateMd, MethodDefinition reconcileMd, CreatedPredictionFields predictionFields)
{
GeneralHelper gh = base.GetClass();
TypeReference replicateDataTr = replicateMd.Parameters[0].ParameterType;
+ TypeReference reconcileDataTr = reconcileMd.Parameters[0].ParameterType;
MethodDefinition injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
ILProcessor processor = injectionMethodDef.Body.GetILProcessor();
- Generate(predictionFields.ReplicateDatasQueue, false);
- Generate(predictionFields.ReplicateDatasHistory, true);
+ Generate(predictionFields.ReplicateDatasQueue, gh.GetGenericBasicQueue(replicateDataTr), isRingBuffer: false);
+ Generate(predictionFields.ReplicateDatasHistory, gh.GetGenericRingBuffer(replicateDataTr), isRingBuffer: true);
+
+ GenericInstanceType localReconcileGit = gh.GetGenericType(typeof(LocalReconcile<>), reconcileDataTr);
+ GenericInstanceType reconcileRingBufferDataGit = gh.GetGenericRingBuffer(localReconcileGit);
+ Generate(predictionFields.ReconcileDatasHistory, gh.GetGenericRingBuffer(localReconcileGit), isRingBuffer: true);
- void Generate(FieldReference fr, bool isList)
+ void Generate(FieldReference fr, GenericInstanceType git, bool isRingBuffer)
{
- MethodDefinition ctorMd = base.GetClass().List_TypeRef.CachedResolve(base.Session).GetDefaultConstructor(base.Session);
- GenericInstanceType collectionGit;
- if (isList)
- gh.GetGenericList(replicateDataTr, out collectionGit);
+ MethodDefinition ctorMd;
+ if (isRingBuffer)
+ //ctorMd = base.GetClass().RingBuffer_TypeRef.CachedResolve(base.Session).GetDefaultConstructor(base.Session);
+ ctorMd = base.GetClass().RingBuffer_TypeRef.CachedResolve(base.Session).GetConstructor(base.Session, 1);
else
- gh.GetGenericBasicQueue(replicateDataTr, out collectionGit);
- MethodReference ctorMr = ctorMd.MakeHostInstanceGeneric(base.Session, collectionGit);
+ ctorMd = base.GetClass().List_TypeRef.CachedResolve(base.Session).GetDefaultConstructor(base.Session);
+
+ MethodReference ctorMr = ctorMd.MakeHostInstanceGeneric(base.Session, git);
List insts = new();
insts.Add(processor.Create(OpCodes.Ldarg_0));
+ if (isRingBuffer)
+ insts.Add(processor.Create(OpCodes.Ldc_I4, RingBuffer.DEFAULT_CAPACITY));
insts.Add(processor.Create(OpCodes.Newobj, ctorMr));
insts.Add(processor.Create(OpCodes.Stfld, fr));
@@ -557,33 +566,39 @@ private void CreateFields(TypeDefinition typeDef, MethodDefinition replicateMd,
GenericInstanceType replicateULDelegateGit;
GenericInstanceType reconcileULDelegateGit;
- GenericInstanceType lstDataGit;
- GenericInstanceType queueDataGit;
+
GetGenericULDelegate(replicateDataTr, typeof(ReplicateUserLogicDelegate<>), out replicateULDelegateGit);
GetGenericULDelegate(reconcileDataTr, typeof(ReconcileUserLogicDelegate<>), out reconcileULDelegateGit);
- gh.GetGenericList(replicateDataTr, out lstDataGit);
- gh.GetGenericBasicQueue(replicateDataTr, out queueDataGit);
+ GenericInstanceType replicateRingBufferDataGit = gh.GetGenericRingBuffer(replicateDataTr);
+ GenericInstanceType queueDataGit = gh.GetGenericBasicQueue(replicateDataTr);
+
+ //Make generic LocalReconcile.
+
+ //TypeReference localReconcileTr = base.ImportReference(typeof(LocalReconcile<>));
+ //GenericInstanceType reconcileRingBufferDataGit = gh.GetGenericRingBuffer(localReconcileTr);
+ GenericInstanceType localReconcileGit = gh.GetGenericType(typeof(LocalReconcile<>), reconcileDataTr);
+ GenericInstanceType reconcileRingBufferDataGit = gh.GetGenericRingBuffer(localReconcileGit);
- base.ImportReference(lstDataGit);
/* Data buffer. */
FieldDefinition replicateULDelegateFd = new($"_replicateULDelegate___{replicateMd.Name}", FieldAttributes.Private, replicateULDelegateGit);
FieldDefinition reconcileULDelegateFd = new($"_reconcileULDelegate___{reconcileMd.Name}", FieldAttributes.Private, reconcileULDelegateGit);
FieldDefinition replicatesQueueFd = new($"_replicatesQueue___{replicateMd.Name}", FieldAttributes.Private, queueDataGit);
- FieldDefinition replicatesListFd = new($"_replicatesHistory___{replicateMd.Name}", FieldAttributes.Private, lstDataGit);
+ FieldDefinition replicatesRingBufferFd = new($"_replicatesHistory___{replicateMd.Name}", FieldAttributes.Private, replicateRingBufferDataGit);
FieldDefinition reconcileDataFd = new($"_reconcileData___{replicateMd.Name}", FieldAttributes.Private, reconcileDataTr);
+ FieldDefinition reconcilesRingBufferFd = new($"_reconcilesHistory___{reconcileMd.Name}", FieldAttributes.Private, reconcileRingBufferDataGit);
FieldDefinition serverReplicatesReadBufferFd = new($"_serverReplicateReadBuffer___{replicateMd.Name}", FieldAttributes.Private, replicateDataArrTr);
FieldDefinition lastReadReplicateFd = new($"_lastReadReplicate___{replicateMd.Name}", FieldAttributes.Private, replicateDataTr);
typeDef.Fields.Add(replicateULDelegateFd);
typeDef.Fields.Add(reconcileULDelegateFd);
typeDef.Fields.Add(replicatesQueueFd);
- typeDef.Fields.Add(replicatesListFd);
+ typeDef.Fields.Add(replicatesRingBufferFd);
typeDef.Fields.Add(reconcileDataFd);
+ typeDef.Fields.Add(reconcilesRingBufferFd);
typeDef.Fields.Add(serverReplicatesReadBufferFd);
typeDef.Fields.Add(lastReadReplicateFd);
- predictionFields = new(replicateDataTr, replicateULDelegateFd, reconcileULDelegateFd, replicatesQueueFd, replicatesListFd, reconcileDataFd,
- serverReplicatesReadBufferFd, lastReadReplicateFd);
+ predictionFields = new(replicateDataTr, replicateULDelegateFd, reconcileULDelegateFd, replicatesQueueFd, replicatesRingBufferFd, reconcileDataFd, reconcilesRingBufferFd, serverReplicatesReadBufferFd, lastReadReplicateFd);
}
///
@@ -670,7 +685,10 @@ private bool CreatePredictionMethods(TypeDefinition typeDef, MethodDefinition re
if (!CreateReplicateReplayStart())
return false;
- CreateClearReplicateCacheMethod(typeDef, replicateMd.Parameters[0].ParameterType, predictionFields);
+ TypeReference replicateDataTr = replicateMd.Parameters[0].ParameterType;
+ TypeReference reconcileDataTr = reconcileMd.Parameters[0].ParameterType;
+
+ CreateClearReplicateCacheMethod(typeDef, replicateDataTr, reconcileDataTr, predictionFields);
CreateReplicateReader(typeDef, startingRpcCount, replicateMd, predictionFields, out replicateReader);
CreateReconcileReader(typeDef, reconcileMd, predictionFields, out reconcileReader);
predictionReaders = new(replicateReader, reconcileReader);
@@ -704,14 +722,12 @@ bool CreateReconcile()
bool CreateEmptyReplicatesQueueIntoHistoryStart()
{
- MethodDefinition newMethodDef = nbh.EmptyReplicatesQueueIntoHistory_Start_MethodRef.CachedResolve(base.Session).CreateCopy(
- base.Session, null, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES);
+ MethodDefinition newMethodDef = nbh.EmptyReplicatesQueueIntoHistory_Start_MethodRef.CachedResolve(base.Session).CreateCopy(base.Session, null, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES);
typeDef.Methods.Add(newMethodDef);
ILProcessor processor = newMethodDef.Body.GetILProcessor();
- MethodReference baseMethodGim = nbh.EmptyReplicatesQueueIntoHistory_MethodRef.GetMethodReference(
- base.Session, new TypeReference[] { predictionFields.ReplicateDataTypeRef });
+ MethodReference baseMethodGim = nbh.EmptyReplicatesQueueIntoHistory_MethodRef.GetMethodReference(base.Session, new TypeReference[] { predictionFields.ReplicateDataTypeRef });
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldarg_0);
@@ -727,39 +743,42 @@ bool CreateEmptyReplicatesQueueIntoHistoryStart()
//Overrides reconcile start to call reconcile_client_internal.
bool CreateReconcileStart()
{
- MethodDefinition newMethodDef = nbh.Reconcile_Client_Start_MethodRef.CachedResolve(base.Session).CreateCopy(
- base.Session, null, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES);
+ MethodDefinition newMethodDef = nbh.Reconcile_Client_Start_MethodRef.CachedResolve(base.Session).CreateCopy(base.Session, null, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES);
typeDef.Methods.Add(newMethodDef);
ILProcessor processor = newMethodDef.Body.GetILProcessor();
- MethodReference baseMethodGim = nbh.Reconcile_Client_MethodRef.GetMethodReference(
- base.Session, new TypeReference[] { predictionFields.ReconcileData.FieldType, predictionFields.ReplicateDataTypeRef });
+ Call_Reconcile_Client();
- processor.Emit(OpCodes.Ldarg_0);
- processor.Emit(OpCodes.Ldarg_0);
- processor.Emit(OpCodes.Ldfld, predictionFields.ReconcileULDelegate);
- processor.Emit(OpCodes.Ldarg_0);
- processor.Emit(OpCodes.Ldfld, predictionFields.ReplicateDatasHistory);
- processor.Emit(OpCodes.Ldarg_0);
- processor.Emit(OpCodes.Ldfld, predictionFields.ReconcileData);
- processor.Emit(OpCodes.Call, baseMethodGim);
- processor.Emit(OpCodes.Ret);
+ void Call_Reconcile_Client()
+ {
+ MethodReference baseMethodGim = nbh.Reconcile_Client_MethodRef.GetMethodReference(base.Session, new TypeReference[] { predictionFields.ReconcileData.FieldType, predictionFields.ReplicateDataTypeRef });
+
+ processor.Emit(OpCodes.Ldarg_0);
+ processor.Emit(OpCodes.Ldarg_0);
+ processor.Emit(OpCodes.Ldfld, predictionFields.ReconcileULDelegate);
+ processor.Emit(OpCodes.Ldarg_0);
+ processor.Emit(OpCodes.Ldfld, predictionFields.ReplicateDatasHistory);
+ processor.Emit(OpCodes.Ldarg_0);
+ processor.Emit(OpCodes.Ldfld, predictionFields.ReconcileDatasHistory);
+ processor.Emit(OpCodes.Ldarg_0);
+ processor.Emit(OpCodes.Ldfld, predictionFields.ReconcileData);
+ processor.Emit(OpCodes.Call, baseMethodGim);
+ processor.Emit(OpCodes.Ret);
+ }
return true;
}
bool CreateReplicateReplayStart()
{
- MethodDefinition newMethodDef = nbh.Replicate_Replay_Start_MethodRef.CachedResolve(base.Session).CreateCopy(
- base.Session, null, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES);
+ MethodDefinition newMethodDef = nbh.Replicate_Replay_Start_MethodRef.CachedResolve(base.Session).CreateCopy(base.Session, null, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES);
typeDef.Methods.Add(newMethodDef);
ParameterDefinition replayTickPd = newMethodDef.Parameters[0];
ILProcessor processor = newMethodDef.Body.GetILProcessor();
- MethodReference baseMethodGim = nbh.Replicate_Replay_MethodRef.GetMethodReference(
- base.Session, new TypeReference[] { predictionFields.ReplicateDataTypeRef });
+ MethodReference baseMethodGim = nbh.Replicate_Replay_MethodRef.GetMethodReference(base.Session, new TypeReference[] { predictionFields.ReplicateDataTypeRef });
//uint replicateTick, ReplicateUserLogicDelegate del, List replicates, Channel channel) where T : IReplicateData
processor.Emit(OpCodes.Ldarg_0);
@@ -779,13 +798,12 @@ bool CreateReplicateReplayStart()
}
#region Universal prediction.
-
///
/// Creates an override for the method responsible for resetting replicates.
///
///
///
- private void CreateClearReplicateCacheMethod(TypeDefinition typeDef, TypeReference dataTr, CreatedPredictionFields predictionFields)
+ private void CreateClearReplicateCacheMethod(TypeDefinition typeDef, TypeReference replicateDataTr, TypeReference reconcileDataTr, CreatedPredictionFields predictionFields)
{
GeneralHelper gh = base.GetClass();
NetworkBehaviourHelper nbh = base.GetClass();
@@ -810,7 +828,7 @@ private void CreateClearReplicateCacheMethod(TypeDefinition typeDef, TypeReferen
//Call the actual clear method.
TypeDefinition nbTypeDef = typeDef.GetTypeDefinitionInBase(base.Session, typeof(NetworkBehaviour).FullName, false);
MethodReference internalClearMr = base.Session.ImportReference(nbTypeDef.GetMethod(nameof(NetworkBehaviour.ClearReplicateCache_Internal)));
- GenericInstanceMethod internalClearGim = internalClearMr.MakeGenericMethod(new TypeReference[] { dataTr });
+ GenericInstanceMethod internalClearGim = internalClearMr.MakeGenericMethod(new TypeReference[] { replicateDataTr, reconcileDataTr });
processor.Emit(OpCodes.Ldarg_0); //Base.
processor.Emit(OpCodes.Ldarg_0);
@@ -818,6 +836,8 @@ private void CreateClearReplicateCacheMethod(TypeDefinition typeDef, TypeReferen
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldfld, predictionFields.ReplicateDatasHistory);
processor.Emit(OpCodes.Ldarg_0);
+ processor.Emit(OpCodes.Ldfld, predictionFields.ReconcileDatasHistory);
+ processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldflda, predictionFields.LastReadReplicate);
processor.Emit(OpCodes.Call, internalClearGim);
processor.Emit(OpCodes.Ret);
@@ -884,11 +904,9 @@ private List SubtractOneVariableFromAnother(MethodDefinition method
return insts;
}
-
#endregion
#region Server side.
-
///
/// Creates replicate code for client.
///
@@ -919,9 +937,7 @@ private void CallNonAuthoritativeReplicate(MethodDefinition replicateMd, Created
private bool CreateReplicateReader(TypeDefinition typeDef, uint hash, MethodDefinition replicateMd, CreatedPredictionFields predictionFields, out MethodDefinition result)
{
string methodName = $"{REPLICATE_READER_PREFIX}{replicateMd.Name}";
- MethodDefinition createdMd = new(methodName,
- MethodAttributes.Private,
- replicateMd.Module.TypeSystem.Void);
+ MethodDefinition createdMd = new(methodName, MethodAttributes.Private, replicateMd.Module.TypeSystem.Void);
typeDef.Methods.Add(createdMd);
createdMd.Body.InitLocals = true;
@@ -975,7 +991,20 @@ private void ServerCreateReconcile(MethodDefinition reconcileMd, CreatedPredicti
ParameterDefinition channelPd = reconcileMd.Parameters[1];
ILProcessor processor = reconcileMd.Body.GetILProcessor();
- GenericInstanceMethod methodGim = base.GetClass().Reconcile_Server_MethodRef.MakeGenericMethod(new TypeReference[] { reconcileDataPd.ParameterType });
+ NetworkBehaviourHelper nbh = base.GetClass();
+ GenericInstanceMethod methodGim;
+
+ /* Reconcile_Client_Local. */
+ methodGim = nbh.Reconcile_Client_Local_MethodRef.MakeGenericMethod(new TypeReference[] { reconcileDataPd.ParameterType });
+
+ processor.Emit(OpCodes.Ldarg_0);
+ processor.Emit(OpCodes.Ldarg_0);
+ processor.Emit(OpCodes.Ldfld, predictionFields.ReconcileDatasHistory);
+ processor.Emit(OpCodes.Ldarg, reconcileDataPd);
+ processor.Emit(OpCodes.Call, methodGim);
+
+ /* Reconcile_Server. */
+ methodGim = nbh.Reconcile_Server_MethodRef.MakeGenericMethod(new TypeReference[] { reconcileDataPd.ParameterType });
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldc_I4, (int)rpcCount);
@@ -987,11 +1016,9 @@ private void ServerCreateReconcile(MethodDefinition reconcileMd, CreatedPredicti
rpcCount++;
}
-
#endregion
#region Client side.
-
///
/// Creates replicate code for client.
///
@@ -1026,9 +1053,7 @@ private void CallAuthoritativeReplicate(MethodDefinition replicateMd, CreatedPre
private bool CreateReconcileReader(TypeDefinition typeDef, MethodDefinition reconcileMd, CreatedPredictionFields predictionFields, out MethodDefinition result)
{
string methodName = $"{RECONCILE_READER_PREFIX}{reconcileMd.Name}";
- MethodDefinition createdMd = new(methodName,
- MethodAttributes.Private,
- reconcileMd.Module.TypeSystem.Void);
+ MethodDefinition createdMd = new(methodName, MethodAttributes.Private, reconcileMd.Module.TypeSystem.Void);
typeDef.Methods.Add(createdMd);
createdMd.Body.InitLocals = true;
@@ -1059,7 +1084,6 @@ private bool CreateReconcileReader(TypeDefinition typeDef, MethodDefinition reco
result = createdMd;
return true;
}
-
#endregion
}
}
\ No newline at end of file
diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark.prefab b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark.prefab
index 27114730..887033bd 100644
--- a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark.prefab
+++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark.prefab
@@ -51,11 +51,9 @@ MonoBehaviour:
k__BackingField: 0
k__BackingField: {fileID: 0}
k__BackingField: {fileID: 0}
- _networkBehaviours:
- - {fileID: 0}
- - {fileID: -5271135124957689192}
- k__BackingField: {fileID: 0}
- k__BackingField: []
+ NetworkBehaviours: []
+ k__BackingField: {fileID: 0}
+ k__BackingField: []
_isNetworked: 1
_isSpawnable: 1
_isGlobal: 0
@@ -75,14 +73,14 @@ MonoBehaviour:
_spectatorInterpolation: 2
_enableTeleport: 0
_teleportThreshold: 1
- k__BackingField: 26
+ k__BackingField: 65535
k__BackingField: 0
k__BackingField: 14364260540862342890
k__BackingField: 0
SerializedTransformProperties:
Position: {x: 0, y: 0, z: 0}
- Rotation: {x: 0, y: 0, z: 0, w: 1}
- LocalScale: {x: 1, y: 1, z: 1}
+ Rotation: {x: 0, y: 0, z: 0, w: 0}
+ LocalScale: {x: 0, y: 0, z: 0}
--- !u!114 &6667641716399555817
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -116,9 +114,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: a2836e36774ca1c4bbbee976e17b649c, type: 3}
m_Name:
m_EditorClassIdentifier:
- _componentIndexCache: 1
+ _componentIndexCache: 255
_addedNetworkObject: {fileID: 4512293259955182956}
- _networkObjectCache: {fileID: 4512293259955182956}
+ _networkObjectCache: {fileID: 0}
_componentConfiguration: 0
_synchronizeParent: 0
_packing:
diff --git a/Assets/FishNet/Demos/ColliderRollback/Prefabs/Player.prefab b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Player.prefab
index 3c599368..38c9802f 100644
--- a/Assets/FishNet/Demos/ColliderRollback/Prefabs/Player.prefab
+++ b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Player.prefab
@@ -31,6 +31,7 @@ Transform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 2, z: -2}
m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
@@ -47,50 +48,40 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3}
m_Name:
m_EditorClassIdentifier:
- NetworkObserver: {fileID: 0}
- k__BackingField: 27
- k__BackingField: 0
- _scenePathHash: 0
- k__BackingField: 0
- k__BackingField: 7473726319608011331
- AdaptiveInterpolationValue: 4
k__BackingField: 0
+ k__BackingField: 0
+ k__BackingField: {fileID: 0}
+ k__BackingField: {fileID: 0}
+ NetworkBehaviours: []
+ k__BackingField: {fileID: 0}
+ k__BackingField: []
+ _isNetworked: 1
+ _isSpawnable: 1
+ _isGlobal: 0
+ _initializeOrder: 0
+ _defaultDespawnType: 0
+ NetworkObserver: {fileID: 0}
_enablePrediction: 0
_predictionType: 0
_graphicalObject: {fileID: 0}
+ _detachGraphicalObject: 0
_enableStateForwarding: 1
+ _networkTransform: {fileID: 0}
_ownerInterpolation: 1
+ _ownerSmoothedProperties: 255
+ _adaptiveInterpolation: 3
+ _spectatorSmoothedProperties: 255
+ _spectatorInterpolation: 2
_enableTeleport: 0
- _ownerTeleportThreshold: 1
- _spectatorAdaptiveInterpolation: 1
- _spectatorInterpolation: 1
- _adaptiveSmoothingType: 0
- _customSmoothingData:
- InterpolationPercent: 1
- CollisionInterpolationPercent: 0.1
- InterpolationDecreaseStep: 1
- InterpolationIncreaseStep: 3
- _preconfiguredSmoothingDataPreview:
- InterpolationPercent: 0.5
- CollisionInterpolationPercent: 0.05
- InterpolationDecreaseStep: 1
- InterpolationIncreaseStep: 2
- k__BackingField: 0
- k__BackingField: {fileID: 0}
- _networkBehaviours:
- - {fileID: 2759061792589502182}
- - {fileID: 1348621277}
- - {fileID: 1348621278}
- k__BackingField: {fileID: 0}
- k__BackingField: []
+ _teleportThreshold: 1
+ k__BackingField: 65535
+ k__BackingField: 0
+ k__BackingField: 7473726319608011331
+ k__BackingField: 0
SerializedTransformProperties:
Position: {x: 0, y: 0, z: 0}
Rotation: {x: 0, y: 0, z: 0, w: 0}
LocalScale: {x: 0, y: 0, z: 0}
- _isNetworked: 1
- _isGlobal: 0
- _initializeOrder: 0
- _defaultDespawnType: 0
--- !u!114 &2759061792589502182
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -103,15 +94,14 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 9d3558aad46c24549bea48d0e3938264, type: 3}
m_Name:
m_EditorClassIdentifier:
- _componentIndexCache: 0
+ _componentIndexCache: 255
_addedNetworkObject: {fileID: 8475222101369129519}
- _networkObjectCache: {fileID: 8475222101369129519}
+ _networkObjectCache: {fileID: 0}
_hitboxLayer:
serializedVersion: 2
- m_Bits: 32
- _audio: {fileID: 8300000, guid: 0330762d2b3c8d641bfe11ad89b7e196, type: 3}
- _muzzleFlashPrefab: {fileID: 39148481766341303, guid: 4385a793e032d634bb912f84a23d6db1,
- type: 3}
+ m_Bits: 0
+ _audio: {fileID: 0}
+ _muzzleFlashPrefab: {fileID: 0}
--- !u!114 &1348621277
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -124,9 +114,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: e6d656f377f37164d8d7431aa4e43cdb, type: 3}
m_Name:
m_EditorClassIdentifier:
- _componentIndexCache: 1
+ _componentIndexCache: 255
_addedNetworkObject: {fileID: 8475222101369129519}
- _networkObjectCache: {fileID: 8475222101369129519}
+ _networkObjectCache: {fileID: 0}
--- !u!114 &1348621278
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -139,9 +129,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: c718ab30626bbd648952910f74780a06, type: 3}
m_Name:
m_EditorClassIdentifier:
- _componentIndexCache: 2
+ _componentIndexCache: 255
_addedNetworkObject: {fileID: 8475222101369129519}
- _networkObjectCache: {fileID: 8475222101369129519}
+ _networkObjectCache: {fileID: 0}
_moveRate: 3
--- !u!143 &5081159371976248031
CharacterController:
diff --git a/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs
index bec21995..8e84c811 100644
--- a/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs
+++ b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs
@@ -8,8 +8,6 @@
namespace FishNet.Example.ComponentStateSync
{
-
-
///
/// It's very important to exclude this from codegen.
/// However, whichever value you are synchronizing must not be excluded. This is why the value is outside the StructySync class.
@@ -29,10 +27,12 @@ public bool Enabled
/// Component to state sync.
///
public T Component { get; private set; }
+
///
/// Delegate signature for when the component changes.
///
public delegate void StateChanged(T component, bool prevState, bool nextState, bool asServer);
+
///
/// Called when the component state changes.
///
@@ -99,6 +99,7 @@ private void AddOperation(T component, bool prev, bool next)
bool asServer = true;
OnChange?.Invoke(component, prev, next, asServer);
}
+
///
/// Writes all changed values.
///
@@ -128,21 +129,20 @@ internal protected override void WriteFull(PooledWriter writer)
[APIExclude]
internal protected override void Read(PooledReader reader, bool asServer)
{
- bool nextValue = reader.ReadBoolean();
- if (base.NetworkManager == null)
- return;
-
+ base.SetReadArguments(reader, asServer, out bool newChangeId, out bool _, out bool canModifyValues);
+
bool prevValue = GetState();
+ bool nextValue = reader.ReadBoolean();
/* When !asServer don't make changes if server is running.
- * This is because changes would have already been made on
- * the server side and doing so again would result in duplicates
- * and potentially overwrite data not yet sent. */
- bool asClientAndHost = (!asServer && base.NetworkManager.IsServerStarted);
- if (!asClientAndHost)
+ * This is because changes would have already been made on
+ * the server side and doing so again would result in duplicates
+ * and potentially overwrite data not yet sent. */
+ if (canModifyValues)
Component.enabled = nextValue;
- OnChange?.Invoke(Component, prevValue, nextValue, asServer);
+ if (newChangeId)
+ OnChange?.Invoke(Component, prevValue, nextValue, asServer);
}
///
@@ -151,4 +151,4 @@ internal protected override void Read(PooledReader reader, bool asServer)
///
public object GetSerializedType() => null;
}
-}
+}
\ No newline at end of file
diff --git a/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs b/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs
index 85abe1f0..4e5afa2c 100644
--- a/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs
+++ b/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs
@@ -47,6 +47,7 @@ public ChangeData(CustomOperation operation, Structy data)
Data = data;
}
}
+
///
/// Types of changes. This is related to ChangedData
/// where you can specify what has changed.
@@ -67,6 +68,7 @@ public enum CustomOperation : byte
///
///
public delegate void CustomChanged(CustomOperation op, Structy oldItem, Structy newItem, bool asServer);
+
///
/// Called when the Structy changes.
///
@@ -121,10 +123,10 @@ private void AddOperation(CustomOperation operation, Structy prev, Structy next)
}
/* Set as changed even if cannot dirty.
- * Dirty is only set when there are observers,
- * but even if there are not observers
- * values must be marked as changed so when
- * there are observers, new values are sent. */
+ * Dirty is only set when there are observers,
+ * but even if there are not observers
+ * values must be marked as changed so when
+ * there are observers, new values are sent. */
_valuesChanged = true;
base.Dirty();
@@ -190,11 +192,7 @@ internal protected override void WriteFull(PooledWriter writer)
[APIExclude]
internal protected override void Read(PooledReader reader, bool asServer)
{
- /* When !asServer don't make changes if server is running.
- * This is because changes would have already been made on
- * the server side and doing so again would result in duplicates
- * and potentially overwrite data not yet sent. */
- bool asClientAndHost = (!asServer && base.NetworkManager.IsServerStarted);
+ base.SetReadArguments(reader, asServer, out bool newChangeId, out bool _, out bool canModifyValues);
int changes = reader.ReadInt32();
for (int i = 0; i < changes; i++)
@@ -213,12 +211,12 @@ internal protected override void Read(PooledReader reader, bool asServer)
else if (operation == CustomOperation.Age)
next.Age = reader.ReadUInt16();
- OnChange?.Invoke(operation, prev, next, asServer);
-
- if (!asClientAndHost)
+ if (canModifyValues)
Value = next;
+
+ if (newChangeId)
+ OnChange?.Invoke(operation, prev, next, asServer);
}
-
}
///
@@ -254,4 +252,4 @@ internal protected override void ResetState(bool asServer)
///
public object GetSerializedType() => typeof(Structy);
}
-}
+}
\ No newline at end of file
diff --git a/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab b/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab
index 78b57b77..35d6222b 100644
--- a/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab
+++ b/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab
@@ -134,26 +134,37 @@ MonoBehaviour:
k__BackingField: 0
k__BackingField: 0
k__BackingField: {fileID: 0}
- _networkBehaviours:
- - {fileID: 4670340455971777434}
- - {fileID: 3019520109258855553}
- k__BackingField: {fileID: 0}
- k__BackingField: []
- SerializedTransformProperties:
- Position: {x: 0, y: 0, z: 0}
- Rotation: {x: 0, y: 0, z: 0, w: 0}
- LocalScale: {x: 0, y: 0, z: 0}
+ k__BackingField: {fileID: 0}
+ NetworkBehaviours: []
+ k__BackingField: {fileID: 0}
+ k__BackingField: []
_isNetworked: 1
_isSpawnable: 1
_isGlobal: 0
_initializeOrder: 0
_defaultDespawnType: 0
NetworkObserver: {fileID: 0}
- k__BackingField: 0
+ _enablePrediction: 0
+ _predictionType: 0
+ _graphicalObject: {fileID: 0}
+ _detachGraphicalObject: 0
+ _enableStateForwarding: 1
+ _networkTransform: {fileID: 0}
+ _ownerInterpolation: 1
+ _ownerSmoothedProperties: 255
+ _adaptiveInterpolation: 3
+ _spectatorSmoothedProperties: 255
+ _spectatorInterpolation: 2
+ _enableTeleport: 0
+ _teleportThreshold: 1
+ k__BackingField: 65535
k__BackingField: 0
- _scenePathHash: 0
- k__BackingField: 0
k__BackingField: 11406911356865610645
+ k__BackingField: 0
+ SerializedTransformProperties:
+ Position: {x: 0, y: 0, z: 0}
+ Rotation: {x: 0, y: 0, z: 0, w: 0}
+ LocalScale: {x: 0, y: 0, z: 0}
--- !u!114 &4670340455971777434
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -166,10 +177,10 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 10f399a5388d3b3459b7a8476ae13e6a, type: 3}
m_Name:
m_EditorClassIdentifier:
- _componentIndexCache: 0
+ _componentIndexCache: 255
_addedNetworkObject: {fileID: 4512293259955182956}
- _networkObjectCache: {fileID: 4512293259955182956}
- _renderer: {fileID: 2529588038898116402}
+ _networkObjectCache: {fileID: 0}
+ _renderer: {fileID: 0}
--- !u!114 &3019520109258855553
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -182,9 +193,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: a2836e36774ca1c4bbbee976e17b649c, type: 3}
m_Name:
m_EditorClassIdentifier:
- _componentIndexCache: 1
+ _componentIndexCache: 255
_addedNetworkObject: {fileID: 4512293259955182956}
- _networkObjectCache: {fileID: 4512293259955182956}
+ _networkObjectCache: {fileID: 0}
_componentConfiguration: 0
_synchronizeParent: 0
_packing:
@@ -193,12 +204,10 @@ MonoBehaviour:
Scale: 0
_interpolation: 2
_extrapolation: 2
- _enableTeleport: 1
- _teleportThreshold: 0.5
- _scaleThreshold: 1
+ _enableTeleport: 0
+ _teleportThreshold: 1
_clientAuthoritative: 1
_sendToOwner: 1
- _enableNetworkLod: 1
_interval: 1
_synchronizePosition: 1
_positionSnapping:
diff --git a/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab b/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab
index 58a18627..f828ee1b 100644
--- a/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab
+++ b/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab
@@ -153,17 +153,17 @@ MonoBehaviour:
_ownerSmoothedProperties: 255
_adaptiveInterpolation: 3
_spectatorSmoothedProperties: 255
- _spectatorInterpolation: 1
+ _spectatorInterpolation: 2
_enableTeleport: 0
_teleportThreshold: 1
- k__BackingField: 22
+ k__BackingField: 65535
k__BackingField: 0
k__BackingField: 17472515426990886281
k__BackingField: 0
SerializedTransformProperties:
Position: {x: 0, y: 0, z: 0}
- Rotation: {x: 0, y: 0, z: 0, w: 1}
- LocalScale: {x: 1, y: 1, z: 1}
+ Rotation: {x: 0, y: 0, z: 0, w: 0}
+ LocalScale: {x: 0, y: 0, z: 0}
--- !u!114 &437322326027960749
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -176,6 +176,6 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 8a6a39c46bf52104ba8efe3100bce3f7, type: 3}
m_Name:
m_EditorClassIdentifier:
- _componentIndexCache: 0
+ _componentIndexCache: 255
_addedNetworkObject: {fileID: 4512293259955182956}
- _networkObjectCache: {fileID: 4512293259955182956}
+ _networkObjectCache: {fileID: 0}
diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab b/Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab
index 10dda46a..9e014cc5 100644
--- a/Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab
+++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab
@@ -218,10 +218,10 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 26e4f626a9ca9704f9befe7673a8dd15, type: 3}
m_Name:
m_EditorClassIdentifier:
- _componentIndexCache: 0
+ _componentIndexCache: 255
_addedNetworkObject: {fileID: 611616139817875448}
- _networkObjectCache: {fileID: 611616139817875448}
- _camera: {fileID: 5090726669533971462}
+ _networkObjectCache: {fileID: 0}
+ _camera: {fileID: 0}
_moveRate: 4
_clientAuth: 1
--- !u!54 &3514369712614123748
@@ -269,15 +269,10 @@ MonoBehaviour:
k__BackingField: 0
k__BackingField: 0
k__BackingField: {fileID: 0}
- _networkBehaviours:
- - {fileID: 5090726670223187108}
- - {fileID: 6654616088585099699}
- k__BackingField: {fileID: 0}
- k__BackingField: []
- SerializedTransformProperties:
- Position: {x: 0, y: 0, z: 0}
- Rotation: {x: 0, y: 0, z: 0, w: 0}
- LocalScale: {x: 0, y: 0, z: 0}
+ k__BackingField: {fileID: 0}
+ NetworkBehaviours: []
+ k__BackingField: {fileID: 0}
+ k__BackingField: []
_isNetworked: 1
_isSpawnable: 1
_isGlobal: 0
@@ -287,15 +282,24 @@ MonoBehaviour:
_enablePrediction: 0
_predictionType: 0
_graphicalObject: {fileID: 0}
+ _detachGraphicalObject: 0
_enableStateForwarding: 1
+ _networkTransform: {fileID: 0}
_ownerInterpolation: 1
+ _ownerSmoothedProperties: 255
+ _adaptiveInterpolation: 3
+ _spectatorSmoothedProperties: 255
+ _spectatorInterpolation: 2
_enableTeleport: 0
_teleportThreshold: 1
- k__BackingField: 12
+ k__BackingField: 65535
k__BackingField: 0
- _scenePathHash: 0
- k__BackingField: 0
k__BackingField: 12334122808499987737
+ k__BackingField: 0
+ SerializedTransformProperties:
+ Position: {x: 0, y: 0, z: 0}
+ Rotation: {x: 0, y: 0, z: 0, w: 0}
+ LocalScale: {x: 0, y: 0, z: 0}
--- !u!114 &6420552185407096997
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -323,9 +327,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: a2836e36774ca1c4bbbee976e17b649c, type: 3}
m_Name:
m_EditorClassIdentifier:
- _componentIndexCache: 1
+ _componentIndexCache: 255
_addedNetworkObject: {fileID: 611616139817875448}
- _networkObjectCache: {fileID: 611616139817875448}
+ _networkObjectCache: {fileID: 0}
_componentConfiguration: 0
_synchronizeParent: 0
_packing:
@@ -336,10 +340,8 @@ MonoBehaviour:
_extrapolation: 2
_enableTeleport: 0
_teleportThreshold: 1
- _scaleThreshold: 1
_clientAuthoritative: 1
_sendToOwner: 1
- _enableNetworkLod: 1
_interval: 1
_synchronizePosition: 1
_positionSnapping:
diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab
index 56293544..90552d40 100644
--- a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab
+++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab
@@ -328,15 +328,10 @@ MonoBehaviour:
k__BackingField: 0
k__BackingField: 0
k__BackingField: {fileID: 0}
- _networkBehaviours:
- - {fileID: 6767263130208162619}
- - {fileID: -7170926880071132319}
- k__BackingField: {fileID: 0}
- k__BackingField: []
- SerializedTransformProperties:
- Position: {x: 0, y: 20, z: 0}
- Rotation: {x: 0, y: 0, z: 0, w: 1}
- LocalScale: {x: 1, y: 1, z: 1}
+ k__BackingField: {fileID: 0}
+ NetworkBehaviours: []
+ k__BackingField: {fileID: 0}
+ k__BackingField: []
_isNetworked: 1
_isSpawnable: 1
_isGlobal: 0
@@ -346,15 +341,24 @@ MonoBehaviour:
_enablePrediction: 0
_predictionType: 0
_graphicalObject: {fileID: 0}
+ _detachGraphicalObject: 0
_enableStateForwarding: 1
+ _networkTransform: {fileID: 0}
_ownerInterpolation: 1
+ _ownerSmoothedProperties: 255
+ _adaptiveInterpolation: 3
+ _spectatorSmoothedProperties: 255
+ _spectatorInterpolation: 2
_enableTeleport: 0
_teleportThreshold: 1
- k__BackingField: 20
+ k__BackingField: 65535
k__BackingField: 0
- _scenePathHash: 0
- k__BackingField: 0
k__BackingField: 5491988111533709812
+ k__BackingField: 0
+ SerializedTransformProperties:
+ Position: {x: 0, y: 0, z: 0}
+ Rotation: {x: 0, y: 0, z: 0, w: 0}
+ LocalScale: {x: 0, y: 0, z: 0}
--- !u!114 &6767263130208162619
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -367,11 +371,11 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: c8541d1cca4da7043b84a0d563939dea, type: 3}
m_Name:
m_EditorClassIdentifier:
- _componentIndexCache: 0
+ _componentIndexCache: 255
_addedNetworkObject: {fileID: 8192566354860284824}
- _networkObjectCache: {fileID: 8192566354860284824}
- _ownerObjects: {fileID: 6508211851680439895}
- _moveRate: 3
+ _networkObjectCache: {fileID: 0}
+ _ownerObjects: {fileID: 0}
+ _moveRate: 2
--- !u!54 &-1062563901163859699
Rigidbody:
m_ObjectHideFlags: 0
@@ -414,9 +418,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: a2836e36774ca1c4bbbee976e17b649c, type: 3}
m_Name:
m_EditorClassIdentifier:
- _componentIndexCache: 1
+ _componentIndexCache: 255
_addedNetworkObject: {fileID: 8192566354860284824}
- _networkObjectCache: {fileID: 8192566354860284824}
+ _networkObjectCache: {fileID: 0}
_componentConfiguration: 0
_synchronizeParent: 0
_packing:
@@ -426,11 +430,9 @@ MonoBehaviour:
_interpolation: 2
_extrapolation: 2
_enableTeleport: 0
- _teleportThreshold: 0.5
- _scaleThreshold: 1
- _clientAuthoritative: 0
+ _teleportThreshold: 1
+ _clientAuthoritative: 1
_sendToOwner: 1
- _enableNetworkLod: 1
_interval: 1
_synchronizePosition: 1
_positionSnapping:
diff --git a/Assets/FishNet/Plugins.meta b/Assets/FishNet/Plugins.meta
index a2186a1f..ab88963a 100644
--- a/Assets/FishNet/Plugins.meta
+++ b/Assets/FishNet/Plugins.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: f8b8569be5872c245a621e319ceb25dd
+guid: 1efd6a2198a30b44c98e7a6e48bc92b6
folderAsset: yes
DefaultImporter:
externalObjects: {}
diff --git a/Assets/FishNet/Plugins/Edgegap.meta b/Assets/FishNet/Plugins/Edgegap.meta
index 58e3cc0a..abd44447 100644
--- a/Assets/FishNet/Plugins/Edgegap.meta
+++ b/Assets/FishNet/Plugins/Edgegap.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: eb02bdac67616b547818f5533ceaa410
+guid: 070891960b4815b44aa984d9791febd6
folderAsset: yes
DefaultImporter:
externalObjects: {}
diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes.meta b/Assets/FishNet/Runtime/Generated/SyncTypes.meta
deleted file mode 100644
index 450b96ce..00000000
--- a/Assets/FishNet/Runtime/Generated/SyncTypes.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: d2bcb6aac372c664091df2e19bc3e85a
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.cs b/Assets/FishNet/Runtime/Managing/NetworkManager.cs
index 706658d6..a99513a7 100644
--- a/Assets/FishNet/Runtime/Managing/NetworkManager.cs
+++ b/Assets/FishNet/Runtime/Managing/NetworkManager.cs
@@ -212,7 +212,7 @@ public static IReadOnlyList Instances
///
/// Version of this release.
///
- public const string FISHNET_VERSION = "4.4.5";
+ public const string FISHNET_VERSION = "4.4.6";
///
/// Maximum framerate allowed.
///
diff --git a/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs b/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs
index 201d33f6..2f40df5c 100644
--- a/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs
+++ b/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs
@@ -13,7 +13,6 @@
namespace FishNet.Managing.Predicting
{
-
///
/// Additional options for managing the observer system.
///
@@ -21,6 +20,35 @@ namespace FishNet.Managing.Predicting
[AddComponentMenu("FishNet/Manager/PredictionManager")]
public sealed class PredictionManager : MonoBehaviour
{
+ #region Types.
+ internal class StatePacketTick
+ {
+ public uint Client = TimeManager.UNSET_TICK;
+ public uint Server = TimeManager.UNSET_TICK;
+
+ ///
+ /// Returns if ticks are unset.
+ /// Only client needs to be checked, as they both are set with non default at the same time.
+ ///
+ public bool IsUnset => Client == TimeManager.UNSET_TICK;
+
+ public void Update(uint client, uint server)
+ {
+ Client = client;
+ Server = server;
+ }
+
+ ///
+ /// Adds ticks onto each field.
+ ///
+ public void AddTick(uint quantity)
+ {
+ Client += quantity;
+ Server += quantity;
+ }
+ }
+ #endregion
+
#region Public.
///
/// Called before performing a reconcile. Contains the client and server tick the reconcile is for.
@@ -28,47 +56,61 @@ public sealed class PredictionManager : MonoBehaviour
public event PreReconcileDel OnPreReconcile;
public delegate void PreReconcileDel(uint clientTick, uint serverTick);
+
///
/// Called when performing a reconcile.
/// This is used internally to reconcile objects and does not gaurantee your subscriptions to this event will process before or after internal components.
///
public event ReconcileDel OnReconcile;
+
public delegate void ReconcileDel(uint clientTick, uint serverTick);
+
///
/// Called after performing a reconcile. Contains the client and server tick the reconcile is for.
///
public event PostReconcileDel OnPostReconcile;
+
public delegate void PostReconcileDel(uint clientTick, uint serverTick);
+
///
/// Called before Physics SyncTransforms are run after a reconcile.
/// This will only invoke if physics are set to TimeManager, within the TimeManager inspector.
///
public event PrePhysicsSyncTransformDel OnPrePhysicsTransformSync;
+
public delegate void PrePhysicsSyncTransformDel(uint clientTick, uint serverTick);
+
///
/// Called after Physics SyncTransforms are run after a reconcile.
/// This will only invoke if physics are set to TimeManager, within the TimeManager inspector.
///
public event PostPhysicsSyncTransformDel OnPostPhysicsTransformSync;
+
public delegate void PostPhysicsSyncTransformDel(uint clientTick, uint serverTick);
///
/// Called before physics is simulated when replaying a replicate method.
///
public event PreReplicateReplayDel OnPreReplicateReplay;
+
public delegate void PreReplicateReplayDel(uint clientTick, uint serverTick);
+
///
/// Called when replaying a replication.
/// This is called before physics are simulated.
/// This is used internally to replay objects and does not gaurantee your subscriptions to this event will process before or after internal components.
///
internal event ReplicateReplayDel OnReplicateReplay;
+
public delegate void ReplicateReplayDel(uint clientTick, uint serverTick);
+
///
/// Called after physics is simulated when replaying a replicate method.
///
public event PostReplicateReplayDel OnPostReplicateReplay;
+
public delegate void PostReplicateReplayDel(uint clientTick, uint serverTick);
+
///
/// True if client timing needs to be reduced. This is fine-tuning of the prediction system.
///
@@ -113,10 +155,12 @@ public sealed class PredictionManager : MonoBehaviour
[Tooltip("Maximum number of replicates a server can queue per object. Higher values will put more load on the server and add replicate latency for the client.")]
[SerializeField]
private byte _maximumServerReplicates = 15;
+
///
/// Maximum number of replicates a server can queue per object. Higher values will put more load on the server and add replicate latency for the client.
///
public byte GetMaximumServerReplicates() => _maximumServerReplicates;
+
///
/// Sets the maximum number of replicates a server can queue per object.
///
@@ -125,6 +169,7 @@ public void SetMaximumServerReplicates(byte value)
{
_maximumServerReplicates = (byte)Mathf.Clamp(value, MINIMUM_REPLICATE_QUEUE_SIZE, MAXIMUM_REPLICATE_QUEUE_SIZE);
}
+
///
/// No more than this value of replicates should be stored as a buffer.
///
@@ -156,6 +201,7 @@ public void SetMaximumServerReplicates(byte value)
/// True if StateOrder is set to future.
///
internal bool IsAppendedStateOrder => (_stateOrder == ReplicateStateOrder.Appended);
+
///
/// Sets the current ReplicateStateOrder. This may be changed at runtime.
/// Changing this value only affects the client which it is changed on.
@@ -180,6 +226,7 @@ public void SetStateOrder(ReplicateStateOrder stateOrder)
item.EmptyReplicatesQueueIntoHistory();
}
}
+
///
/// Number of past inputs to send, which is also the number of times to resend final datas.
///
@@ -204,6 +251,10 @@ public void SetStateOrder(ReplicateStateOrder stateOrder)
///
private byte _droppedReconcilesCount;
///
+ /// Ticks for the last state packet to run.
+ ///
+ private StatePacketTick _lastStatePacketTick = new();
+ ///
/// Current reconcile state to use.
///
//private StatePacket _reconcileState;
@@ -292,6 +343,7 @@ public IncomingData(ArraySegment data, Channel channel)
Channel = channel;
}
}
+
public List Datas;
public uint ClientTick;
public uint ServerTick;
@@ -305,7 +357,8 @@ public void Update(ArraySegment data, uint clientTick, uint serverTick, Ch
public void AddData(ArraySegment data, Channel channel)
{
- Datas.Add(new(data, channel));
+ if (data.Array != null)
+ Datas.Add(new(data, channel));
}
public void ResetState()
@@ -322,14 +375,25 @@ public void InitializeState()
}
}
+ ///
+ /// Returns client or server state tick for the current reconcile.
+ ///
+ /// True to return client state tick, false for servers.
+ ///
+ public uint GetReconcileStateTick(bool clientTick) => (clientTick) ? ClientStateTick : ServerStateTick;
+
///
/// Reconciles to received states.
///
internal void ReconcileToStates()
- {
+ {
if (!_networkManager.IsClientStarted)
return;
- //No states.
+
+ //Creates a local state update if one is not available in reconcile states.
+ // CreateLocalStateUpdate();
+
+ //If there are no states then guestimate the next state.
if (_reconcileStates.Count == 0)
return;
@@ -339,7 +403,7 @@ internal void ReconcileToStates()
/* When there is an excessive amount of states try to consume
* some.This only happens when the client gets really far behind
- * and has to catch up, such as a latency increase then drop.
+ * and has to catch up, such as a latency increase then drop.
* Limit the number of states consumed per tick so the clients
* computer doesn't catch fire. */
int iterations = 0;
@@ -375,7 +439,7 @@ bool ConditionsMet(StatePacket spChecked)
* packets to arrive. This adds on varianceAllowance to replays but greatly
* increases the chances of the state being received before skipping past it
* in a replay.
- *
+ *
* When using Inserted (not AppendedStateOrder) there does not need to be any
* additional allowance since there is no extra queue like appended, they rather just
* go right into the past. */
@@ -384,6 +448,7 @@ bool ConditionsMet(StatePacket spChecked)
bool serverPass = (spChecked.ServerTick < (estimatedLastRemoteTick - serverTickDifferenceRequirement));
bool clientPass = spChecked.ClientTick < (localTick - stateInterpolation);
+
return (serverPass && clientPass);
}
@@ -421,6 +486,8 @@ bool ConditionsMet(StatePacket spChecked)
if (!dropReconcile)
{
IsReconciling = true;
+ _lastStatePacketTick.Update(clientTick, serverTick);
+
ClientStateTick = clientTick;
/* This is the tick which the reconcile is for.
* Since reconciles are performed after replicate, if
@@ -431,6 +498,10 @@ bool ConditionsMet(StatePacket spChecked)
//Have the reader get processed.
foreach (StatePacket.IncomingData item in sp.Datas)
{
+ // //If data isn't set skip it. This can be true if a locally generated state packet.
+ // if (item.Data.Array == null)
+ // continue;
+
PooledReader reader = ReaderPool.Retrieve(item.Data, _networkManager, Reader.DataSource.Server);
_networkManager.ClientManager.ParseReader(reader, item.Channel);
ReaderPool.Store(reader);
@@ -451,9 +522,9 @@ bool ConditionsMet(StatePacket spChecked)
}
/* Set first replicate to be the 1 tick
* after reconcile. This is because reconcile calcs
- * should be performed after replicate has run.
+ * should be performed after replicate has run.
* In result object will reconcile to data AFTER
- * the replicate tick, and then run remaining replicates as replay.
+ * the replicate tick, and then run remaining replicates as replay.
*
* Replay up to localtick, excluding localtick. There will
* be no input for localtick since reconcile runs before
@@ -463,7 +534,7 @@ bool ConditionsMet(StatePacket spChecked)
/* Only replay up to but excluding local tick.
* This prevents client from running 1 local tick into the future
- * since the OnTick has not run yet.
+ * since the OnTick has not run yet.
*
* EG: if localTick is 100 replay will run up to 99, then OnTick
* will fire for 100. */
@@ -483,8 +554,8 @@ bool ConditionsMet(StatePacket spChecked)
OnPostReconcile?.Invoke(ClientStateTick, ServerStateTick);
- ClientStateTick = TimeManager.UNSET_TICK;
- ServerStateTick = TimeManager.UNSET_TICK;
+ // ClientStateTick = TimeManager.UNSET_TICK;
+ // ServerStateTick = TimeManager.UNSET_TICK;
ClientReplayTick = TimeManager.UNSET_TICK;
ServerReplayTick = TimeManager.UNSET_TICK;
IsReconciling = false;
@@ -493,7 +564,32 @@ bool ConditionsMet(StatePacket spChecked)
DisposeOfStatePacket(sp);
}
}
-
+
+ ///
+ /// Gets the reconcile tick to use when generating a local reconcile.
+ ///
+ ///
+ internal uint GetCreateReconcileTick(bool isOwner)
+ {
+ uint localTick = _networkManager.TimeManager.LocalTick;
+
+ //Client uses current localTick if owner.
+ if (isOwner)
+ return localTick;
+
+ //ClientStateTick has never been set, might happen when just connecting. Cannot get tick.
+ if (ClientStateTick == TimeManager.UNSET_TICK)
+ return TimeManager.UNSET_TICK;
+
+ long tickDifference = (long)(localTick - ClientStateTick);
+
+ //Should not be possible given state tick is always behind.
+ if (tickDifference < 0)
+ tickDifference = 0;
+
+ return (ServerStateTick + (uint)tickDifference);
+ }
+
///
/// Sends written states for clients.
///
@@ -555,7 +651,6 @@ internal void SendStateUpdate()
}
}
-
///
/// Parses a received state update.
///
@@ -577,23 +672,7 @@ internal void ParseStateUpdate(PooledReader reader, Channel channel)
{
_lastOrderedReadReconcileTick = lastRemoteTick;
- /* There should never really be more than queuedInputs so set
- * a limit a little beyond to prevent reconciles from building up.
- * This is more of a last result if something went terribly
- * wrong with the network. */
- int adjustedStateInterpolation = (StateInterpolation * 4) + 2;
- /* If appending allow an additional of stateInterpolation since
- * entries arent added into the past until they are run on the appended
- * queue for each networkObject. */
- if (IsAppendedStateOrder)
- adjustedStateInterpolation += StateInterpolation;
- int maxAllowedStates = Mathf.Max(adjustedStateInterpolation, 4);
-
- while (_reconcileStates.Count > maxAllowedStates)
- {
- StatePacket oldSp = _reconcileStates.Dequeue();
- DisposeOfStatePacket(oldSp);
- }
+ RemoveExcessiveStates();
//LocalTick of this client the state is for.
uint clientTick = reader.ReadTickUnpacked();
@@ -620,6 +699,56 @@ internal void ParseStateUpdate(PooledReader reader, Channel channel)
}
}
}
+ //
+ // ///
+ // /// Creates a local statePacket with no data other than ticks.
+ // ///
+ // internal void CreateLocalStateUpdate()
+ // {
+ // //Only to be called when there are no reconcile states available.
+ // if (_reconcileStates.Count > 0)
+ // return;
+ // if (_networkManager.IsServerStarted)
+ // return;
+ // //Not yet received first state, cannot apply tick.
+ // if (_lastStatePacketTick.IsUnset)
+ // return;
+ //
+ // _lastStatePacketTick.AddTick(1);
+ //
+ // /* Update last read as well. If we've made it this far we won't be caring about states before this
+ // * even if they come in late. */
+ // _lastOrderedReadReconcileTick = _lastStatePacketTick.Server;
+ //
+ // StatePacket sp = ResettableObjectCaches.Retrieve();
+ // //Channel does not matter; it's only used to determine how data is parsed, data we don't have.
+ // sp.Update(default, _lastStatePacketTick.Client, _lastStatePacketTick.Server, Channel.Unreliable);
+ // _reconcileStates.Enqueue(sp);
+ // }
+
+ ///
+ /// Removes excessively stored state packets.
+ ///
+ private void RemoveExcessiveStates()
+ {
+ /* There should never really be more than queuedInputs so set
+ * a limit a little beyond to prevent reconciles from building up.
+ * This is more of a last result if something went terribly
+ * wrong with the network. */
+ int adjustedStateInterpolation = (StateInterpolation * 4) + 2;
+ /* If appending allow an additional of stateInterpolation since
+ * entries arent added into the past until they are run on the appended
+ * queue for each networkObject. */
+ if (IsAppendedStateOrder)
+ adjustedStateInterpolation += StateInterpolation;
+ int maxAllowedStates = Mathf.Max(adjustedStateInterpolation, 4);
+
+ while (_reconcileStates.Count > maxAllowedStates)
+ {
+ StatePacket oldSp = _reconcileStates.Dequeue();
+ DisposeOfStatePacket(oldSp);
+ }
+ }
///
/// Disposes of and cleans up everything related to a StatePacket.
@@ -639,5 +768,4 @@ private void OnValidate()
#endif
}
-
-}
+}
\ No newline at end of file
diff --git a/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs b/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs
index 288b66b8..a96dd2ae 100644
--- a/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs
+++ b/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs
@@ -89,16 +89,19 @@ internal enum LightProbeUpdateType
[Tooltip("Script to handle addressables loading and unloading. This field may be blank if addressables are not being used.")]
[SerializeField]
private SceneProcessorBase _sceneProcessor;
+
///
/// Script to handle addressables loading and unloading. This field may be blank if addressables are not being used.
///
///
public SceneProcessorBase GetSceneProcessor() => _sceneProcessor;
+
///
/// Sets the SceneProcessor to use.
///
///
public void SetSceneProcessor(SceneProcessorBase value) => _sceneProcessor = value;
+
///
/// NetworkManager for this script.
///
@@ -255,8 +258,8 @@ private void ServerManager_OnServerConnectionState(ServerConnectionStateArgs obj
//If no servers are started.
if (!NetworkManager.ServerManager.AnyServerStarted())
ResetValues();
-
}
+
///
/// Resets as if first use.
///
@@ -368,6 +371,7 @@ private void OnClientEmptyStartScenes(EmptyStartScenesBroadcast msg, Channel cha
TryInvokeLoadedStartScenes(_clientManager.Connection, false);
_clientManager.Broadcast(msg);
}
+
///
/// Received on server when client confirms there are no start scenes.
///
@@ -405,9 +409,7 @@ private void ClientDisconnected(NetworkConnection conn)
bool removed = hs.Remove(conn);
/* If no more observers for scene, not a global scene, and not to be manually unloaded
* then remove scene from SceneConnections and unload it. */
- if (removed && hs.Count == 0 &&
- !IsGlobalScene(scene) && !_manualUnloadScenes.Contains(scene) &&
- (scene != activeScene))
+ if (removed && hs.Count == 0 && !IsGlobalScene(scene) && !_manualUnloadScenes.Contains(scene) && (scene != activeScene))
scenesToUnload.Add(scene);
}
@@ -476,6 +478,7 @@ private void TryInvokeOnQueueStart()
IteratingQueue = true;
OnQueueStart?.Invoke();
}
+
///
/// Checks if OnQueueEnd should invoke, and if so invokes.
///
@@ -489,6 +492,7 @@ private void TryInvokeOnQueueEnd()
QueueCompleteTime = Time.unscaledTime;
OnQueueEnd?.Invoke();
}
+
///
/// Invokes that a scene load has started. Only called when valid scenes will be loaded.
///
@@ -498,6 +502,7 @@ private void InvokeOnSceneLoadStart(LoadQueueData qd)
TryInvokeOnQueueStart();
OnLoadStart?.Invoke(new(qd));
}
+
///
/// Invokes that a scene load has ended. Only called after a valid scene has loaded.
///
@@ -513,6 +518,7 @@ private void InvokeOnSceneLoadEnd(LoadQueueData qd, List requestedLoadSc
SceneLoadEndEventArgs args = new(qd, skippedScenes.ToArray(), loadedScenes.ToArray(), unloadedSceneNames);
OnLoadEnd?.Invoke(args);
}
+
///
/// Invokes that a scene unload has started. Only called when valid scenes will be unloaded.
///
@@ -522,6 +528,7 @@ private void InvokeOnSceneUnloadStart(UnloadQueueData sqd)
TryInvokeOnQueueStart();
OnUnloadStart?.Invoke(new(sqd));
}
+
///
/// Invokes that a scene unload has ended. Only called after a valid scene has unloaded.
///
@@ -531,6 +538,7 @@ private void InvokeOnSceneUnloadEnd(UnloadQueueData sqd, List unloadedSce
SceneUnloadEndEventArgs args = new(sqd, unloadedScenes, newUnloadedScenes);
OnUnloadEnd?.Invoke(args);
}
+
///
/// Invokes when completion percentage changes while unloading or unloading a scene. Value is between 0f and 1f, while 1f is 100% done.
///
@@ -553,12 +561,13 @@ private void QueueOperation(object data)
//Add to scene queue data.
_queuedOperations.Add(data);
/* If only one entry then scene operations are not currently in progress.
- * Should there be more than one entry then scene operations are already
+ * Should there be more than one entry then scene operations are already
* occuring. The coroutine will automatically load in order. */
if (_queuedOperations.Count == 1)
StartCoroutine(__ProcessSceneQueue());
}
+
///
/// Processes queued scene operations.
///
@@ -704,6 +713,7 @@ public void LoadGlobalScenes(SceneLoadData sceneLoadData)
{
LoadGlobalScenes_Internal(sceneLoadData, _globalScenes, true);
}
+
private void LoadGlobalScenes_Internal(SceneLoadData sceneLoadData, string[] globalScenes, bool asServer)
{
if (!CanExecute(asServer, true))
@@ -730,6 +740,7 @@ public void LoadConnectionScenes(NetworkConnection conn, SceneLoadData sceneLoad
//This cannot use cache because the array will persist for many frames after this method completion.
LoadConnectionScenes(new NetworkConnection[] { conn }, sceneLoadData);
}
+
///
/// Loads scenes on server and tells connections to load them as well. Other connections will not load this scene.
///
@@ -739,6 +750,7 @@ public void LoadConnectionScenes(NetworkConnection[] conns, SceneLoadData sceneL
{
LoadConnectionScenes_Internal(conns, sceneLoadData, _globalScenes, true);
}
+
///
/// Loads scenes on server without telling clients to load the scenes.
///
@@ -747,6 +759,7 @@ public void LoadConnectionScenes(SceneLoadData sceneLoadData)
{
LoadConnectionScenes_Internal(Array.Empty(), sceneLoadData, _globalScenes, true);
}
+
private void LoadConnectionScenes_Internal(NetworkConnection[] conns, SceneLoadData sceneLoadData, string[] globalScenes, bool asServer)
{
if (!CanExecute(asServer, true))
@@ -855,7 +868,6 @@ private IEnumerator __LoadScenes()
int index = _globalScenes.Length;
Array.Resize(ref _globalScenes, _globalScenes.Length + names.Length);
Array.Copy(names, 0, _globalScenes, index, names.Length);
-
}
CheckForDuplicateGlobalSceneNames();
data.GlobalScenes = _globalScenes;
@@ -863,7 +875,7 @@ private IEnumerator __LoadScenes()
/* Scene queue data scenes.
- * All scenes in the scene queue data whether they will be loaded or not. */
+ * All scenes in the scene queue data whether they will be loaded or not. */
List requestedLoadSceneNames = new();
List requestedLoadSceneHandles = new();
@@ -913,7 +925,7 @@ private IEnumerator __LoadScenes()
}
/* Move identities
- * to holder scene to preserve them.
+ * to holder scene to preserve them.
* Required if a single scene is specified. Cannot rely on
* loadSingleScene since it is only true if the single scene
* must be loaded, which may be false if it's already loaded on
@@ -967,9 +979,9 @@ private IEnumerator __LoadScenes()
/* Scene unloading if replacing scenes.
- *
- * Unload all scenes except MovedObjectsHolder. Also don't
- * unload GlobalScenes if loading as connection. */
+ *
+ * Unload all scenes except MovedObjectsHolder. Also don't
+ * unload GlobalScenes if loading as connection. */
List unloadableScenes = new();
//Do not run if running as client, and server is active. This would have already run as server.
if ((replaceScenes != ReplaceOption.None) && !asHost)
@@ -982,8 +994,8 @@ private IEnumerator __LoadScenes()
if (s == GetMovedObjectsScene())
continue;
/* Scene is in one of the scenes being loaded.
- * This can occur when trying to load additional clients
- * into an existing scene. */
+ * This can occur when trying to load additional clients
+ * into an existing scene. */
if (requestedLoadSceneNames.Contains(s.name))
continue;
//Same as above but using handles.
@@ -1074,8 +1086,8 @@ private IEnumerator __LoadScenes()
};
/* How much percentage each scene load can be worth
- * at maximum completion. EG: if there are two scenes
- * 1f / 2f is 0.5f. */
+ * at maximum completion. EG: if there are two scenes
+ * 1f / 2f is 0.5f. */
float maximumIndexWorth = (1f / (float)loadableScenes.Count);
_sceneProcessor.BeginLoadAsync(loadableScenes[i].Name, loadSceneParameters);
@@ -1090,8 +1102,8 @@ private IEnumerator __LoadScenes()
void InvokePercentageChange(int index, float maximumWorth, float currentScenePercent)
{
/* Total percent will be how much percentage is complete
- * in total. Initialize it with a value based on how many
- * scenes are already fully loaded. */
+ * in total. Initialize it with a value based on how many
+ * scenes are already fully loaded. */
float totalPercent = (index * maximumWorth);
//Add this scenes progress onto total percent.
totalPercent += Mathf.Lerp(0f, maximumWorth, currentScenePercent);
@@ -1140,7 +1152,7 @@ void InvokePercentageChange(int index, float maximumWorth, float currentScenePer
lastSameSceneName = s;
}
- /* Shouldn't be possible since the scene will always exist either by
+ /* Shouldn't be possible since the scene will always exist either by
* just being loaded or already loaded. */
if (string.IsNullOrEmpty(lastSameSceneName.name))
NetworkManager.LogError($"Scene {sceneLoadData.SceneLookupDatas[0].Name} could not be found in loaded scenes.");
@@ -1207,6 +1219,7 @@ Scene GetFirstLoadedScene()
} while (!allScenesLoaded);
SetActiveScene_Local();
+
void SetActiveScene_Local()
{
bool byUser;
@@ -1214,15 +1227,20 @@ void SetActiveScene_Local()
//If preferred still is not set then try to figure it out.
if (!preferredActiveScene.IsValid())
{
- /* Populate preferred scene to first loaded if replacing
- * scenes for connection. Does not need to be set for
- * global because when a global exist it's always set
- * as the active scene.
- *
- * Do not set preferred scene if server as this could cause
- * problems when stacking or connection specific scenes. Let the
- * user make those changes. */
- if (sceneLoadData.ReplaceScenes != ReplaceOption.None && data.ScopeType == SceneScopeType.Connections && !NetworkManager.IsServerStarted)
+ bool setToFirstLookup = false;
+ //If any scenes are being replaced see if active needs to be updated.
+ if (sceneLoadData.ReplaceScenes != ReplaceOption.None)
+ {
+ //If load is for a connection and server isnt started.
+ setToFirstLookup |= (data.ScopeType == SceneScopeType.Connections && !NetworkManager.IsServerStarted);
+ /* If current active is the movedObjectsHolder, such as moved objects.
+ * This can happen when replacing a scene that was active and the next in line is
+ * set by unity as one of the temp scenes. */
+ Scene activeScene = UnitySceneManager.GetActiveScene();
+ setToFirstLookup |= (activeScene == GetMovedObjectsScene());
+ }
+
+ if (setToFirstLookup)
preferredActiveScene = sceneLoadData.GetFirstLookupScene();
}
@@ -1355,13 +1373,13 @@ public void UnloadGlobalScenes(SceneUnloadData sceneUnloadData)
UnloadGlobalScenes_Internal(sceneUnloadData, _globalScenes, true);
}
+
private void UnloadGlobalScenes_Internal(SceneUnloadData sceneUnloadData, string[] globalScenes, bool asServer)
{
UnloadQueueData uqd = new(SceneScopeType.Global, Array.Empty(), sceneUnloadData, globalScenes, asServer);
QueueOperation(uqd);
}
-
///
/// Unloads scenes on server and tells a connection to unload them as well. Other connections will not unload this scene.
///
@@ -1372,6 +1390,7 @@ public void UnloadConnectionScenes(NetworkConnection connection, SceneUnloadData
//This cannot use cache because the array will persist for many frames after this method completion.
UnloadConnectionScenes(new NetworkConnection[] { connection }, sceneUnloadData);
}
+
///
/// Unloads scenes on server and tells connections to unload them as well. Other connections will not unload this scene.
///
@@ -1390,6 +1409,7 @@ public void UnloadConnectionScenes(SceneUnloadData sceneUnloadData)
{
UnloadConnectionScenes_Internal(Array.Empty(), sceneUnloadData, _globalScenes, true);
}
+
private void UnloadConnectionScenes_Internal(NetworkConnection[] connections, SceneUnloadData sceneUnloadData, string[] globalScenes, bool asServer)
{
if (!CanExecute(asServer, true))
@@ -1400,6 +1420,7 @@ private void UnloadConnectionScenes_Internal(NetworkConnection[] connections, Sc
UnloadQueueData uqd = new(SceneScopeType.Connections, connections, sceneUnloadData, globalScenes, asServer);
QueueOperation(uqd);
}
+
///
/// Loads scenes within QueuedSceneLoads.
///
@@ -1414,7 +1435,7 @@ private IEnumerator __UnloadScenes()
yield break;
/* Some actions should not run as client if server is also active.
- * This is to keep things from running twice. */
+ * This is to keep things from running twice. */
bool asClientHost = (!data.AsServer && NetworkManager.IsServerStarted);
///True if running asServer.
bool asServer = data.AsServer;
@@ -1432,10 +1453,10 @@ private IEnumerator __UnloadScenes()
}
/* Remove from global scenes
- * if server and scope is global.
- * All passed in scenes should be removed from global
- * regardless of if they're valid or not. If they are invalid,
- * then they shouldn't be in global to begin with. */
+ * if server and scope is global.
+ * All passed in scenes should be removed from global
+ * regardless of if they're valid or not. If they are invalid,
+ * then they shouldn't be in global to begin with. */
if (asServer && data.ScopeType == SceneScopeType.Global)
{
RemoveFromGlobalScenes(sceneUnloadData.SceneLookupDatas);
@@ -1458,7 +1479,6 @@ private IEnumerator __UnloadScenes()
}
-
/* This will contain all scenes which can be unloaded.
* The collection will be modified through various checks. */
List unloadableScenes = scenes.ToList();
@@ -1503,7 +1523,7 @@ private IEnumerator __UnloadScenes()
/* Remove from manualUnloadedScenes.
* Scene may not be in this collection
* but removing is one call vs checking
- * then removing. */
+ * then removing. */
_manualUnloadScenes.Remove(s);
_sceneProcessor.BeginUnloadAsync(s);
@@ -1515,11 +1535,11 @@ private IEnumerator __UnloadScenes()
}
/* Must yield after sceneProcessor handles things.
- * This is a Unity bug of sorts. I'm not entirely sure what
- * is happening, but without the yield it seems as though
- * the processor logic doesn't complete. This doesn't make much
- * sense given unity is supposed to be single threaded. Must be
- * something to do with the coroutine. */
+ * This is a Unity bug of sorts. I'm not entirely sure what
+ * is happening, but without the yield it seems as though
+ * the processor logic doesn't complete. This doesn't make much
+ * sense given unity is supposed to be single threaded. Must be
+ * something to do with the coroutine. */
yield return null;
bool byUser;
@@ -1570,7 +1590,6 @@ private IEnumerator __UnloadScenes()
InvokeOnSceneUnloadEnd(data, unloadableScenes, unloadedScenes);
}
-
///
/// Received on clients when networked scenes must be unloaded.
///
@@ -1645,7 +1664,7 @@ private void MoveClientHostObjects(Scene scene, bool asServer)
nob.ClearRuntimeSceneObject();
/* If the object is already being despawned then
*just disable and move it. Otherwise despawn it
- * on the server then move it. */
+ * on the server then move it. */
//Not deinitializing, despawn it then.
if (!nob.IsDeinitializing)
nob.Despawn();
@@ -1722,13 +1741,12 @@ public void AddConnectionToScene(NetworkConnection conn, Scene scene)
InvokeClientPresenceChange(scene, arrayConn, true, false);
/* Also need to rebuild all networkobjects
- * for connection so other players can
- * see them. */
+ * for connection so other players can
+ * see them. */
RebuildObservers(conn.Objects.ToArray());
}
}
-
///
/// Removes connections from any scene which is not global.
/// Exposed for power users, use caution.
@@ -1783,7 +1801,6 @@ public void RemoveConnectionsFromNonGlobalScenes(NetworkConnection[] conns)
RebuildObservers(c.Objects.ToArray());
}
-
///
/// Removes connections from specified scenes.
/// Exposed for power users, use caution.
@@ -1825,8 +1842,8 @@ public void RemoveConnectionsFromScene(NetworkConnection[] conns, Scene scene)
}
/* Also rebuild observers for objects owned by connection.
- * This ensures other connections will lose visibility if
- * they no longer share a scene. */
+ * This ensures other connections will lose visibility if
+ * they no longer share a scene. */
foreach (NetworkConnection c in conns)
RebuildObservers(c.Objects.ToArray());
}
@@ -1915,6 +1932,7 @@ private void RebuildObservers(IList networkObjects)
_serverManager.Objects.RebuildObservers(nob);
}
}
+
///
/// Rebuilds all NetworkObjects for connection.
///
@@ -1924,6 +1942,7 @@ internal void RebuildObservers(NetworkConnection connection)
RebuildObservers(connCache);
CollectionCaches.Store(connCache);
}
+
///
/// Rebuilds all NetworkObjects for connections.
///
@@ -1933,6 +1952,7 @@ internal void RebuildObservers(IList connections)
for (int i = 0; i < count; i++)
_serverManager.Objects.RebuildObservers(connections[i]);
}
+
///
/// Invokes OnClientPresenceChange start or end.
///
@@ -2012,6 +2032,7 @@ public static Scene GetScene(string sceneName, NetworkManager nm = null, bool wa
return result;
}
+
///
/// Returns a scene by handle.
///
@@ -2047,7 +2068,6 @@ private bool IsGlobalScene(Scene scene)
return false;
}
-
///
/// Warns if any scene names in GlobalScenes are unsupported.
/// This only applies to FishNet version 3.
@@ -2071,7 +2091,6 @@ private void CheckForDuplicateGlobalSceneNames()
}
}
-
///
/// Removes datas from GlobalScenes.
///
@@ -2080,6 +2099,7 @@ private void RemoveFromGlobalScenes(Scene scene)
{
RemoveFromGlobalScenes(new SceneLookupData[] { SceneLookupData.CreateData(scene) });
}
+
///
/// Removes datas from GlobalScenes.
///
@@ -2146,6 +2166,7 @@ private void AddPendingLoad(NetworkConnection conn)
AddPendingLoad(conns, 1);
CollectionCaches.Store(conns, 1);
}
+
///
/// Adds a pending load for a connection.
///
@@ -2155,8 +2176,8 @@ private void AddPendingLoad(NetworkConnection[] conns, int count)
{
/* Make sure connection is active. This should always be true
* but perhaps disconnect happened as scene was loading on server
- * therefor it cannot be sent to the client.
- * Also only authenticated clients can load scenes. */
+ * therefor it cannot be sent to the client.
+ * Also only authenticated clients can load scenes. */
if (!c.IsActive || !c.IsAuthenticated)
continue;
@@ -2166,6 +2187,7 @@ private void AddPendingLoad(NetworkConnection[] conns, int count)
_pendingClientSceneChanges[c] = 1;
}
}
+
///
/// Sets the first global scene as the active scene.
/// If a global scene is not available then FallbackActiveScene is used.
@@ -2269,6 +2291,7 @@ internal bool IsIteratingQueue(float completionTimeRequirement = 0f)
{
return (IteratingQueue || (Time.unscaledTime - QueueCompleteTime) < completionTimeRequirement);
}
+
///
/// Returns if a SceneLoadData is valid.
///
@@ -2283,6 +2306,7 @@ private bool SceneDataInvalid(SceneLoadData data, bool error)
return result;
}
+
///
/// Returns if a SceneLoadData is valid.
///
@@ -2298,6 +2322,7 @@ private bool SceneDataInvalid(SceneUnloadData data, bool error)
return result;
}
+
///
/// Returns if connection is active for server or client in association with AsServer.
///
@@ -2307,6 +2332,7 @@ private bool ConnectionActive(bool asServer)
{
return (asServer) ? NetworkManager.IsServerStarted : NetworkManager.IsClientStarted;
}
+
///
/// Returns if a method can execute.
///
@@ -2332,6 +2358,5 @@ private bool CanExecute(bool asServer, bool warn)
return result;
}
#endregion
-
}
}
\ No newline at end of file
diff --git a/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs
index 6804447b..5462cfda 100644
--- a/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs
+++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs
@@ -393,7 +393,6 @@ private void SetupSceneObjects(Scene s)
CollectionCaches.Store(sceneNobs);
}
-
///
/// Setup NetworkObjects in a scene. Should only be called when server is active.
///
@@ -591,8 +590,12 @@ private void SpawnWithoutChecks(NetworkObject networkObject, NetworkConnection o
if (NetworkManager.IsClientStarted)
{
int count = spawnCacheCopyCount;
+ NetworkConnection localConnection = NetworkManager.ClientManager.Connection;
for (int i = 0; i < count; i++)
- spawnCacheCopy[i].SetRenderersVisible(networkObject.Observers.Contains(NetworkManager.ClientManager.Connection));
+ {
+ NetworkObject nob = spawnCacheCopy[i];
+ nob.SetRenderersVisible(nob.Observers.Contains(localConnection));
+ }
}
CollectionCaches.Store(spawnCacheCopy);
@@ -685,7 +688,7 @@ internal void ReadSpawn(PooledReader reader, NetworkConnection conn)
nob.Preinitialize_Internal(NetworkManager, objectId, owner, true);
//Initialize for prediction.
nob.InitializePredictedObject_Server(base.NetworkManager, conn);
-
+
base.ReadPayload(conn, nob, reader);
//Check user implementation of trySpawn.
@@ -700,7 +703,7 @@ internal void ReadSpawn(PooledReader reader, NetworkConnection conn)
/* This initializes the object again which cost only a small
* amount of perf. Once the refactor is complete this will be changed. */
SpawnWithoutChecks(nob, owner, objectId);
-
+
void SendFailedResponse(int objectId)
{
SkipRemainingSpawnLength();
@@ -740,7 +743,7 @@ void SendResponse(bool success, int objectId)
rw.Initialize(writer, NetworkBehaviour.RPCLINK_RESERVED_BYTES);
foreach (NetworkBehaviour nb in nob.NetworkBehaviours)
nb.WriteRpcLinks(writer);
-
+
rw.WriteLength();
}
diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs
index 820db919..3a24f5bd 100644
--- a/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs
+++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs
@@ -20,6 +20,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
+using GameKit.Dependencies.Utilities.Types;
using UnityEngine;
[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)]
@@ -57,7 +58,7 @@ public enum DataPlacementResult
///
/// Gets the index in replicates where the tick matches.
///
- public static int GetReplicateHistoryIndex(uint tick, List replicatesHistory, out DataPlacementResult findResult) where T : IReplicateData
+ public static int GetReplicateHistoryIndex(uint tick, RingBuffer replicatesHistory, out DataPlacementResult findResult) where T : IReplicateData
{
int replicatesCount = replicatesHistory.Count;
if (replicatesCount == 0)
@@ -214,9 +215,9 @@ public abstract partial class NetworkBehaviour : MonoBehaviour
///
private List _readReplicateTicks;
///
- /// Last tick read for a reconcile.
+ /// Last tick read for a reconcile. This is only set on the client.
///
- private uint _lastReadReconcileTick = TimeManager.UNSET_TICK;
+ private uint _lastReadReconcileRemoteTick = TimeManager.UNSET_TICK;
///
/// Last tick this object reconciled on.
///
@@ -238,7 +239,6 @@ public abstract partial class NetworkBehaviour : MonoBehaviour
/// Last values when checking for transform changes since previous tick.
///
private Vector3 _lastTransformScale;
-
#endregion
#region Consts.
@@ -342,7 +342,7 @@ internal void ResetState_Prediction(bool asServer)
{
if (_readReplicateTicks != null)
_readReplicateTicks.Clear();
- _lastReadReconcileTick = TimeManager.UNSET_TICK;
+ _lastReadReconcileRemoteTick = TimeManager.UNSET_TICK;
_lastReconcileTick = TimeManager.UNSET_TICK;
}
@@ -364,7 +364,7 @@ public virtual void ClearReplicateCache() { }
///
[MakePublic]
[APIExclude]
- protected internal void ClearReplicateCache_Internal(BasicQueue replicatesQueue, List replicatesHistory, ref T lastFirstReadReplicate) where T : IReplicateData
+ protected internal void ClearReplicateCache_Internal(BasicQueue replicatesQueue, RingBuffer replicatesHistory, RingBuffer> reconcilesHistory, ref T lastFirstReadReplicate) where T : IReplicateData where T2 : IReconcileData
{
while (replicatesQueue.Count > 0)
{
@@ -378,6 +378,10 @@ protected internal void ClearReplicateCache_Internal(BasicQueue replicates
for (int i = 0; i < replicatesHistory.Count; i++)
replicatesHistory[i].Dispose();
replicatesHistory.Clear();
+
+ // for (int i = 0; i < reconcilesHistory.Count; i++)
+ // reconcilesHistory[i].Dispose();
+ // reconcilesHistory.Clear();
}
///
@@ -394,8 +398,8 @@ protected internal void Server_SendReconcileRpc(uint hash, ref T lastReconcil
//No more redundancy left. Check if transform may have changed.
if (_remainingReconcileResends == 0)
return;
- // else
- // _remainingReconcileResends--;
+ else
+ _remainingReconcileResends--;
//No owner and no state forwarding, nothing to do.
bool stateForwarding = _networkObjectCache.EnableStateForwarding;
@@ -501,7 +505,7 @@ internal virtual void Replicate_Replay_Start(uint replayTick) { }
///
/// Replays inputs from replicates.
///
- protected internal void Replicate_Replay(uint replayTick, ReplicateUserLogicDelegate del, List replicatesHistory, Channel channel) where T : IReplicateData
+ protected internal void Replicate_Replay(uint replayTick, ReplicateUserLogicDelegate del, RingBuffer replicatesHistory, Channel channel) where T : IReplicateData
{
//Reconcile data was not received so cannot replay.
if (!IsBehaviourReconciling)
@@ -516,7 +520,7 @@ protected internal void Replicate_Replay(uint replayTick, ReplicateUserLogicD
///
/// Replays an input for authoritative entity.
///
- protected internal void Replicate_Replay_Authoritative(uint replayTick, ReplicateUserLogicDelegate del, List replicatesHistory, Channel channel) where T : IReplicateData
+ protected internal void Replicate_Replay_Authoritative(uint replayTick, ReplicateUserLogicDelegate del, RingBuffer replicatesHistory, Channel channel) where T : IReplicateData
{
ReplicateTickFinder.DataPlacementResult findResult;
int replicateIndex = ReplicateTickFinder.GetReplicateHistoryIndex(replayTick, replicatesHistory, out findResult);
@@ -537,7 +541,7 @@ protected internal void Replicate_Replay_Authoritative(uint replayTick, Repli
///
/// Replays an input for non authoritative entity.
///
- protected internal void Replicate_Replay_NonAuthoritative(uint replayTick, ReplicateUserLogicDelegate del, List replicatesHistory, Channel channel) where T : IReplicateData
+ protected internal void Replicate_Replay_NonAuthoritative(uint replayTick, ReplicateUserLogicDelegate del, RingBuffer replicatesHistory, Channel channel) where T : IReplicateData
{
//NOTESSTART
/* When inserting states only replay the first state after the reconcile.
@@ -600,7 +604,7 @@ protected internal virtual void EmptyReplicatesQueueIntoHistory_Start() { }
/// This should only be called when client only.
///
[MakePublic]
- protected internal void EmptyReplicatesQueueIntoHistory(BasicQueue replicatesQueue, List replicatesHistory) where T : IReplicateData
+ protected internal void EmptyReplicatesQueueIntoHistory(BasicQueue replicatesQueue, RingBuffer replicatesHistory) where T : IReplicateData
{
while (replicatesQueue.TryDequeue(out T data))
InsertIntoReplicateHistory(data.GetTick(), data, replicatesHistory);
@@ -612,7 +616,7 @@ protected internal void EmptyReplicatesQueueIntoHistory(BasicQueue replica
///
[MakePublic]
[APIExclude]
- protected internal void Replicate_NonAuthoritative(ReplicateUserLogicDelegate del, BasicQueue replicatesQueue, List replicatesHistory, Channel channel) where T : IReplicateData
+ protected internal void Replicate_NonAuthoritative(ReplicateUserLogicDelegate del, BasicQueue replicatesQueue, RingBuffer replicatesHistory, Channel channel) where T : IReplicateData
{
bool serverStarted = _networkObjectCache.IsServerStarted;
bool ownerlessAndServer = (!Owner.IsValid && serverStarted);
@@ -669,7 +673,9 @@ protected internal void Replicate_NonAuthoritative(ReplicateUserLogicDelegate
_remainingReconcileResends = pm.RedundancyCount;
ReplicateData(queueEntry, ReplicateState.CurrentCreated);
- count--;
+
+ //Update count since old entries were dropped and one replicate run.
+ count = replicatesQueue.Count;
bool consumeExcess = (!pm.DropExcessiveReplicates || IsClientOnlyStarted);
int leaveInBuffer = _networkObjectCache.PredictionManager.StateInterpolation;
@@ -677,10 +683,10 @@ protected internal void Replicate_NonAuthoritative(ReplicateUserLogicDelegate
//Only consume if the queue count is over leaveInBuffer.
if (consumeExcess && count > leaveInBuffer)
{
- byte maximumAllowedConsumes = 1;
+ const byte maximumAllowedConsumes = 1;
int maximumPossibleConsumes = (count - leaveInBuffer);
int consumeAmount = Mathf.Min(maximumAllowedConsumes, maximumPossibleConsumes);
-
+
for (int i = 0; i < consumeAmount; i++)
ReplicateData(replicatesQueue.Dequeue(), ReplicateState.CurrentCreated);
}
@@ -722,7 +728,7 @@ void ReplicateData(T data, ReplicateState state)
//Server always adds.
if (isServer)
{
- replicatesHistory.Add(data);
+ AddReplicatesHistory(replicatesHistory, data);
}
//If client insert value into history.
else
@@ -753,7 +759,7 @@ uint GetDefaultedLastReplicateTick()
/// True if data has changed..
[MakePublic] //internal
[APIExclude]
- protected internal void Replicate_Authoritative(ReplicateUserLogicDelegate del, uint methodHash, BasicQueue replicatesQueue, List replicatesHistory, T data, Channel channel) where T : IReplicateData
+ protected internal void Replicate_Authoritative(ReplicateUserLogicDelegate del, uint methodHash, BasicQueue replicatesQueue, RingBuffer replicatesHistory, T data, Channel channel) where T : IReplicateData
{
bool ownerlessAndServer = (!Owner.IsValid && IsServerStarted);
if (!IsOwner && !ownerlessAndServer)
@@ -792,12 +798,13 @@ protected internal void Replicate_Authoritative(ReplicateUserLogicDelegate
replicatesHistory[i].Dispose();
//Then remove range.
- replicatesHistory.RemoveRange(0, removeCount);
+ replicatesHistory.RemoveRange(true, removeCount);
}
}
data.SetTick(dataTick);
- replicatesHistory.Add(data);
+ AddReplicatesHistory(replicatesHistory, data);
+
//Check to reset resends.
bool isDefault = isDefaultDel.Invoke(data);
bool resetResends = (!isDefault || TransformChanged());
@@ -828,7 +835,7 @@ protected internal void Replicate_Authoritative(ReplicateUserLogicDelegate
//Owner always replicates with new data.
del.Invoke(data, ReplicateState.CurrentCreated, channel);
}
-
+
///
/// Returns the DeltaSerializeOption to use for the tick.
///
@@ -858,7 +865,7 @@ internal DeltaSerializerOption GetDeltaSerializeOption()
///
/// Sends a Replicate to server or clients.
///
- private void Replicate_SendAuthoritative(bool toServer, uint hash, int pastInputs, List replicatesHistory, uint queuedTick, Channel channel, DeltaSerializerOption deltaOption) where T : IReplicateData
+ private void Replicate_SendAuthoritative(bool toServer, uint hash, int pastInputs, RingBuffer replicatesHistory, uint queuedTick, Channel channel, DeltaSerializerOption deltaOption) where T : IReplicateData
{
/* Do not use IsSpawnedWithWarning because the server
* will still call this a tick or two as clientHost when
@@ -933,7 +940,7 @@ private void Replicate_SendAuthoritative(bool toServer, uint hash, int pastIn
/// Reads a replicate the client.
///
[MakePublic]
- internal void Replicate_Reader(uint hash, PooledReader reader, NetworkConnection sender, ref T lastReadReplicate, ref T[] arrBuffer, BasicQueue replicatesQueue, List replicatesHistory, Channel channel) where T : IReplicateData
+ internal void Replicate_Reader(uint hash, PooledReader reader, NetworkConnection sender, ref T lastReadReplicate, ref T[] arrBuffer, BasicQueue replicatesQueue, RingBuffer replicatesHistory, Channel channel) where T : IReplicateData
{
/* This will never be received on owner, except in the condition
* the server is the owner and also a client. In such condition
@@ -1063,7 +1070,7 @@ internal void Replicate_SendNonAuthoritative(uint hash, BasicQueue replica
///
/// Handles a received replicate packet.
///
- private void Replicate_EnqueueReceivedReplicate(int receivedReplicatesCount, T[] arrBuffer, BasicQueue replicatesQueue, List replicatesHistory, Channel channel) where T : IReplicateData
+ private void Replicate_EnqueueReceivedReplicate(int receivedReplicatesCount, T[] arrBuffer, BasicQueue replicatesQueue, RingBuffer replicatesHistory, Channel channel) where T : IReplicateData
{
int startQueueCount = replicatesQueue.Count;
/* Owner never gets this for their own object so
@@ -1137,7 +1144,7 @@ private void Replicate_EnqueueReceivedReplicate(int receivedReplicatesCount,
/// Inserts data into the replicatesHistory collection.
/// This should only be called when client only.
///
- private void InsertIntoReplicateHistory(uint tick, T data, List replicatesHistory) where T : IReplicateData
+ private void InsertIntoReplicateHistory(uint tick, T data, RingBuffer replicatesHistory) where T : IReplicateData
{
/* See if replicate tick is in history. Keep in mind
* this is the localTick from the server, not the localTick of
@@ -1157,21 +1164,38 @@ private void InsertIntoReplicateHistory(uint tick, T data, List replicates
}
else if (findResult == ReplicateTickFinder.DataPlacementResult.InsertMiddle)
{
- replicatesHistory.Insert(index, data);
+ InsertReplicatesHistory(replicatesHistory, data, index);
}
else if (findResult == ReplicateTickFinder.DataPlacementResult.InsertEnd)
{
- replicatesHistory.Add(data);
+ AddReplicatesHistory(replicatesHistory, data);
}
/* Insert beginning should not happen unless the data is REALLY old.
* This would mean the network was in an unplayable state. Discard the
* data. */
if (findResult == ReplicateTickFinder.DataPlacementResult.InsertBeginning)
- {
- //data.Dispose();
- replicatesHistory.Insert(0, data);
- }
+ InsertReplicatesHistory(replicatesHistory, data, 0);
+ }
+
+ ///
+ /// Adds to replicate history disposing of old entries if needed.
+ ///
+ private void AddReplicatesHistory(RingBuffer replicatesHistory, T value) where T : IReplicateData
+ {
+ T prev = replicatesHistory.Add(value);
+ if (prev != null)
+ prev.Dispose();
+ }
+
+ ///
+ /// Inserts to replicate history disposing of old entries if needed.
+ ///
+ private void InsertReplicatesHistory(RingBuffer replicatesHistory, T value, int index) where T : IReplicateData
+ {
+ T prev = replicatesHistory.Insert(index, value);
+ if (prev != null)
+ prev.Dispose();
}
///
@@ -1201,16 +1225,137 @@ protected internal virtual void Reconcile_Client_Start() { }
///
[APIExclude]
[MakePublic]
- protected internal void Reconcile_Client(ReconcileUserLogicDelegate reconcileDel, List replicatesHistory, T data) where T : IReconcileData where T2 : IReplicateData
+ protected internal void Reconcile_Client_Local(RingBuffer> reconcilesHistory, T data) where T : IReconcileData
+ {
+ // //Server does not need to store these locally.
+ // if (_networkObjectCache.IsServerStarted)
+ // return;
+ //
+ // /* This is called by the local client when creating
+ // * a local reconcile state. These states should always
+ // * be in order, so we will add data to the end
+ // * of the collection. */
+ //
+ // /* These datas are used to fill missing reconciles
+ // * be it the packet dropped, server doesnt need to send,
+ // * or if the player is throttling reconciles. */
+ //
+ // uint tick = _networkObjectCache.PredictionManager.GetCreateReconcileTick(_networkObjectCache.IsOwner);
+ // //Tick couldn't be retrieved.
+ // if (tick == TimeManager.UNSET_TICK)
+ // return;
+ //
+ // data.SetTick(tick);
+ //
+ // //Build LocalReconcile.
+ // LocalReconcile lr = new();
+ // lr.Initialize(tick, data);
+ //
+ // reconcilesHistory.Add(lr);
+ }
+
+ //private bool _skip = false;
+ ///
+ /// Processes a reconcile for client.
+ ///
+ [APIExclude]
+ [MakePublic]
+ protected internal void Reconcile_Client(ReconcileUserLogicDelegate reconcileDel, RingBuffer replicatesHistory, RingBuffer> reconcilesHistory, T data) where T : IReconcileData where T2 : IReplicateData
{
- //No data to reconcile to.
+
if (!IsBehaviourReconciling)
+ {
+ Debug.Log("Not reconciling.");
return;
+ }
+
+ //
+ // if (_skip)
+ // IsBehaviourReconciling = false;
+ //
+ // _skip = !_skip;
+ // /* If there is no data to reconcile from then
+ // * try to pull from the reconcilesHistory; this is the
+ // * collection the client had made locally.*/
+ // if (!IsBehaviourReconciling)
+ // {
+ // //Should not ever happen, but cannot proceed without entries.
+ // if (reconcilesHistory.Count == 0)
+ // return;
+ //
+ // uint firstHistoryTick = reconcilesHistory[0].Tick;
+ // uint reconcileTick = _networkObjectCache.PredictionManager.GetReconcileStateTick(_networkObjectCache.IsOwner); //(_lastReconcileTick + 1);
+ //
+ // long historyIndex = ((long)reconcileTick - (long)firstHistoryTick);
+ //
+ // /* If difference is negative then negative then
+ // * the first history is beyond the tick being reconciled.
+ // * EG: if history index 0 is 100 and reconcile tick is 90 then
+ // * (90 - 100) = -10.
+ // * This should only happen when first connecting and data hasn't been made yet. */
+ // if (historyIndex < 0)
+ // return;
+ //
+ // //Not enough histories to grab local reconcile form.
+ // if (reconcilesHistory.Count <= historyIndex)
+ // return;
+ //
+ // LocalReconcile localReconcile = reconcilesHistory[(int)historyIndex];
+ // uint lrTick = localReconcile.Tick;
+ // /* Since we store reconcile data every tick moving ahead a set number of ticks
+ // * should usually match up to the reconcile tick. There are exceptions where the tick
+ // * used to locally create the reconcile was for non owner, so using the server tick,
+ // * and there is a slight misalignment in the server tick. This is not unusual as the
+ // * client corrects it's tick timing regularly, but such an alignment could make this not line up. */
+ // if (lrTick != reconcileTick)
+ // {
+ // //When tick doesn't match still allow use of the entry if it's within 3 ticks.
+ // long mismatchDifference = Math.Abs((long)lrTick - (long)reconcileTick);
+ // Debug.LogError($"Match difference of {mismatchDifference}");
+ // if (mismatchDifference > 3)
+ // return;
+ // }
+ // //Before disposing get the writer and call reconcile reader so it's parsed.
+ // PooledWriter reconcileWritten = reconcilesHistory[(int)historyIndex].Writer;
+ // /* Although this is actually from the local client the datasource is being set to server since server
+ // * is what typically sends reconciles. */
+ // PooledReader reader = ReaderPool.Retrieve(reconcileWritten.GetArraySegment(), null, Reader.DataSource.Server);
+ // data = Reconcile_Reader(lrTick, reader);
+ // ReaderPool.Store(reader);
+ //
+ // //If here everything is good, remove up to used index.
+ // for (int i = 0; i < historyIndex; i++)
+ // reconcilesHistory[i].Dispose();
+ //
+ // reconcilesHistory.RemoveRange(true, (int)historyIndex);
+ //
+ // //Uses iteration to try and find the tick.
+ // bool FindTickThroughIteration(uint tickToFind, out long index)
+ // {
+ // for (int i = 0; i < reconcilesHistory.Count; i++)
+ // {
+ // uint historyTick = reconcilesHistory[i].Tick;
+ // //Exact match.
+ // if (historyTick == tickToFind)
+ // {
+ // index = i;
+ // return true;
+ // }
+ // }
+ //
+ // //Failed to find.
+ // index = -1;
+ // return false;
+ // }
+ // }
+
+ //Set on the networkObject that a reconcile can now occur.
+ _networkObjectCache.IsObjectReconciling = true;
- //Remove up reconcile tick from received ticks.
uint dataTick = data.GetTick();
_lastReconcileTick = dataTick;
+ //Remove up reconcile tick from received ticks.
int readReplicatesRemovalCount = 0;
for (int i = 0; i < _readReplicateTicks.Count; i++)
{
@@ -1228,7 +1373,7 @@ protected internal void Reconcile_Client(ReconcileUserLogicDelegate re
* is the state after a replicate for it's tick we no longer
* need any replicates prior. */
//Find the closest entry which can be removed.
- int removalCount = 0;
+ int removeCount = 0;
//A few quick tests.
if (replicatesHistory.Count > 0)
{
@@ -1237,7 +1382,7 @@ protected internal void Reconcile_Client(ReconcileUserLogicDelegate re
* as reconcile is beyond them. */
if (replicatesHistory[^1].GetTick() <= dataTick)
{
- removalCount = replicatesHistory.Count;
+ removeCount = replicatesHistory.Count;
}
//Somewhere in between. Find what to remove up to.
else
@@ -1249,16 +1394,16 @@ protected internal void Reconcile_Client(ReconcileUserLogicDelegate re
* found remove up to that entry. */
if (entryTick > dataTick)
{
- removalCount = i;
+ removeCount = i;
break;
}
}
}
}
- for (int i = 0; i < removalCount; i++)
+ for (int i = 0; i < removeCount; i++)
replicatesHistory[i].Dispose();
- replicatesHistory.RemoveRange(0, removalCount);
+ replicatesHistory.RemoveRange(true, removeCount);
}
//Call reconcile user logic.
@@ -1271,7 +1416,7 @@ internal void Reconcile_Client_End()
}
///
- /// Reads a reconcile for the client.
+ /// Reads a reconcile from the server.
///
public void Reconcile_Reader(PooledReader reader, ref T lastReconciledata, Channel channel) where T : IReconcileData
{
@@ -1283,18 +1428,30 @@ public void Reconcile_Reader(PooledReader reader, ref T lastReconciledata, Ch
T newData = reader.ReadReconcile();
#endif
//Do not process if an old state.
- if (tick < _lastReadReconcileTick)
+ if (tick < _lastReadReconcileRemoteTick)
return;
lastReconciledata = newData;
lastReconciledata.SetTick(tick);
IsBehaviourReconciling = true;
- //Also set on NetworkObject since at least one behaviour is reconciling.
_networkObjectCache.IsObjectReconciling = true;
- _lastReadReconcileTick = tick;
+ _lastReadReconcileRemoteTick = tick;
}
+ // ///
+ // /// Reads a local reconcile from the client.
+ // ///
+ // public T Reconcile_Reader(uint tick, PooledReader reader) where T : IReconcileData
+ // {
+ // T newData = reader.ReadReconcile();
+ // newData.SetTick(tick);
+ //
+ // IsBehaviourReconciling = true;
+ //
+ // return newData;
+ // }
+
///
/// Sets the last tick this NetworkBehaviour replicated with.
///
diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs
index 3f69275e..56770899 100644
--- a/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs
+++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs
@@ -113,12 +113,14 @@ internal bool DirtySyncType()
{
if (!IsServerStarted)
return false;
- // /* No reason to dirty if there are no observers.
- // * This can happen even if a client is going to see
- // * this object because the server side initializes
- // * before observers are built. */
- // if (_networkObjectCache.Observers.Count == 0 && !_networkObjectCache.PredictedSpawner.IsValid)
- // return false;
+ /* No reason to dirty if there are no observers.
+ * This can happen even if a client is going to see
+ * this object because the server side initializes
+ * before observers are built. Clients which become observers
+ * will get the latest values in the spawn message, which is separate
+ * from writing dirty syncTypes. */
+ if (_networkObjectCache.Observers.Count == 0 && !_networkObjectCache.PredictedSpawner.IsValid)
+ return false;
if (!SyncTypeDirty)
_networkObjectCache.NetworkManager.ServerManager.Objects.SetDirtySyncType(this);
@@ -265,7 +267,7 @@ internal bool WriteDirtySyncTypes(SyncTypeWriteFlag flags)
dirtyCount++;
//Interval not yet met.
- if (!ignoreInterval && !sb.SyncTimeMet(tick))
+ if (!ignoreInterval && !sb.IsNextSyncTimeMet(tick))
continue;
//Unset that SyncType is dirty as it will be written now.
@@ -505,7 +507,7 @@ internal void ReadSyncTypesForSpawn(PooledReader reader)
byte syncTypeId = reader.ReadUInt8Unpacked();
if (_syncTypes.TryGetValueIL2CPP(syncTypeId, out SyncBase sb))
- sb.Read(reader, asServer: true);
+ sb.Read(reader, asServer: false);
else
NetworkManager.LogWarning($"SyncType not found for index {syncTypeId} on {transform.name}, component {GetType().FullName}. Remainder of packet may become corrupt.");
}
diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.Prediction.cs b/Assets/FishNet/Runtime/Object/NetworkObject.Prediction.cs
index fac02bcf..ec01529e 100644
--- a/Assets/FishNet/Runtime/Object/NetworkObject.Prediction.cs
+++ b/Assets/FishNet/Runtime/Object/NetworkObject.Prediction.cs
@@ -1,10 +1,13 @@
-using FishNet.Component.Prediction;
+using System;
+using FishNet.Component.Prediction;
using FishNet.Component.Transforming;
using FishNet.Managing;
using FishNet.Managing.Timing;
using FishNet.Object.Prediction;
using GameKit.Dependencies.Utilities;
using System.Collections.Generic;
+using FishNet.Connection;
+using FishNet.Managing.Server;
using UnityEngine;
namespace FishNet.Object
@@ -235,7 +238,6 @@ private void ChangePredictionSubscriptions(bool subscribe, NetworkManager manage
}
}
-
///
/// Initializes tick smoothing.
///
@@ -287,7 +289,6 @@ private void DeinitializeSmoothers()
}
}
-
private void InvokeStartCallbacks_Prediction(bool asServer)
{
if (_predictionBehaviours.Count == 0)
@@ -338,19 +339,25 @@ private void PredictionManager_OnPreReconcile(uint clientTick, uint serverTick)
PredictionSmoother.OnPreReconcile();
}
-
private void PredictionManager_OnReconcile(uint clientReconcileTick, uint serverReconcileTick)
{
+ /* If still not reconciling then pause rigidbody.
+ * This shouldn't happen unless the user is not calling
+ * reconcile at all. */
if (!IsObjectReconciling)
{
if (_rigidbodyPauser != null)
_rigidbodyPauser.Pause();
+
+ return;
}
- else
- {
- for (int i = 0; i < _predictionBehaviours.Count; i++)
- _predictionBehaviours[i].Reconcile_Client_Start();
- }
+
+ /* Tell all prediction behaviours to set/validate their
+ * reconcile data now. This will use reconciles from the server
+ * whenever possible, and local reconciles if a server reconcile
+ * is not available. */
+ for (int i = 0; i < _predictionBehaviours.Count; i++)
+ _predictionBehaviours[i].Reconcile_Client_Start();
}
private void PredictionManager_OnPostReconcile(uint clientReconcileTick, uint serverReconcileTick)
@@ -367,7 +374,6 @@ private void PredictionManager_OnPostReconcile(uint clientReconcileTick, uint se
IsObjectReconciling = false;
}
-
private void PredictionManager_OnReplicateReplay(uint clientTick, uint serverTick)
{
uint replayTick = (IsOwner) ? clientTick : serverTick;
@@ -409,4 +415,47 @@ internal void SetReplicateTick(uint value, bool createdReplicate)
///
private void ResetState_Prediction(bool asServer) { }
}
+
+ ///
+ /// Place this component on your NetworkManager object to remove ownership of objects for a disconnecting client.
+ /// This prevents any owned object from being despawned when the owner disconnects.
+ ///
+ public class GlobalPreserveOwnedObjects : MonoBehaviour
+ {
+ private void Awake()
+ {
+ ServerManager sm = GetComponent();
+ sm.Objects.OnPreDestroyClientObjects += Objects_OnPreDestroyClientObjects;
+ }
+
+ protected virtual void Objects_OnPreDestroyClientObjects(NetworkConnection conn)
+ {
+ foreach (NetworkObject networkObject in conn.Objects)
+ networkObject.RemoveOwnership();
+ }
+ }
+
+ ///
+ /// Place this component on NetworkObjects you wish to remove ownership on for a disconnecting owner.
+ /// This prevents the object from being despawned when the owner disconnects.
+ ///
+ public class NetworkPreserveOwnedObjects : NetworkBehaviour
+ {
+ public override void OnStartServer()
+ {
+ ServerManager.Objects.OnPreDestroyClientObjects += OnPreDestroyClientObjects;
+ }
+
+ public override void OnStopServer()
+ {
+ if (ServerManager != null)
+ ServerManager.Objects.OnPreDestroyClientObjects -= OnPreDestroyClientObjects;
+ }
+
+ private void OnPreDestroyClientObjects(NetworkConnection conn)
+ {
+ if (conn == Owner)
+ RemoveOwnership();
+ }
+ }
}
\ No newline at end of file
diff --git a/Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs b/Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs
new file mode 100644
index 00000000..f48369b2
--- /dev/null
+++ b/Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs
@@ -0,0 +1,46 @@
+using FishNet.Documenting;
+using FishNet.Serializing;
+
+namespace FishNet.Object.Prediction
+{
+ ///
+ /// Used to store reconciles locally.
+ ///
+ /// This is for internal use only.
+ [APIExclude]
+ public struct LocalReconcile where T : IReconcileData
+ {
+ ///
+ /// Tick for reconcile.
+ ///
+ public uint Tick;
+ ///
+ /// Writer reconcile was written to.
+ ///
+ public PooledWriter Writer;
+ ///
+ /// Data inside writer.
+ ///
+ public T Data;
+
+ public void Initialize(uint tick, T data)
+ {
+ Tick = tick;
+ Data = data;
+ Writer = WriterPool.Retrieve();
+ Writer.Write(data);
+ }
+
+ ///
+ /// Disposes of used data.
+ ///
+ public void Dispose()
+ {
+ Data.Dispose();
+ if (Writer != null)
+ WriterPool.Store(Writer);
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs.meta b/Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs.meta
new file mode 100644
index 00000000..a4eaa212
--- /dev/null
+++ b/Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: eb90cfdc07524be40a9d4d11ae7c35e6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs
index 23d4c423..24cf84c1 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs
@@ -239,8 +239,6 @@ internal protected override void OnStartCallback(bool asServer)
[APIExclude]
internal protected override void WriteDelta(PooledWriter writer, bool resetSyncTick = true)
{
- base.WriteDelta(writer, resetSyncTick);
-
//If sending all then clear changed and write full.
if (_sendAll)
{
@@ -250,10 +248,11 @@ internal protected override void WriteDelta(PooledWriter writer, bool resetSyncT
}
else
{
+ base.WriteDelta(writer, resetSyncTick);
+
//False for not full write.
writer.WriteBoolean(false);
- base.WriteChangeId(writer, false);
-
+
writer.WriteInt32(_changed.Count);
for (int i = 0; i < _changed.Count; i++)
@@ -276,7 +275,7 @@ internal protected override void WriteDelta(PooledWriter writer, bool resetSyncT
_changed.Clear();
}
}
-
+
///
/// Writers all values if not initial values.
/// Internal use.
@@ -290,9 +289,9 @@ internal protected override void WriteFull(PooledWriter writer)
return;
base.WriteHeader(writer, false);
+
//True for full write.
writer.WriteBoolean(true);
- base.WriteChangeId(writer, true);
writer.WriteInt32(Collection.Count);
foreach (KeyValuePair item in Collection)
@@ -309,24 +308,19 @@ internal protected override void WriteFull(PooledWriter writer)
[APIExclude]
internal protected override void Read(PooledReader reader, bool asServer)
{
- /* When !asServer don't make changes if server is running.
- * This is because changes would have already been made on
- * the server side and doing so again would result in duplicates
- * and potentially overwrite data not yet sent. */
- bool asClientAndHost = (!asServer && base.NetworkBehaviour.IsServerStarted);
+ base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues);
+
//True to warn if this object was deinitialized on the server.
- bool deinitialized = (asClientAndHost && !base.OnStartServerCalled);
+ bool deinitialized = (asClientHost && !base.OnStartServerCalled);
if (deinitialized)
base.NetworkManager.LogWarning($"SyncType {GetType().Name} received a Read but was deinitialized on the server. Client callback values may be incorrect. This is a ClientHost limitation.");
IDictionary collection = Collection;
bool fullWrite = reader.ReadBoolean();
- bool ignoreReadChanges = base.ReadChangeId(reader);
- bool canModifyCollection = (!asClientAndHost && !ignoreReadChanges);
//Clear collection since it's a full write.
- if (canModifyCollection && fullWrite)
+ if (canModifyValues && fullWrite)
collection.Clear();
int changes = reader.ReadInt32();
@@ -344,29 +338,31 @@ internal protected override void Read(PooledReader reader, bool asServer)
{
key = reader.Read();
value = reader.Read();
- if (canModifyCollection)
+
+ if (canModifyValues)
collection[key] = value;
}
//Clear.
else if (operation == SyncDictionaryOperation.Clear)
{
- if (canModifyCollection)
+ if (canModifyValues)
collection.Clear();
}
//Remove.
else if (operation == SyncDictionaryOperation.Remove)
{
key = reader.Read();
- if (canModifyCollection)
+
+ if (canModifyValues)
collection.Remove(key);
}
- if (!ignoreReadChanges)
+ if (newChangeId)
InvokeOnChange(operation, key, value, false);
}
//If changes were made invoke complete after all have been read.
- if (!ignoreReadChanges && changes > 0)
+ if (newChangeId && changes > 0)
InvokeOnChange(SyncDictionaryOperation.Complete, default, default, false);
}
@@ -567,7 +563,7 @@ public void DirtyAll()
{
if (!base.IsInitialized)
return;
- if (!base.CanNetworkSetValues(true))
+ if (!base.CanNetworkSetValues(log: true))
return;
if (base.Dirty())
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs
index 47deea01..4c55f710 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs
@@ -226,10 +226,10 @@ internal protected override void WriteDelta(PooledWriter writer, bool resetSyncT
else
{
base.WriteDelta(writer, resetSyncTick);
+
//False for not full write.
writer.WriteBoolean(false);
- base.WriteChangeId(writer, false);
- //
+
writer.WriteInt32(_changed.Count);
for (int i = 0; i < _changed.Count; i++)
@@ -260,7 +260,6 @@ internal protected override void WriteFull(PooledWriter writer)
base.WriteHeader(writer, false);
//True for full write.
writer.WriteBoolean(true);
- base.WriteChangeId(writer, true);
int count = Collection.Count;
writer.WriteInt32(count);
@@ -277,24 +276,19 @@ internal protected override void WriteFull(PooledWriter writer)
[APIExclude]
internal protected override void Read(PooledReader reader, bool asServer)
{
- /* When !asServer don't make changes if server is running.
- * This is because changes would have already been made on
- * the server side and doing so again would result in duplicates
- * and potentially overwrite data not yet sent. */
- bool asClientAndHost = (!asServer && base.NetworkManager.IsServerStarted);
+ base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues);
+
//True to warn if this object was deinitialized on the server.
- bool deinitialized = (asClientAndHost && !base.OnStartServerCalled);
+ bool deinitialized = (asClientHost && !base.OnStartServerCalled);
if (deinitialized)
base.NetworkManager.LogWarning($"SyncType {GetType().Name} received a Read but was deinitialized on the server. Client callback values may be incorrect. This is a ClientHost limitation.");
ISet collection = Collection;
bool fullWrite = reader.ReadBoolean();
- bool ignoreReadChanges = base.ReadChangeId(reader);
- bool canModifyCollection = (!asClientAndHost && !ignoreReadChanges);
//Clear collection since it's a full write.
- if (canModifyCollection && fullWrite)
+ if (canModifyValues && fullWrite)
collection.Clear();
int changes = reader.ReadInt32();
@@ -307,39 +301,42 @@ internal protected override void Read(PooledReader reader, bool asServer)
if (operation == SyncHashSetOperation.Add)
{
next = reader.Read();
- if (canModifyCollection)
+
+ if (canModifyValues)
collection.Add(next);
}
//Clear.
else if (operation == SyncHashSetOperation.Clear)
{
- if (canModifyCollection)
+ if (canModifyValues)
collection.Clear();
}
//Remove.
else if (operation == SyncHashSetOperation.Remove)
{
next = reader.Read();
- if (canModifyCollection)
+
+ if (canModifyValues)
collection.Remove(next);
}
//Updated.
else if (operation == SyncHashSetOperation.Update)
{
next = reader.Read();
- if (canModifyCollection)
+
+ if (canModifyValues)
{
collection.Remove(next);
collection.Add(next);
}
}
- if (!ignoreReadChanges)
+ if (newChangeId)
InvokeOnChange(operation, next, false);
}
//If changes were made invoke complete after all have been read.
- if (!ignoreReadChanges && changes > 0)
+ if (newChangeId && changes > 0)
InvokeOnChange(SyncHashSetOperation.Complete, default, false);
}
@@ -468,7 +465,7 @@ public void DirtyAll()
{
if (!base.IsInitialized)
return;
- if (!base.CanNetworkSetValues(true))
+ if (!base.CanNetworkSetValues(log: true))
return;
if (base.Dirty())
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs
index 30b3fa1c..90634f2b 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs
@@ -255,9 +255,10 @@ internal protected override void WriteDelta(PooledWriter writer, bool resetSyncT
else
{
base.WriteDelta(writer, resetSyncTick);
+
//False for not full write.
writer.WriteBoolean(false);
- base.WriteChangeId(writer, false);
+
//Number of entries expected.
writer.WriteInt32(_changed.Count);
@@ -298,7 +299,6 @@ internal protected override void WriteFull(PooledWriter writer)
base.WriteHeader(writer, false);
//True for full write.
writer.WriteBoolean(true);
- base.WriteChangeId(writer, true);
int count = Collection.Count;
writer.WriteInt32(count);
@@ -316,24 +316,18 @@ internal protected override void WriteFull(PooledWriter writer)
[APIExclude]
internal protected override void Read(PooledReader reader, bool asServer)
{
- /* When !asServer don't make changes if server is running.
- * This is because changes would have already been made on
- * the server side and doing so again would result in duplicates
- * and potentially overwrite data not yet sent. */
- bool asClientAndHost = (!asServer && base.NetworkManager.IsServerStarted);
+ base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues);
+
//True to warn if this object was deinitialized on the server.
- bool deinitialized = (asClientAndHost && !base.OnStartServerCalled);
+ bool deinitialized = (asClientHost && !base.OnStartServerCalled);
if (deinitialized)
base.NetworkManager.LogWarning($"SyncType {GetType().Name} received a Read but was deinitialized on the server. Client callback values may be incorrect. This is a ClientHost limitation.");
List collection = Collection;
bool fullWrite = reader.ReadBoolean();
- bool ignoreReadChanges = base.ReadChangeId(reader);
- bool canModifyCollection = (!asClientAndHost && !ignoreReadChanges);
-
//Clear collection since it's a full write.
- if (canModifyCollection && fullWrite)
+ if (canModifyValues && fullWrite)
collection.Clear();
int changes = reader.ReadInt32();
@@ -349,7 +343,8 @@ internal protected override void Read(PooledReader reader, bool asServer)
if (operation == SyncListOperation.Add)
{
next = reader.Read();
- if (canModifyCollection)
+
+ if (canModifyValues)
{
index = collection.Count;
collection.Add(next);
@@ -358,7 +353,7 @@ internal protected override void Read(PooledReader reader, bool asServer)
//Clear.
else if (operation == SyncListOperation.Clear)
{
- if (canModifyCollection)
+ if (canModifyValues)
collection.Clear();
}
//Insert.
@@ -366,14 +361,16 @@ internal protected override void Read(PooledReader reader, bool asServer)
{
index = reader.ReadInt32();
next = reader.Read();
- if (canModifyCollection)
+
+ if (canModifyValues)
collection.Insert(index, next);
}
//RemoveAt.
else if (operation == SyncListOperation.RemoveAt)
{
index = reader.ReadInt32();
- if (canModifyCollection)
+
+ if (canModifyValues)
{
prev = collection[index];
collection.RemoveAt(index);
@@ -384,19 +381,20 @@ internal protected override void Read(PooledReader reader, bool asServer)
{
index = reader.ReadInt32();
next = reader.Read();
- if (canModifyCollection)
+
+ if (canModifyValues)
{
prev = collection[index];
collection[index] = next;
}
}
- if (!ignoreReadChanges)
+ if (newChangeId)
InvokeOnChange(operation, index, prev, next, false);
}
//If changes were made invoke complete after all have been read.
- if (!ignoreReadChanges && changes > 0)
+ if (newChangeId && changes > 0)
InvokeOnChange(SyncListOperation.Complete, -1, default, default, false);
}
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs
index b5ff9a0a..cdf2f1c7 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs
@@ -8,13 +8,11 @@
namespace FishNet.Object.Synchronizing
{
-
///
/// A SyncObject to efficiently synchronize Stopwatchs over the network.
///
public class SyncStopwatch : SyncBase, ICustomSync
{
-
#region Type.
///
/// Information about how the Stopwatch has changed.
@@ -40,6 +38,7 @@ public ChangeData(SyncStopwatchOperation operation, float previous)
/// Previous value of the Stopwatch. This will be -1f is the value is not available.
/// True if occurring on server.
public delegate void SyncTypeChanged(SyncStopwatchOperation op, float prev, bool asServer);
+
///
/// Called when a Stopwatch operation occurs.
///
@@ -248,10 +247,11 @@ private void WriteStartStopwatch(Writer w, float elapsed, bool includeOperationB
///
/// Reads and sets the current values for server or client.
///
-
[APIExclude]
internal protected override void Read(PooledReader reader, bool asServer)
{
+ base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues);
+
int changes = reader.ReadInt32();
for (int i = 0; i < changes; i++)
@@ -260,67 +260,71 @@ internal protected override void Read(PooledReader reader, bool asServer)
if (op == SyncStopwatchOperation.Start)
{
float elapsed = reader.ReadSingle();
- if (CanSetValues(asServer))
+
+ if (canModifyValues)
Elapsed = elapsed;
- InvokeOnChange(op, elapsed, asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, elapsed, asServer);
}
else if (op == SyncStopwatchOperation.Pause)
{
- if (CanSetValues(asServer))
+ if (canModifyValues)
Paused = true;
- InvokeOnChange(op, -1f, asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, -1f, asServer);
}
else if (op == SyncStopwatchOperation.PauseUpdated)
{
float prev = reader.ReadSingle();
- if (CanSetValues(asServer))
+
+ if (canModifyValues)
Paused = true;
- InvokeOnChange(op, prev, asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, prev, asServer);
}
else if (op == SyncStopwatchOperation.Unpause)
{
- if (CanSetValues(asServer))
+ if (canModifyValues)
Paused = false;
- InvokeOnChange(op, -1f, asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, -1f, asServer);
}
else if (op == SyncStopwatchOperation.Stop)
{
- StopStopwatch_Internal(asServer);
- InvokeOnChange(op, -1f, false);
+ if (canModifyValues)
+ StopStopwatch_Internal(asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, -1f, false);
}
else if (op == SyncStopwatchOperation.StopUpdated)
{
float prev = reader.ReadSingle();
- StopStopwatch_Internal(asServer);
- InvokeOnChange(op, prev, asServer);
+ if (canModifyValues)
+ StopStopwatch_Internal(asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, prev, asServer);
}
}
- if (changes > 0)
+ if (newChangeId && changes > 0)
InvokeOnChange(SyncStopwatchOperation.Complete, -1f, asServer);
}
- ///
- /// Returns if values can be updated.
- ///
- private bool CanSetValues(bool asServer)
- {
- return (asServer || !base.NetworkManager.IsServerStarted);
- }
-
///
/// Stops the Stopwatch and resets.
///
private void StopStopwatch_Internal(bool asServer)
{
- if (!CanSetValues(asServer))
- return;
-
Paused = false;
Elapsed = -1f;
}
-
///
/// Invokes OnChanged callback.
///
@@ -342,7 +346,6 @@ private void InvokeOnChange(SyncStopwatchOperation operation, float prev, bool a
}
}
-
///
/// Called after OnStartXXXX has occurred.
///
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs
index aad2ad63..f27bddf1 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs
@@ -266,20 +266,14 @@ private void WriteStartTimer(Writer w, bool includeOperationByte)
w.WriteSingle(Duration);
}
- ///
- /// Returns if values can be updated.
- ///
- private bool CanSetValues(bool asServer)
- {
- return (asServer || !base.NetworkManager.IsServerStarted);
- }
-
///
/// Reads and sets the current values for server or client.
///
[APIExclude]
internal protected override void Read(PooledReader reader, bool asServer)
{
+ base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues);
+
int changes = reader.ReadInt32();
for (int i = 0; i < changes; i++)
@@ -289,32 +283,43 @@ internal protected override void Read(PooledReader reader, bool asServer)
{
float next = reader.ReadSingle();
float duration = reader.ReadSingle();
- if (CanSetValues(asServer))
+
+ if (canModifyValues)
{
Paused = false;
Remaining = next;
Duration = duration;
}
-
- InvokeOnChange(op, -1f, next, asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, -1f, next, asServer);
}
else if (op == SyncTimerOperation.Pause || op == SyncTimerOperation.PauseUpdated || op == SyncTimerOperation.Unpause)
{
- UpdatePauseState(op);
+ if (canModifyValues)
+ UpdatePauseState(op);
}
else if (op == SyncTimerOperation.Stop)
{
float prev = Remaining;
- StopTimer_Internal(asServer);
- InvokeOnChange(op, prev, 0f, false);
+
+ if (canModifyValues)
+ StopTimer_Internal(asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, prev, 0f, false);
}
//
else if (op == SyncTimerOperation.StopUpdated)
{
float prev = Remaining;
float next = reader.ReadSingle();
- StopTimer_Internal(asServer);
- InvokeOnChange(op, prev, next, asServer);
+
+ if (canModifyValues)
+ StopTimer_Internal(asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, prev, next, asServer);
}
}
@@ -329,20 +334,19 @@ void UpdatePauseState(SyncTimerOperation op)
if (op == SyncTimerOperation.PauseUpdated)
{
next = reader.ReadSingle();
- if (CanSetValues(asServer))
- Remaining = next;
+ Remaining = next;
}
else
{
next = Remaining;
}
- if (CanSetValues(asServer))
- Paused = newPauseState;
- InvokeOnChange(op, prev, next, asServer);
+ Paused = newPauseState;
+ if (newChangeId)
+ InvokeOnChange(op, prev, next, asServer);
}
- if (changes > 0)
+ if (newChangeId && changes > 0)
InvokeOnChange(SyncTimerOperation.Complete, -1f, -1f, false);
}
@@ -351,9 +355,6 @@ void UpdatePauseState(SyncTimerOperation op)
///
private void StopTimer_Internal(bool asServer)
{
- if (!CanSetValues(asServer))
- return;
-
Paused = false;
Remaining = 0f;
}
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs
index 4d69cef6..ec2a2f3d 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs
@@ -12,7 +12,6 @@
namespace FishNet.Object.Synchronizing
{
-
internal interface ISyncVar { }
[APIExclude]
@@ -114,6 +113,7 @@ public T Value
/// Called when the SyncDictionary changes.
///
public event OnChanged OnChange;
+
public delegate void OnChanged(T prev, T next, bool asServer);
#endregion
@@ -178,6 +178,7 @@ public void SetInitialValues(T value)
if (base.IsInitialized)
_valueSetAfterInitialized = true;
}
+
///
/// Sets current and previous values.
///
@@ -190,11 +191,11 @@ private void UpdateValues(T next)
_value = next;
}
+
///
/// Sets current value and marks the SyncVar dirty when able to. Returns if able to set value.
///
/// True if SetValue was called in response to user code. False if from automated code.
-
internal void SetValue(T nextValue, bool calledByUser, bool sendRpc = false)
{
/* IsInitialized is only set after the script containing this SyncVar
@@ -227,7 +228,7 @@ internal void SetValue(T nextValue, bool calledByUser, bool sendRpc = false)
if (!base.CanNetworkSetValues(true))
return;
/* We will only be this far if the network is not active yet,
- * server is active, or client has setting permissions.
+ * server is active, or client has setting permissions.
* We only need to set asServerInvoke to false if the network
* is initialized and the server is not active. */
bool asServerInvoke = CanInvokeCallbackAsServer();
@@ -240,7 +241,7 @@ internal void SetValue(T nextValue, bool calledByUser, bool sendRpc = false)
T prev = _value;
UpdateValues(nextValue);
//Still call invoke because change will be cached for when the network initializes.
- InvokeOnChange(prev, _value, calledByUser);
+ InvokeOnChange(prev, _value, asServer: true);
}
else
{
@@ -257,13 +258,27 @@ internal void SetValue(T nextValue, bool calledByUser, bool sendRpc = false)
//Not called by user.
else
{
- /* Previously clients were not allowed to set values
- * but this has been changed because clients may want
- * to update values locally while occasionally
- * letting the syncvar adjust their side. */
- T prev = _value;
- if (Comparers.EqualityCompare(prev, nextValue))
- return;
+ /* Only perform the equality checks when not host.
+ *
+ * In the previous SyncVar version it was okay to call
+ * this on host because a separate clientHost value was kept for
+ * the client side, and that was compared against.
+ *
+ * In newer SyncVar(this one) a client side copy is
+ * not kept so when compariing against the current vlaue
+ * as clientHost, it will always return as matched.
+ *
+ * But it's impossible for clientHost to send a value
+ * and it not have changed, so this check is not needed. */
+
+ // /* Previously clients were not allowed to set values
+ // * but this has been changed because clients may want
+ // * to update values locally while occasionally
+ // * letting the syncvar adjust their side. */
+ // T prev = _value;
+ // if (Comparers.EqualityCompare(prev, nextValue))
+ // return;
+
/* If also server do not update value.
* Server side has say of the current value. */
/* Only update value if not server. We do not want
@@ -272,7 +287,9 @@ internal void SetValue(T nextValue, bool calledByUser, bool sendRpc = false)
if (!base.NetworkManager.IsServerStarted)
UpdateValues(nextValue);
- InvokeOnChange(prev, nextValue, calledByUser);
+ T prev = _value;
+
+ InvokeOnChange(prev, nextValue, asServer: false);
}
@@ -353,7 +370,6 @@ private void InvokeOnChange(T prev, T next, bool asServer)
/// Called after OnStartXXXX has occurred.
///
/// True if OnStartServer was called, false if OnStartClient.
-
[MakePublic]
internal protected override void OnStartCallback(bool asServer)
{
@@ -391,7 +407,7 @@ internal protected override void WriteFull(PooledWriter obj0)
{
/* If a class then skip comparer check.
* InitialValue and Value will be the same reference.
- *
+ *
* If a value then compare field changes, since the references
* will not be the same. */
//Compare if a value type.
@@ -416,9 +432,22 @@ internal protected override void WriteFull(PooledWriter obj0)
protected internal override void Read(PooledReader reader, bool asServer)
{
T value = reader.Read();
+
+ if (!ReadChangeId(reader))
+ return;
+
SetValue(value, false);
+ //TODO this needs to separate invokes from setting values so that syncvar can be written like remainder of synctypes.
}
+ //SyncVars do not use changeId.
+ [APIExclude]
+ protected override bool ReadChangeId(Reader reader) => true;
+
+ //SyncVars do not use changeId.
+ [APIExclude]
+ protected override void WriteChangeId(PooledWriter writer) { }
+
///
/// Resets to initialized values.
///
@@ -430,8 +459,7 @@ protected internal override void ResetState(bool asServer)
* asServer is true.
* Is not network initialized.
* asServer is false, and server is not started. */
- if ((asServer && !base.NetworkManager.IsClientStarted) ||
- (!asServer && base.NetworkBehaviour.IsDeinitializing))
+ if ((asServer && !base.NetworkManager.IsClientStarted) || (!asServer && base.NetworkBehaviour.IsDeinitializing))
{
_value = _initialValue;
_valueSetAfterInitialized = false;
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs
index 5ab7a105..9c46cf51 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs
@@ -1,4 +1,5 @@
-using FishNet.CodeGenerating;
+using System;
+using FishNet.CodeGenerating;
using FishNet.Managing;
using FishNet.Managing.Timing;
using FishNet.Serializing;
@@ -41,13 +42,11 @@ public class SyncBase
///
/// NetworkManager this uses.
///
- [MakePublic]
- internal NetworkManager NetworkManager = null;
+ public NetworkManager NetworkManager = null;
///
/// NetworkBehaviour this SyncVar belongs to.
///
- [MakePublic]
- internal NetworkBehaviour NetworkBehaviour = null;
+ public NetworkBehaviour NetworkBehaviour = null;
///
/// True if the server side has initialized this SyncType.
///
@@ -70,13 +69,14 @@ public class SyncBase
/// Channel to send on.
///
internal Channel Channel => _currentChannel;
+
///
/// Sets a new currentChannel.
///
///
internal void SetCurrentChannel(Channel channel) => _currentChannel = channel;
#endregion
-
+
#region Private.
///
/// Sync interval converted to ticks.
@@ -87,30 +87,29 @@ public class SyncBase
///
private Channel _currentChannel;
///
- /// Last localTick when full data was written.
- ///
- protected uint _lastWriteFullLocalTick;
- ///
- /// Id of the current change since the last full write.
- /// This is used to prevent duplicates caused by deltas writing after full writes when clients already received the delta in the full write, such as a spawn message.
+ /// Last changerId read from sender.
///
- protected uint _changeId;
+ private ushort _lastReadChangeId = UNSET_CHANGE_ID;
///
- /// Last changeId read.
+ /// Last changeId that was sent to receivers.
///
- private long _lastReadDirtyId = DEFAULT_LAST_READ_DIRTYID;
+ private ushort _lastWrittenChangeId = UNSET_CHANGE_ID;
#endregion
- #region Const.
+ #region Consts.
+ ///
+ /// Value to use when readId is unset.
+ ///
+ private const ushort UNSET_CHANGE_ID = 0;
///
- /// Default value for LastReadDirtyId.
+ /// Maximum value readId can be before resetting to the beginning.
///
- private const long DEFAULT_LAST_READ_DIRTYID = -1;
+ private const ushort MAXIMUM_CHANGE_ID = ushort.MaxValue;
#endregion
-
#region Constructors
public SyncBase() : this(new()) { }
+
public SyncBase(SyncTypeSettings settings)
{
Settings = settings;
@@ -125,32 +124,35 @@ public void UpdateSettings(SyncTypeSettings settings)
Settings = settings;
SetTimeToTicks();
}
+
///
/// Updates settings with new values.
///
-
public void UpdatePermissions(WritePermission writePermissions, ReadPermission readPermissions)
{
UpdatePermissions(writePermissions);
UpdatePermissions(readPermissions);
}
+
///
/// Updates settings with new values.
///
public void UpdatePermissions(WritePermission writePermissions) => Settings.WritePermission = writePermissions;
+
///
/// Updates settings with new values.
///
public void UpdatePermissions(ReadPermission readPermissions) => Settings.ReadPermission = readPermissions;
+
///
/// Updates settings with new values.
///
-
public void UpdateSendRate(float sendRate)
{
Settings.SendRate = sendRate;
SetTimeToTicks();
}
+
///
/// Updates settings with new values.
///
@@ -159,6 +161,7 @@ public void UpdateSettings(Channel channel)
CheckChannel(ref channel);
_currentChannel = channel;
}
+
///
/// Updates settings with new values.
///
@@ -187,7 +190,6 @@ private void CheckChannel(ref Channel c)
///
/// Initializes this SyncBase before user Awake code.
///
-
[MakePublic]
internal void InitializeEarly(NetworkBehaviour nb, uint syncIndex, bool isSyncObject)
{
@@ -201,7 +203,6 @@ internal void InitializeEarly(NetworkBehaviour nb, uint syncIndex, bool isSyncOb
///
/// Called during InitializeLate in NetworkBehaviours to indicate user Awake code has executed.
///
-
[MakePublic]
internal void InitializeLate()
{
@@ -255,6 +256,7 @@ internal protected virtual void OnStartCallback(bool asServer)
else
OnStartClientCalled = true;
}
+
///
/// Called before OnStopXXXX has occurred for the NetworkBehaviour.
///
@@ -271,9 +273,9 @@ internal protected virtual void OnStopCallback(bool asServer)
///
/// True if can set values and send them over the network.
///
- ///
+ ///
///
- protected bool CanNetworkSetValues(bool warn = true)
+ protected bool CanNetworkSetValues(bool log = true)
{
/* If not registered then values can be set
* since at this point the object is still being initialized
@@ -294,7 +296,7 @@ protected bool CanNetworkSetValues(bool warn = true)
/* If here then server is not active and additional
* checks must be performed. */
bool result = (Settings.WritePermission == WritePermission.ClientUnsynchronized) || (Settings.ReadPermission == ReadPermission.ExcludeOwner && NetworkBehaviour.IsOwner);
- if (!result && warn)
+ if (!result && log)
LogServerNotActiveWarning();
return result;
@@ -313,7 +315,7 @@ protected void LogServerNotActiveWarning()
/// Dirties this Sync and the NetworkBehaviour.
///
/// True to send current dirtied values immediately as a RPC. When this occurs values will arrive in the order they are sent and interval is ignored.
- protected bool Dirty()//bool sendRpc = false)
+ protected bool Dirty() //bool sendRpc = false)
{
//if (sendRpc)
// NextSyncTick = 0;
@@ -326,9 +328,6 @@ protected bool Dirty()//bool sendRpc = false)
* processed. This ensures that data
* is flushed. */
bool canDirty = NetworkBehaviour.DirtySyncType();
- //If first time dirtying increase dirtyId.
- if (IsDirty != canDirty)
- _changeId++;
IsDirty |= canDirty;
return canDirty;
@@ -342,33 +341,88 @@ protected bool Dirty()//bool sendRpc = false)
protected bool CanInvokeCallbackAsServer() => (!IsNetworkInitialized || NetworkBehaviour.IsServerStarted);
///
- /// Reads the change Id and returns if changes should be ignored.
+ /// Reads a change Id and returns true if the change is new.
///
- ///
- protected bool ReadChangeId(PooledReader reader)
+ /// This method is currently under evaluation and may change at any time.
+ protected virtual bool ReadChangeId(Reader reader)
{
- bool reset = reader.ReadBoolean();
- uint id = reader.ReadUInt32();
- bool ignoreResults = !reset && (id <= _lastReadDirtyId);
+ if (NetworkManager == null)
+ {
+ NetworkManager.LogWarning($"NetworkManager is unexpectedly null during a SyncType read.");
+ return false;
+ }
+
+ bool rolledOver = reader.ReadBoolean();
+ ushort id = reader.ReadUInt16();
+
+ //Only check lastReadId if its not unset.
+ if (_lastReadChangeId != UNSET_CHANGE_ID)
+ {
+ /* If not rolledOver then Id should always be larger
+ * than the last read. If it's not then the data is
+ * old.
+ *
+ * If Id is smaller then rolledOver should be normal,
+ * as rolling over means to restart the Id from the lowest
+ * value. */
+ if (rolledOver)
+ {
+ if (id >= _lastReadChangeId)
+ return false;
+ }
+ else
+ {
+ if (id <= _lastReadChangeId)
+ return false;
+ }
+ }
- _lastReadDirtyId = id;
- return ignoreResults;
+ _lastReadChangeId = id;
+ return true;
}
+
+
+ ///
+ /// Writes the readId for a change.
+ ///
+ /// This method is currently under evaluation and may change at any time.
+ protected virtual void WriteChangeId(PooledWriter writer)
+ {
+ bool rollOver;
+ if (_lastWrittenChangeId >= MAXIMUM_CHANGE_ID)
+ {
+ rollOver = true;
+ _lastWrittenChangeId = UNSET_CHANGE_ID;
+ }
+ else
+ {
+ rollOver = false;
+ }
+
+ _lastWrittenChangeId++;
+ writer.WriteBoolean(rollOver);
+ writer.WriteUInt16(_lastWrittenChangeId);
+ }
+
+ ///
+ /// Returns true if values are being read as clientHost.
+ ///
+ /// True if reading as server.
+ /// This method is currently under evaluation and may change at any time.
+ protected bool IsReadAsClientHost(bool asServer) => (!asServer && NetworkManager.IsServerStarted);
///
- /// Writers the current ChangeId, and if it has been reset.
+ /// Outputs values which may be helpful on how to process a read operation.
///
- protected void WriteChangeId(PooledWriter writer, bool fullWrite)
+ /// True if the changeId read is not old data.
+ /// True if being read as clientHost.
+ /// True if can modify values from the read, typically when asServer or not asServer and not clientHost.
+ /// This method is currently under evaluation and may change at any time.
+ protected void SetReadArguments(PooledReader reader, bool asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues)
{
- /* Fullwrites do not reset the Id, only
- * delta changes do. */
- bool resetId = (!fullWrite && NetworkManager.TimeManager.LocalTick > _lastWriteFullLocalTick);
- writer.WriteBoolean(resetId);
- //If to reset Id then do so.
- if (resetId)
- _changeId = 0;
- //Write Id.
- writer.WriteUInt32(_changeId);
+ newChangeId = ReadChangeId(reader);
+ asClientHost = IsReadAsClientHost(asServer);
+ canModifyValues = (newChangeId && !asClientHost);
}
///
@@ -392,27 +446,27 @@ internal void ResetDirty()
IsDirty = false;
}
}
+
///
/// True if dirty and enough time has passed to write changes.
///
- ///
- ///
- internal bool SyncTimeMet(uint tick)
- {
- return (IsDirty && tick >= NextSyncTick);
- }
+ internal bool IsNextSyncTimeMet(uint tick) => (IsDirty && tick >= NextSyncTick);
+
+ [Obsolete("Use IsNextSyncTimeMet.")] //Remove on V5
+ internal bool SyncTimeMet(uint tick) => IsNextSyncTimeMet(tick);
+
///
/// Writes current value.
///
/// True to set the next time data may sync.
-
[MakePublic]
internal protected virtual void WriteDelta(PooledWriter writer, bool resetSyncTick = true)
{
WriteHeader(writer, resetSyncTick);
}
+
///
- /// Writers the header for this SyncType.
+ /// Writes the header for this SyncType.
///
protected virtual void WriteHeader(PooledWriter writer, bool resetSyncTick = true)
{
@@ -420,30 +474,28 @@ protected virtual void WriteHeader(PooledWriter writer, bool resetSyncTick = tru
NextSyncTick = NetworkManager.TimeManager.LocalTick + _timeToTicks;
writer.WriteUInt8Unpacked((byte)SyncIndex);
+ WriteChangeId(writer);
}
///
/// Indicates that a full write has occurred.
/// This is called from WriteFull, or can be called manually.
///
- protected void FullWritten()
- {
- _lastWriteFullLocalTick = NetworkManager.TimeManager.LocalTick;
- }
+ [Obsolete("This method no longer functions. You may remove it from your code.")] //Remove on V5.
+ protected void FullWritten() { }
+
///
/// Writes all values for the SyncType.
///
-
[MakePublic]
- internal protected virtual void WriteFull(PooledWriter writer)
- {
- FullWritten();
- }
+ internal protected virtual void WriteFull(PooledWriter writer) { }
+
///
/// Sets current value as server or client through deserialization.
///
[MakePublic]
internal protected virtual void Read(PooledReader reader, bool asServer) { }
+
///
/// Resets initialized values for server and client.
///
@@ -461,8 +513,6 @@ internal protected virtual void ResetState(bool asServer)
{
if (asServer)
{
- _lastWriteFullLocalTick = 0;
- _changeId = 0;
NextSyncTick = 0;
SetCurrentChannel(Settings.Channel);
IsDirty = false;
@@ -473,13 +523,11 @@ internal protected virtual void ResetState(bool asServer)
* that means the object is deinitializing, and won't have any
* client observers anyway. Because of this it's safe to reset
* with asServer true, or false.
- *
+ *
* This change is made to resolve a bug where asServer:false
* sometimes does not invoke when stopping clientHost while not
* also stopping play mode. */
- _lastReadDirtyId = DEFAULT_LAST_READ_DIRTYID;
+ _lastReadChangeId = UNSET_CHANGE_ID;
}
-
-
}
}
\ No newline at end of file
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs
index 5ea3dd6a..bd3c76dc 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs
@@ -15,7 +15,6 @@ namespace FishNet.Object.Synchronizing
[System.Serializable]
public class SyncDictionary : SyncBase, IDictionary, IReadOnlyDictionary
{
-
#region Types.
///
/// Information needed to invoke a callback.
@@ -58,6 +57,7 @@ public ChangeData(SyncDictionaryOperation operation, TKey key, TValue value)
///
[APIExclude]
public bool IsReadOnly => false;
+
///
/// Delegate signature for when SyncDictionary changes.
///
@@ -67,6 +67,7 @@ public ChangeData(SyncDictionaryOperation operation, TKey key, TValue value)
/// True if callback is on the server side. False is on the client side.
[APIExclude]
public delegate void SyncDictionaryChanged(SyncDictionaryOperation op, TKey key, TValue value, bool asServer);
+
///
/// Called when the SyncDictionary changes.
///
@@ -128,6 +129,7 @@ public ChangeData(SyncDictionaryOperation operation, TKey key, TValue value)
#region Constructors.
public SyncDictionary(SyncTypeSettings settings = new()) : this(CollectionCaches.RetrieveDictionary(), settings) { }
+
public SyncDictionary(Dictionary objects, SyncTypeSettings settings = new()) : base(settings)
{
Collection = objects;
@@ -195,18 +197,17 @@ protected override void Initialized()
///
///
[APIExclude]
-
private void AddOperation(SyncDictionaryOperation operation, TKey key, TValue value)
{
if (!base.IsInitialized)
return;
/* asServer might be true if the client is setting the value
- * through user code. Typically synctypes can only be set
- * by the server, that's why it is assumed asServer via user code.
- * However, when excluding owner for the synctype the client should
- * have permission to update the value locally for use with
- * prediction. */
+ * through user code. Typically synctypes can only be set
+ * by the server, that's why it is assumed asServer via user code.
+ * However, when excluding owner for the synctype the client should
+ * have permission to update the value locally for use with
+ * prediction. */
bool asServerInvoke = (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServerStarted);
if (asServerInvoke)
@@ -222,7 +223,6 @@ private void AddOperation(SyncDictionaryOperation operation, TKey key, TValue va
InvokeOnChange(operation, key, value, asServerInvoke);
}
-
///
/// Called after OnStartXXXX has occurred.
///
@@ -241,7 +241,6 @@ internal protected override void OnStartCallback(bool asServer)
collection.Clear();
}
-
///
/// Writes all changed values.
/// Internal use.
@@ -252,8 +251,6 @@ internal protected override void OnStartCallback(bool asServer)
[APIExclude]
internal protected override void WriteDelta(PooledWriter writer, bool resetSyncTick = true)
{
- base.WriteDelta(writer, resetSyncTick);
-
//If sending all then clear changed and write full.
if (_sendAll)
{
@@ -263,6 +260,8 @@ internal protected override void WriteDelta(PooledWriter writer, bool resetSyncT
}
else
{
+ base.WriteDelta(writer, resetSyncTick);
+
//False for not full write.
writer.WriteBoolean(false);
writer.WriteInt32(_changed.Count);
@@ -273,8 +272,7 @@ internal protected override void WriteDelta(PooledWriter writer, bool resetSyncT
writer.WriteUInt8Unpacked((byte)change.Operation);
//Clear does not need to write anymore data so it is not included in checks.
- if (change.Operation == SyncDictionaryOperation.Add ||
- change.Operation == SyncDictionaryOperation.Set)
+ if (change.Operation == SyncDictionaryOperation.Add || change.Operation == SyncDictionaryOperation.Set)
{
writer.Write(change.Key);
writer.Write(change.Value);
@@ -289,7 +287,6 @@ internal protected override void WriteDelta(PooledWriter writer, bool resetSyncT
}
}
-
///
/// Writers all values if not initial values.
/// Internal use.
@@ -314,25 +311,20 @@ internal protected override void WriteFull(PooledWriter writer)
}
}
-
///
/// Reads and sets the current values for server or client.
///
[APIExclude]
-
internal protected override void Read(PooledReader reader, bool asServer)
{
- /* When !asServer don't make changes if server is running.
- * This is because changes would have already been made on
- * the server side and doing so again would result in duplicates
- * and potentially overwrite data not yet sent. */
- bool asClientAndHost = (!asServer && base.NetworkBehaviour.IsServerStarted);
+ base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues);
+
//True to warn if this object was deinitialized on the server.
- bool deinitialized = (asClientAndHost && !base.OnStartServerCalled);
+ bool deinitialized = (asClientHost && !base.OnStartServerCalled);
if (deinitialized)
base.NetworkManager.LogWarning($"SyncType {GetType().Name} received a Read but was deinitialized on the server. Client callback values may be incorrect. This is a ClientHost limitation.");
- IDictionary collection = (asClientAndHost) ? ClientHostCollection : Collection;
+ IDictionary collection = (asClientHost) ? ClientHostCollection : Collection;
//Clear collection since it's a full write.
bool fullWrite = reader.ReadBoolean();
@@ -354,6 +346,7 @@ internal protected override void Read(PooledReader reader, bool asServer)
{
key = reader.Read();
value = reader.Read();
+
if (!deinitialized)
collection[key] = value;
}
@@ -367,19 +360,20 @@ internal protected override void Read(PooledReader reader, bool asServer)
else if (operation == SyncDictionaryOperation.Remove)
{
key = reader.Read();
+
if (!deinitialized)
collection.Remove(key);
}
- InvokeOnChange(operation, key, value, false);
+ if (newChangeId)
+ InvokeOnChange(operation, key, value, false);
}
//If changes were made invoke complete after all have been read.
- if (changes > 0)
+ if (newChangeId && changes > 0)
InvokeOnChange(SyncDictionaryOperation.Complete, default, default, false);
}
-
///
/// Invokes OnChanged callback.
///
@@ -401,7 +395,6 @@ private void InvokeOnChange(SyncDictionaryOperation operation, TKey key, TValue
}
}
-
///
/// Resets to initialized values.
///
@@ -422,7 +415,6 @@ internal protected override void ResetState(bool asServer)
}
}
-
///
/// Adds item.
///
@@ -431,6 +423,7 @@ public void Add(KeyValuePair item)
{
Add(item.Key, item.Value);
}
+
///
/// Adds key and value.
///
@@ -440,6 +433,7 @@ public void Add(TKey key, TValue value)
{
Add(key, value, true);
}
+
private void Add(TKey key, TValue value, bool asServer)
{
if (!base.CanNetworkSetValues(true))
@@ -457,6 +451,7 @@ public void Clear()
{
Clear(true);
}
+
private void Clear(bool asServer)
{
if (!base.CanNetworkSetValues(true))
@@ -467,7 +462,6 @@ private void Clear(bool asServer)
AddOperation(SyncDictionaryOperation.Clear, default, default);
}
-
///
/// Returns if key exist.
///
@@ -477,12 +471,12 @@ public bool ContainsKey(TKey key)
{
return Collection.ContainsKey(key);
}
+
///
/// Returns if item exist.
///
/// Item to use.
/// True if found.
-
public bool Contains(KeyValuePair item)
{
return TryGetValue(item.Key, out TValue value) && EqualityComparer.Default.Equals(value, item.Value);
@@ -516,7 +510,6 @@ public void CopyTo([NotNull] KeyValuePair[] array, int offset)
}
}
-
///
/// Removes a key.
///
@@ -536,7 +529,6 @@ public bool Remove(TKey key)
return false;
}
-
///
/// Removes an item.
///
@@ -553,7 +545,6 @@ public bool Remove(KeyValuePair item)
/// Key to use.
/// Variable to output to.
/// True if able to output value.
-
public bool TryGetValue(TKey key, out TValue value)
{
return Collection.TryGetValueIL2CPP(key, out value);
@@ -640,12 +631,12 @@ public bool Dirty(TValue value, EqualityComparer comparer = null)
///
///
public IEnumerator> GetEnumerator() => Collection.GetEnumerator();
+
///
/// Gets the IEnumerator for the collection.
///
///
IEnumerator IEnumerable.GetEnumerator() => Collection.GetEnumerator();
-
}
}
#endif
\ No newline at end of file
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSet.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSet.cs
index e28fede5..34087dcc 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSet.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSet.cs
@@ -53,6 +53,7 @@ public ChangeData(SyncHashSetOperation operation, T item)
///
[APIExclude]
public bool IsReadOnly => false;
+
///
/// Delegate signature for when SyncList changes.
///
@@ -61,6 +62,7 @@ public ChangeData(SyncHashSetOperation operation, T item)
/// True if callback is occuring on the server.
[APIExclude]
public delegate void SyncHashSetChanged(SyncHashSetOperation op, T item, bool asServer);
+
///
/// Called when the SyncList changes.
///
@@ -80,7 +82,7 @@ public ChangeData(SyncHashSetOperation operation, T item)
public int Count => Collection.Count;
#endregion
- #region Private.
+ #region Private.
///
/// ListCache for comparing.
///
@@ -120,6 +122,7 @@ public ChangeData(SyncHashSetOperation operation, T item)
#region Constructors.
public SyncHashSet(SyncTypeSettings settings = new()) : this(CollectionCaches.RetrieveHashSet(), EqualityComparer.Default, settings) { }
public SyncHashSet(IEqualityComparer comparer, SyncTypeSettings settings = new()) : this(CollectionCaches.RetrieveHashSet(), (comparer == null) ? EqualityComparer.Default : comparer, settings) { }
+
public SyncHashSet(HashSet collection, IEqualityComparer comparer = null, SyncTypeSettings settings = new()) : base(settings)
{
_comparer = (comparer == null) ? EqualityComparer.Default : comparer;
@@ -181,7 +184,6 @@ public HashSet GetCollection(bool asServer)
///
/// Adds an operation and invokes locally.
///
-
private void AddOperation(SyncHashSetOperation operation, T item)
{
if (!base.IsInitialized)
@@ -236,6 +238,7 @@ internal protected override void WriteDelta(PooledWriter writer, bool resetSyncT
else
{
base.WriteDelta(writer, resetSyncTick);
+
//False for not full write.
writer.WriteBoolean(false);
writer.WriteInt32(_changed.Count);
@@ -280,21 +283,17 @@ internal protected override void WriteFull(PooledWriter writer)
///
/// Reads and sets the current values for server or client.
///
-
[APIExclude]
internal protected override void Read(PooledReader reader, bool asServer)
{
- /* When !asServer don't make changes if server is running.
- * This is because changes would have already been made on
- * the server side and doing so again would result in duplicates
- * and potentially overwrite data not yet sent. */
- bool asClientAndHost = (!asServer && base.NetworkManager.IsServerStarted);
+ base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues);
+
//True to warn if this object was deinitialized on the server.
- bool deinitialized = (asClientAndHost && !base.OnStartServerCalled);
+ bool deinitialized = (asClientHost && !base.OnStartServerCalled);
if (deinitialized)
base.NetworkManager.LogWarning($"SyncType {GetType().Name} received a Read but was deinitialized on the server. Client callback values may be incorrect. This is a ClientHost limitation.");
- ISet collection = (asClientAndHost) ? ClientHostCollection : Collection;
+ ISet collection = (asClientHost) ? ClientHostCollection : Collection;
//Clear collection since it's a full write.
bool fullWrite = reader.ReadBoolean();
@@ -338,11 +337,12 @@ internal protected override void Read(PooledReader reader, bool asServer)
}
}
- InvokeOnChange(operation, next, false);
+ if (newChangeId)
+ InvokeOnChange(operation, next, false);
}
//If changes were made invoke complete after all have been read.
- if (changes > 0)
+ if (newChangeId && changes > 0)
InvokeOnChange(SyncHashSetOperation.Complete, default, false);
}
@@ -393,6 +393,7 @@ public bool Add(T item)
{
return Add(item, true);
}
+
private bool Add(T item, bool asServer)
{
if (!base.CanNetworkSetValues(true))
@@ -409,6 +410,7 @@ private bool Add(T item, bool asServer)
return result;
}
+
///
/// Adds a range of values.
///
@@ -426,6 +428,7 @@ public void Clear()
{
Clear(true);
}
+
private void Clear(bool asServer)
{
if (!base.CanNetworkSetValues(true))
@@ -459,6 +462,7 @@ public bool Remove(T item)
{
return Remove(item, true);
}
+
private bool Remove(T item, bool asServer)
{
if (!base.CanNetworkSetValues(true))
@@ -520,8 +524,10 @@ public void Dirty(T obj)
///
///
public IEnumerator GetEnumerator() => Collection.GetEnumerator();
+
[APIExclude]
IEnumerator IEnumerable.GetEnumerator() => Collection.GetEnumerator();
+
[APIExclude]
IEnumerator IEnumerable.GetEnumerator() => Collection.GetEnumerator();
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs
index 8d541854..dcd48ee5 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs
@@ -268,9 +268,10 @@ internal protected override void WriteDelta(PooledWriter writer, bool resetSyncT
else
{
base.WriteDelta(writer, resetSyncTick);
+
//False for not full write.
writer.WriteBoolean(false);
- WriteChangeId(writer, false);
+
//Number of entries expected.
writer.WriteInt32(_changed.Count);
@@ -311,7 +312,6 @@ internal protected override void WriteFull(PooledWriter writer)
base.WriteHeader(writer, false);
//True for full write.
writer.WriteBoolean(true);
- WriteChangeId(writer, true);
int count = Collection.Count;
writer.WriteInt32(count);
@@ -329,24 +329,20 @@ internal protected override void WriteFull(PooledWriter writer)
[APIExclude]
internal protected override void Read(PooledReader reader, bool asServer)
{
- /* When !asServer don't make changes if server is running.
- * This is because changes would have already been made on
- * the server side and doing so again would result in duplicates
- * and potentially overwrite data not yet sent. */
- bool asClientAndHost = (!asServer && base.NetworkManager.IsServerStarted);
+ base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues);
+
//True to warn if this object was deinitialized on the server.
- bool deinitialized = (asClientAndHost && !base.OnStartServerCalled);
+ bool deinitialized = (asClientHost && !base.OnStartServerCalled);
if (deinitialized)
base.NetworkManager.LogWarning($"SyncType {GetType().Name} received a Read but was deinitialized on the server. Client callback values may be incorrect. This is a ClientHost limitation.");
- List collection = (asClientAndHost) ? ClientHostCollection : Collection;
+ List collection = (asClientHost) ? ClientHostCollection : Collection;
//Clear collection since it's a full write.
bool fullWrite = reader.ReadBoolean();
if (fullWrite)
collection.Clear();
- bool ignoreReadChanges = base.ReadChangeId(reader);
int changes = reader.ReadInt32();
for (int i = 0; i < changes; i++)
@@ -360,7 +356,8 @@ internal protected override void Read(PooledReader reader, bool asServer)
if (operation == SyncListOperation.Add)
{
next = reader.Read();
- if (!ignoreReadChanges)
+
+ if (newChangeId)
{
index = collection.Count;
collection.Add(next);
@@ -369,7 +366,7 @@ internal protected override void Read(PooledReader reader, bool asServer)
//Clear.
else if (operation == SyncListOperation.Clear)
{
- if (!ignoreReadChanges)
+ if (newChangeId)
collection.Clear();
}
//Insert.
@@ -377,14 +374,16 @@ internal protected override void Read(PooledReader reader, bool asServer)
{
index = reader.ReadInt32();
next = reader.Read();
- if (!ignoreReadChanges)
+
+ if (newChangeId)
collection.Insert(index, next);
}
//RemoveAt.
else if (operation == SyncListOperation.RemoveAt)
{
index = reader.ReadInt32();
- if (!ignoreReadChanges)
+
+ if (newChangeId)
{
prev = collection[index];
collection.RemoveAt(index);
@@ -395,19 +394,20 @@ internal protected override void Read(PooledReader reader, bool asServer)
{
index = reader.ReadInt32();
next = reader.Read();
- if (!ignoreReadChanges)
+
+ if (newChangeId)
{
prev = collection[index];
collection[index] = next;
}
}
- if (!ignoreReadChanges)
+ if (newChangeId)
InvokeOnChange(operation, index, prev, next, false);
}
//If changes were made invoke complete after all have been read.
- if (changes > 0)
+ if (newChangeId && changes > 0)
InvokeOnChange(SyncListOperation.Complete, -1, default, default, false);
}
diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatch.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs
similarity index 89%
rename from Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatch.cs
rename to Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs
index fedb1129..4c09be8c 100644
--- a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatch.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs
@@ -8,13 +8,11 @@
namespace FishNet.Object.Synchronizing
{
-
///
/// A SyncObject to efficiently synchronize Stopwatchs over the network.
///
public class SyncStopwatch : SyncBase, ICustomSync
{
-
#region Type.
///
/// Information about how the Stopwatch has changed.
@@ -40,6 +38,7 @@ public ChangeData(SyncStopwatchOperation operation, float previous)
/// Previous value of the Stopwatch. This will be -1f is the value is not available.
/// True if occurring on server.
public delegate void SyncTypeChanged(SyncStopwatchOperation op, float prev, bool asServer);
+
///
/// Called when a Stopwatch operation occurs.
///
@@ -248,10 +247,11 @@ private void WriteStartStopwatch(Writer w, float elapsed, bool includeOperationB
///
/// Reads and sets the current values for server or client.
///
-
[APIExclude]
internal protected override void Read(PooledReader reader, bool asServer)
{
+ base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues);
+
int changes = reader.ReadInt32();
for (int i = 0; i < changes; i++)
@@ -260,67 +260,72 @@ internal protected override void Read(PooledReader reader, bool asServer)
if (op == SyncStopwatchOperation.Start)
{
float elapsed = reader.ReadSingle();
- if (CanSetValues(asServer))
+
+ if (canModifyValues)
Elapsed = elapsed;
- InvokeOnChange(op, elapsed, asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, elapsed, asServer);
}
else if (op == SyncStopwatchOperation.Pause)
{
- if (CanSetValues(asServer))
+ if (canModifyValues)
Paused = true;
- InvokeOnChange(op, -1f, asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, -1f, asServer);
}
else if (op == SyncStopwatchOperation.PauseUpdated)
{
float prev = reader.ReadSingle();
- if (CanSetValues(asServer))
+
+ if (newChangeId)
Paused = true;
- InvokeOnChange(op, prev, asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, prev, asServer);
}
else if (op == SyncStopwatchOperation.Unpause)
{
- if (CanSetValues(asServer))
+ if (canModifyValues)
Paused = false;
- InvokeOnChange(op, -1f, asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, -1f, asServer);
}
else if (op == SyncStopwatchOperation.Stop)
{
- StopStopwatch_Internal(asServer);
- InvokeOnChange(op, -1f, false);
+ if (canModifyValues)
+ StopStopwatch_Internal(asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, -1f, false);
}
else if (op == SyncStopwatchOperation.StopUpdated)
{
float prev = reader.ReadSingle();
- StopStopwatch_Internal(asServer);
- InvokeOnChange(op, prev, asServer);
+
+ if (canModifyValues)
+ StopStopwatch_Internal(asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, prev, asServer);
}
}
- if (changes > 0)
+ if (newChangeId && changes > 0)
InvokeOnChange(SyncStopwatchOperation.Complete, -1f, asServer);
}
- ///
- /// Returns if values can be updated.
- ///
- private bool CanSetValues(bool asServer)
- {
- return (asServer || !base.NetworkManager.IsServerStarted);
- }
-
///
/// Stops the Stopwatch and resets.
///
private void StopStopwatch_Internal(bool asServer)
{
- if (!CanSetValues(asServer))
- return;
-
Paused = false;
Elapsed = -1f;
}
-
///
/// Invokes OnChanged callback.
///
@@ -342,7 +347,6 @@ private void InvokeOnChange(SyncStopwatchOperation operation, float prev, bool a
}
}
-
///
/// Called after OnStartXXXX has occurred.
///
diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatch.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs.meta
similarity index 100%
rename from Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatch.cs.meta
rename to Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs.meta
diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatchOperation.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatchOperation.cs
similarity index 100%
rename from Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatchOperation.cs
rename to Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatchOperation.cs
diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatchOperation.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatchOperation.cs.meta
similarity index 100%
rename from Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatchOperation.cs.meta
rename to Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatchOperation.cs.meta
diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimer.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs
similarity index 90%
rename from Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimer.cs
rename to Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs
index b3e88d5d..4c2f2bfa 100644
--- a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimer.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs
@@ -16,7 +16,6 @@ namespace FishNet.Object.Synchronizing
public class SyncTimer : SyncBase, ICustomSync
{
#region Type.
-
///
/// Information about how the timer has changed.
///
@@ -33,11 +32,9 @@ public ChangeData(SyncTimerOperation operation, float previous, float next)
Next = next;
}
}
-
#endregion
#region Public.
-
///
/// Delegate signature for when the timer operation occurs.
///
@@ -71,11 +68,9 @@ public ChangeData(SyncTimerOperation operation, float previous, float next)
/// True if the SyncTimer is currently paused. Calls to Update(float) will be ignored when paused.
///
public bool Paused { get; private set; }
-
#endregion
#region Private.
-
///
/// Changed data which will be sent next tick.
///
@@ -95,15 +90,10 @@ public ChangeData(SyncTimerOperation operation, float previous, float next)
/// Last Time.unscaledTime the timer delta was updated.
///
private float _updateTime;
-
#endregion
#region Constructors
-
- public SyncTimer(SyncTypeSettings settings = new()) : base(settings)
- {
- }
-
+ public SyncTimer(SyncTypeSettings settings = new()) : base(settings) { }
#endregion
///
@@ -183,11 +173,12 @@ public void StopTimer(bool sendRemaining = false)
{
if (Remaining <= 0f)
return;
- if (!base.CanNetworkSetValues(true))
+ if (!base.CanNetworkSetValues(log: true))
return;
bool asServer = true;
float prev = Remaining;
+
StopTimer_Internal(asServer);
SyncTimerOperation op = (sendRemaining) ? SyncTimerOperation.StopUpdated : SyncTimerOperation.Stop;
AddOperation(op, prev, 0f);
@@ -276,21 +267,14 @@ private void WriteStartTimer(Writer w, bool includeOperationByte)
w.WriteSingle(Duration);
}
- ///
- /// Returns if values can be updated.
- ///
- private bool CanSetValues(bool asServer)
- {
- return (asServer || !base.NetworkManager.IsServerStarted);
- }
-
///
/// Reads and sets the current values for server or client.
///
-
[APIExclude]
internal protected override void Read(PooledReader reader, bool asServer)
{
+ base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues);
+
int changes = reader.ReadInt32();
for (int i = 0; i < changes; i++)
@@ -300,33 +284,43 @@ internal protected override void Read(PooledReader reader, bool asServer)
{
float next = reader.ReadSingle();
float duration = reader.ReadSingle();
- if (CanSetValues(asServer))
+
+ if (canModifyValues)
{
Paused = false;
Remaining = next;
Duration = duration;
}
- InvokeOnChange(op, -1f, next, asServer);
+ if (newChangeId)
+ InvokeOnChange(op, -1f, next, asServer);
}
- else if (op == SyncTimerOperation.Pause || op == SyncTimerOperation.PauseUpdated
- || op == SyncTimerOperation.Unpause)
+ else if (op == SyncTimerOperation.Pause || op == SyncTimerOperation.PauseUpdated || op == SyncTimerOperation.Unpause)
{
- UpdatePauseState(op);
+ if (canModifyValues)
+ UpdatePauseState(op);
}
else if (op == SyncTimerOperation.Stop)
{
float prev = Remaining;
- StopTimer_Internal(asServer);
- InvokeOnChange(op, prev, 0f, false);
+
+ if (canModifyValues)
+ StopTimer_Internal(asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, prev, 0f, false);
}
//
else if (op == SyncTimerOperation.StopUpdated)
{
float prev = Remaining;
float next = reader.ReadSingle();
- StopTimer_Internal(asServer);
- InvokeOnChange(op, prev, next, asServer);
+
+ if (canModifyValues)
+ StopTimer_Internal(asServer);
+
+ if (newChangeId)
+ InvokeOnChange(op, prev, next, asServer);
}
}
@@ -341,20 +335,18 @@ void UpdatePauseState(SyncTimerOperation op)
if (op == SyncTimerOperation.PauseUpdated)
{
next = reader.ReadSingle();
- if (CanSetValues(asServer))
- Remaining = next;
+ Remaining = next;
}
else
{
next = Remaining;
}
- if (CanSetValues(asServer))
- Paused = newPauseState;
+ Paused = newPauseState;
InvokeOnChange(op, prev, next, asServer);
}
- if (changes > 0)
+ if (newChangeId && changes > 0)
InvokeOnChange(SyncTimerOperation.Complete, -1f, -1f, false);
}
@@ -363,14 +355,10 @@ void UpdatePauseState(SyncTimerOperation op)
///
private void StopTimer_Internal(bool asServer)
{
- if (!CanSetValues(asServer))
- return;
-
Paused = false;
Remaining = 0f;
}
-
///
/// Invokes OnChanged callback.
///
@@ -392,7 +380,6 @@ private void InvokeOnChange(SyncTimerOperation operation, float prev, float next
}
}
-
///
/// Called after OnStartXXXX has occurred.
///
@@ -422,20 +409,17 @@ private void SetUpdateTime()
///
/// Removes time passed from Remaining since the last unscaled time using this method.
///
-
public void Update()
{
float delta = (Time.unscaledTime - _updateTime);
Update(delta);
}
-
///
/// Removes delta from Remaining for server and client.
/// This also resets unscaledTime delta for Update().
///
/// Value to remove from Remaining.
-
public void Update(float delta)
{
//Not enabled.
diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimer.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs.meta
similarity index 100%
rename from Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimer.cs.meta
rename to Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs.meta
diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimerOperation.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs
similarity index 100%
rename from Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimerOperation.cs
rename to Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs
diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimerOperation.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs.meta
similarity index 100%
rename from Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimerOperation.cs.meta
rename to Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs.meta
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs
index ce67ce4a..819812a4 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs
@@ -423,8 +423,21 @@ internal protected override void WriteFull(PooledWriter obj0)
protected internal override void Read(PooledReader reader, bool asServer)
{
T value = reader.Read();
+
+ if (!ReadChangeId(reader))
+ return;
+
SetValue(value, false);
+ //TODO this needs to separate invokes from setting values so that syncvar can be written like remainder of synctypes.
}
+
+ //SyncVars do not use changeId.
+ [APIExclude]
+ protected override bool ReadChangeId(Reader reader) => true;
+
+ //SyncVars do not use changeId.
+ [APIExclude]
+ protected override void WriteChangeId(PooledWriter writer) { }
///
/// Resets to initialized values.
diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs
index 65b36be6..439a33f7 100644
--- a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs
+++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs
@@ -1,4 +1,5 @@
using System;
+using UnityEngine;
namespace GameKit.Dependencies.Utilities
{
@@ -82,7 +83,10 @@ public bool TryDequeue(out T result, bool defaultArrayEntry = true)
public T Dequeue(bool defaultArrayEntry = true)
{
if (_written == 0)
- throw new($"Queue of type {typeof(T).Name} is empty.");
+ {
+ Debug.LogError($"Queue of type {typeof(T).Name} is empty.");
+ return default;
+ }
T result = Collection[_read];
if (defaultArrayEntry)
diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs
index 620ff828..20eabaf3 100644
--- a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs
+++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs
@@ -3,13 +3,15 @@
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using UnityEngine;
+
namespace GameKit.Dependencies.Utilities.Types
{
///
/// Writes values to a collection of a set size, overwriting old values as needed.
///
- public class ResettableRingBuffer : IResettable where T : IResettable
+ public class ResettableRingBuffer : IResettable, IEnumerable where T : IResettable
{
#region Types.
///
@@ -30,7 +32,7 @@ public int ActualIndex
get
{
int total = (_startIndex + (_read - 1));
- int capacity = _rollingCollection.Capacity;
+ int capacity = _enumeratedRingBuffer.Capacity;
if (total >= capacity)
total -= capacity;
@@ -47,11 +49,15 @@ public int ActualIndex
///
/// RollingCollection to use.
///
- private ResettableRingBuffer _rollingCollection;
+ private ResettableRingBuffer _enumeratedRingBuffer;
///
/// Collection to iterate.
///
- private readonly T[] _collection;
+ private T[] _collection;
+ ///
+ /// Number of entries read during the enumeration.
+ ///
+ private int _entriesEnumerated;
///
/// Number of entries read during the enumeration.
///
@@ -60,78 +66,67 @@ public int ActualIndex
/// Start index of enumerations.
///
private int _startIndex;
+ ///
+ /// True if currently enumerating.
+ ///
+ private bool _enumerating => (_enumeratedRingBuffer != null);
+ ///
+ /// Count of the collection during initialization.
+ ///
+ private int _initializeCollectionCount;
#endregion
- public Enumerator(ResettableRingBuffer c)
+ public void Initialize(ResettableRingBuffer c)
{
- _read = 0;
- _startIndex = 0;
- _rollingCollection = c;
+ _entriesEnumerated = 0;
+ _startIndex = c.GetRealIndex(0);
+ _enumeratedRingBuffer = c;
_collection = c.Collection;
+ _initializeCollectionCount = c.Count;
Current = default;
}
public bool MoveNext()
{
- int written = _rollingCollection.Count;
- if (_read >= written)
+ if (!_enumerating)
+ return false;
+
+ int written = _enumeratedRingBuffer.Count;
+
+ if (written != _initializeCollectionCount)
+ {
+ Debug.LogError($"{_enumeratedRingBuffer.GetType().Name} collection was modified during enumeration.");
+ //This will force a return/reset.
+ _entriesEnumerated = written;
+ }
+
+ if (_entriesEnumerated >= written)
{
- ResetRead();
+ Reset();
return false;
}
- int index = (_startIndex + _read);
- int capacity = _rollingCollection.Capacity;
+ int index = (_startIndex + _entriesEnumerated);
+ int capacity = _enumeratedRingBuffer.Capacity;
if (index >= capacity)
index -= capacity;
Current = _collection[index];
- _read++;
+ _entriesEnumerated++;
return true;
}
- ///
- /// Sets a new start index to begin reading at.
- ///
- public void SetStartIndex(int index)
- {
- _startIndex = index;
- ResetRead();
- }
-
-
- ///
- /// Sets a new start index to begin reading at.
- ///
- public void AddStartIndex(int value)
- {
- _startIndex += value;
-
- int cap = _rollingCollection.Capacity;
- if (_startIndex > cap)
- _startIndex -= cap;
- else if (_startIndex < 0)
- _startIndex += cap;
-
- ResetRead();
- }
-
- ///
- /// Resets number of entries read during the enumeration.
- ///
- public void ResetRead()
- {
- _read = 0;
- }
-
///
/// Resets read count.
///
public void Reset()
{
- _startIndex = 0;
- ResetRead();
+ /* Only need to reset value types.
+ * Numeric types change during initialization. */
+ _enumeratedRingBuffer = default;
+ _collection = default;
+ Current = default;
}
object IEnumerator.Current => Current;
@@ -178,10 +173,18 @@ public void Dispose() { }
///
private bool _atCapacity => (_written == Capacity);
#endregion
+
+
+ #region Consts.
+ ///
+ /// Default capacity when none is psecified.
+ ///
+ public const int DEFAULT_CAPACITY = 60;
+ #endregion
- public ResettableRingBuffer()
+ public ResettableRingBuffer()
{
- _enumerator = new(this);
+ Initialize(DEFAULT_CAPACITY);
}
///
@@ -216,6 +219,20 @@ public void Initialize(int capacity)
void GetNewCollection() => Collection = ArrayPool.Shared.Rent(capacity);
}
+
+ ///
+ /// Initializes with default capacity.
+ ///
+ /// True to log automatic initialization.
+ public void Initialize()
+ {
+ if (!Initialized)
+ {
+ UnityEngine.Debug.Log($"RingBuffer for type {typeof(T).FullName} is being initialized with a default capacity of {DEFAULT_CAPACITY}.");
+ Initialize(DEFAULT_CAPACITY);
+ }
+ }
+
///
/// Clears the collection to default values and resets indexing.
@@ -345,13 +362,10 @@ private void IncreaseWritten()
WriteIndex = 0;
/* If written has exceeded capacity
- * then the start index needs to be moved
- * to adjust for overwritten values. */
+ * then the start index needs to be moved
+ * to adjust for overwritten values. */
if (_written > capacity)
- {
_written = capacity;
- _enumerator.SetStartIndex(WriteIndex);
- }
}
@@ -388,20 +402,6 @@ int ReturnError()
}
}
- ///
- /// Returns Enumerator for the collection.
- ///
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Enumerator GetEnumerator()
- {
- if (!IsInitializedWithError())
- return default;
-
- _enumerator.ResetRead();
- return _enumerator;
- }
-
///
/// Removes values from the simulated start of the collection.
///
@@ -426,11 +426,10 @@ public void RemoveRange(bool fromStart, int length)
_written -= length;
if (fromStart)
{
- _enumerator.AddStartIndex(length);
+ //No steps are needed from start other than reduce written, which is done above.
}
else
{
-
WriteIndex -= length;
if (WriteIndex < 0)
WriteIndex += Capacity;
@@ -467,6 +466,21 @@ public void ResetState()
}
public void InitializeState() { }
+
+
+ ///
+ /// Returns Enumerator for the collection.
+ ///
+ ///
+ public Enumerator GetEnumerator()
+ {
+ Initialize();
+ _enumerator.Initialize(this);
+ return _enumerator;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); // Collection.GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); // Collection.GetEnumerator();
}
}
\ No newline at end of file
diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs
index fd6ad6d8..c734f0a6 100644
--- a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs
+++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs
@@ -3,13 +3,14 @@
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using UnityEngine;
+
namespace GameKit.Dependencies.Utilities.Types
{
-
///
/// Writes values to a collection of a set size, overwriting old values as needed.
///
- public class RingBuffer
+ public class RingBuffer : IEnumerable
{
#region Types.
///
@@ -22,122 +23,91 @@ public struct Enumerator : IEnumerator
/// Current entry in the enumerator.
///
public T Current { get; private set; }
- ///
- /// Actual index of the last enumerated value.
- ///
- public int ActualIndex
- {
- get
- {
- int total = (_startIndex + (_read - 1));
- int capacity = _rollingCollection.Capacity;
- if (total >= capacity)
- total -= capacity;
-
- return total;
- }
- }
- ///
- /// Simulated index of the last enumerated value.
- ///
- public int SimulatedIndex => (_read - 1);
#endregion
#region Private.
///
/// RollingCollection to use.
///
- private RingBuffer _rollingCollection;
+ private RingBuffer _enumeratedRingBuffer;
///
/// Collection to iterate.
///
- private readonly T[] _collection;
+ private T[] _collection;
///
/// Number of entries read during the enumeration.
///
- private int _read;
+ private int _entriesEnumerated;
///
/// Start index of enumerations.
///
private int _startIndex;
+ ///
+ /// True if currently enumerating.
+ ///
+ private bool _enumerating => (_enumeratedRingBuffer != null);
+ ///
+ /// Count of the collection during initialization.
+ ///
+ private int _initializeCollectionCount;
#endregion
- public Enumerator(RingBuffer c)
+ public void Initialize(RingBuffer c)
{
- _read = 0;
- _startIndex = 0;
- _rollingCollection = c;
+ _entriesEnumerated = 0;
+ _startIndex = c.GetRealIndex(0);
+ _enumeratedRingBuffer = c;
_collection = c.Collection;
+ _initializeCollectionCount = c.Count;
Current = default;
}
public bool MoveNext()
{
- int written = _rollingCollection.Count;
- if (_read >= written)
+ if (!_enumerating)
+ return false;
+
+ int written = _enumeratedRingBuffer.Count;
+
+ if (written != _initializeCollectionCount)
+ {
+ Debug.LogError($"{_enumeratedRingBuffer.GetType().Name} collection was modified during enumeration.");
+ //This will force a return/reset.
+ _entriesEnumerated = written;
+ }
+
+ if (_entriesEnumerated >= written)
{
- ResetRead();
+ Reset();
return false;
}
- int index = (_startIndex + _read);
- int capacity = _rollingCollection.Capacity;
+ int index = (_startIndex + _entriesEnumerated);
+ int capacity = _enumeratedRingBuffer.Capacity;
if (index >= capacity)
index -= capacity;
Current = _collection[index];
- _read++;
+ _entriesEnumerated++;
return true;
}
- ///
- /// Sets a new start index to begin reading at.
- ///
- public void SetStartIndex(int index)
- {
- _startIndex = index;
- ResetRead();
- }
-
-
- ///
- /// Sets a new start index to begin reading at.
- ///
- public void AddStartIndex(int value)
- {
- _startIndex += value;
-
- int cap = _rollingCollection.Capacity;
- if (_startIndex > cap)
- _startIndex -= cap;
- else if (_startIndex < 0)
- _startIndex += cap;
-
- ResetRead();
- }
-
- ///
- /// Resets number of entries read during the enumeration.
- ///
- public void ResetRead()
- {
- _read = 0;
- }
-
///
/// Resets read count.
///
public void Reset()
{
- _startIndex = 0;
- ResetRead();
+ /* Only need to reset value types.
+ * Numeric types change during initialization. */
+ _enumeratedRingBuffer = default;
+ _collection = default;
+ Current = default;
}
object IEnumerator.Current => Current;
public void Dispose() { }
}
-
#endregion
#region Public.
@@ -174,6 +144,30 @@ public void Dispose() { }
private Enumerator _enumerator;
#endregion
+ #region Consts.
+ ///
+ /// Default capacity when none is psecified.
+ ///
+ public const int DEFAULT_CAPACITY = 60;
+ #endregion
+
+ ///
+ /// Initializes with default capacity.
+ ///
+ public RingBuffer()
+ {
+ Initialize(DEFAULT_CAPACITY);
+ }
+
+ ///
+ /// Initializes with a set capacity.
+ ///
+ /// Size to initialize the collection as. This cannot be changed after initialized.
+ public RingBuffer(int capacity)
+ {
+ Initialize(capacity);
+ }
+
///
/// Initializes the collection at length.
///
@@ -192,15 +186,34 @@ public void Initialize(int capacity)
}
else if (Collection.Length < capacity)
{
+ Clear();
ArrayPool.Shared.Return(Collection);
GetNewCollection();
}
+ else
+ {
+ Clear();
+ }
Capacity = capacity;
Initialized = true;
void GetNewCollection() => Collection = ArrayPool.Shared.Rent(capacity);
}
+
+ ///
+ /// Initializes with default capacity.
+ ///
+ /// True to log automatic initialization.
+ public void Initialize()
+ {
+ if (!Initialized)
+ {
+ UnityEngine.Debug.Log($"RingBuffer for type {typeof(T).FullName} is being initialized with a default capacity of {DEFAULT_CAPACITY}.");
+ Initialize(DEFAULT_CAPACITY);
+ }
+ }
+
///
/// Clears the collection to default values and resets indexing.
@@ -218,7 +231,7 @@ public void Clear()
///
/// Resets the collection without clearing.
///
- [Obsolete("This method no longer functions. Use Clear() instead.")] //Remove on 2024/06/01.
+ [Obsolete("This method no longer functions. Use Clear() instead.")] //Remove on V5
public void Reset() { }
///
@@ -228,22 +241,23 @@ public void Reset() { }
/// Simulated index to return. A value of 0 would return the first simulated index in the collection.
/// Data to insert.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Insert(int simulatedIndex, T data)
+ public T Insert(int simulatedIndex, T data)
{
- if (!IsInitializedWithError())
- return;
+ Initialize();
+
+ int written = _written;
+ //If simulatedIndex is 0 and none are written then add.
+ if (simulatedIndex == 0 && written == 0)
+ return Add(data);
int realIndex = GetRealIndex(simulatedIndex);
if (realIndex == -1)
- return;
+ return default;
- int written = _written;
- //If adding to the end.
+
+ //If adding to the end or none written.
if (simulatedIndex == (written - 1))
- {
- Add(data);
- return;
- }
+ return Add(data);
int lastSimulatedIndex = (written == Capacity) ? (written - 1) : written;
@@ -255,10 +269,13 @@ public void Insert(int simulatedIndex, T data)
lastSimulatedIndex--;
}
+ T prev = Collection[realIndex];
Collection[realIndex] = data;
//If written was not maxed out then increase it.
if (written < Capacity)
IncreaseWritten();
+
+ return prev;
}
///
@@ -269,8 +286,7 @@ public void Insert(int simulatedIndex, T data)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Add(T data)
{
- if (!IsInitializedWithError())
- return default;
+ Initialize();
T current = Collection[WriteIndex];
Collection[WriteIndex] = data;
@@ -302,7 +318,6 @@ public T this[int simulatedIndex]
}
}
-
///
/// Increases written count and handles offset changes.
///
@@ -317,16 +332,12 @@ private void IncreaseWritten()
WriteIndex = 0;
/* If written has exceeded capacity
- * then the start index needs to be moved
- * to adjust for overwritten values. */
+ * then the start index needs to be moved
+ * to adjust for overwritten values. */
if (_written > capacity)
- {
_written = capacity;
- _enumerator.SetStartIndex(WriteIndex);
- }
}
-
///
/// Returns the real index of the collection using a simulated index.
///
@@ -355,25 +366,11 @@ private int GetRealIndex(int simulatedIndex, bool allowUnusedBuffer = false)
int ReturnError()
{
- UnityEngine.Debug.LogError($"Index {simulatedIndex} is out of range. Collection count is {_written}, Capacity is {Capacity}");
+ UnityEngine.Debug.LogError($"Index {simulatedIndex} is out of range. Written count is {_written}, Capacity is {Capacity}");
return -1;
}
}
- ///
- /// Returns Enumerator for the collection.
- ///
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Enumerator GetEnumerator()
- {
- if (!IsInitializedWithError())
- return default;
-
- _enumerator.ResetRead();
- return _enumerator;
- }
-
///
/// Removes values from the simulated start of the collection.
///
@@ -398,32 +395,28 @@ public void RemoveRange(bool fromStart, int length)
_written -= length;
if (fromStart)
{
- _enumerator.AddStartIndex(length);
+ //No steps are needed from start other than reduce written, which is done above.
}
else
{
-
WriteIndex -= length;
if (WriteIndex < 0)
WriteIndex += Capacity;
}
}
-
+
///
- /// Returns if initialized and errors if not.
+ /// Returns Enumerator for the collection.
///
///
- private bool IsInitializedWithError()
+ public Enumerator GetEnumerator()
{
- if (!Initialized)
- {
- UnityEngine.Debug.LogError($"RingBuffer has not yet been initialized.");
- return false;
- }
-
- return true;
+ Initialize();
+ _enumerator.Initialize(this);
+ return _enumerator;
}
+ IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); // Collection.GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); // Collection.GetEnumerator();
}
-
-}
+}
\ No newline at end of file
diff --git a/Assets/FishNet/Runtime/Serializing/Writer.cs b/Assets/FishNet/Runtime/Serializing/Writer.cs
index d7cef4d3..45c2698e 100644
--- a/Assets/FishNet/Runtime/Serializing/Writer.cs
+++ b/Assets/FishNet/Runtime/Serializing/Writer.cs
@@ -10,6 +10,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using GameKit.Dependencies.Utilities.Types;
using UnityEngine;
[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)]
@@ -1261,9 +1262,8 @@ internal void WriteReconcile(T data)
///
/// Writes a replication to the server.
///
- internal void WriteReplicate(List values, int offset) where T : IReplicateData
+ internal void WriteReplicate(RingBuffer values, int offset) where T : IReplicateData
{
- int startLength = Length;
/* COUNT
*
* Each Entry:
diff --git a/Assets/FishNet/Runtime/Utility/ChildTransformTickSmoother.cs b/Assets/FishNet/Runtime/Utility/ChildTransformTickSmoother.cs
index e2536a6d..1eae853c 100644
--- a/Assets/FishNet/Runtime/Utility/ChildTransformTickSmoother.cs
+++ b/Assets/FishNet/Runtime/Utility/ChildTransformTickSmoother.cs
@@ -224,7 +224,7 @@ public ChildTransformTickSmoother() { }
public void InitializeNetworked(NetworkObject nob, Transform graphicalObject, bool detach, float teleportDistance, float tickDelta, byte ownerInterpolation, TransformPropertiesFlag ownerSmoothedProperties, byte spectatorInterpolation, TransformPropertiesFlag specatorSmoothedProperties, AdaptiveInterpolationType adaptiveInterpolation)
{
ResetState();
-
+
_networkObject = nob;
_spectatorInterpolation = spectatorInterpolation;
_spectatorSmoothedProperties = specatorSmoothedProperties;
@@ -579,7 +579,7 @@ private void SetMoveRates(in TransformProperties prevValues)
_moveRates = MoveRates.GetMoveRates(prevValues, nextValues, duration, teleportT);
_moveRates.TimeRemaining = duration;
-
+
SetMovementMultiplier();
}
diff --git a/Assets/FishNet/package.json b/Assets/FishNet/package.json
index e6f7c780..11ead87f 100644
--- a/Assets/FishNet/package.json
+++ b/Assets/FishNet/package.json
@@ -1,6 +1,6 @@
{
"name": "com.firstgeargames.fishnet",
- "version": "4.4.5",
+ "version": "4.4.6",
"displayName": "FishNet: Networking Evolved",
"description": "A feature-rich Unity networking solution aimed towards reliability, ease of use, efficiency, and flexibility.",
"unity": "2021.3",