chore: updated FishNet

This commit is contained in:
Jakob Feldmann 2024-05-14 14:29:29 +02:00
parent 79a147dead
commit b69f2aff47
197 changed files with 8429 additions and 8131 deletions

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: f1525dbf8ebd59e438b504fa19c4fd6c
guid: baec5a367bebced4da1b56dbcedde312
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -8,11 +8,13 @@ using FishNet.Object.Helping;
using FishNet.Serializing;
using FishNet.Serializing.Helping;
using FishNet.Utility.Performance;
using GameKit.Dependencies.Utilities;
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using MonoFN.Cecil.Rocks;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices.ComTypes;
using UnityEngine;
using SR = System.Reflection;
@ -63,6 +65,7 @@ namespace FishNet.CodeGenerating.Helping
public TypeReference ActionT3_TypeRef;
public MethodReference ActionT2Constructor_MethodRef;
public MethodReference ActionT3Constructor_MethodRef;
public TypeReference ObjectCaches_TypeRef;
private Dictionary<Type, TypeReference> _importedTypeReferences = new Dictionary<Type, TypeReference>();
private Dictionary<FieldDefinition, FieldReference> _importedFieldReferences = new Dictionary<FieldDefinition, FieldReference>();
@ -70,6 +73,7 @@ namespace FishNet.CodeGenerating.Helping
private Dictionary<TypeReference, TypeDefinition> _typeReferenceResolves = new Dictionary<TypeReference, TypeDefinition>();
private Dictionary<FieldReference, FieldDefinition> _fieldReferenceResolves = new Dictionary<FieldReference, FieldDefinition>();
private Dictionary<string, MethodDefinition> _comparerDelegates = new Dictionary<string, MethodDefinition>();
private MethodReference _objectCaches_Retrieve_MethodRef;
#endregion
#region Const.
@ -93,6 +97,9 @@ namespace FishNet.CodeGenerating.Helping
ExcludeSerializationAttribute_FullName = typeof(ExcludeSerializationAttribute).FullName;
NotSerializerAttribute_FullName = typeof(NotSerializerAttribute).FullName;
TypeReference _objectCaches_TypeRef = base.ImportReference(typeof(ObjectCaches<>));
_objectCaches_Retrieve_MethodRef = _objectCaches_TypeRef.CachedResolve(base.Session).GetMethodReference(base.Session, nameof(ObjectCaches<int>.Retrieve));
tmpType = typeof(BasicQueue<>);
base.ImportReference(tmpType);
foreach (SR.MethodInfo mi in tmpType.GetMethods())
@ -904,7 +911,23 @@ namespace FishNet.CodeGenerating.Helping
#region SetVariableDef.
/// <summary>
/// Initializes variableDef as a new object or collection of typeDef.
/// Initializes variableDef as an object or collection of typeDef using cachces.
/// </summary>
/// <param name="processor"></param>
/// <param name="variableDef"></param>
/// <param name="typeDef"></param>
public void SetVariableDefinitionFromCaches(ILProcessor processor, VariableDefinition variableDef, TypeDefinition typeDef)
{
TypeReference dataTr = variableDef.VariableType;
GenericInstanceType git = ObjectCaches_TypeRef.MakeGenericInstanceType(new TypeReference[] { dataTr });
MethodReference genericInstanceMethod = _objectCaches_Retrieve_MethodRef.MakeHostInstanceGeneric(base.Session, git);
processor.Emit(OpCodes.Call, genericInstanceMethod);
processor.Emit(OpCodes.Stloc, variableDef);
}
/// <summary>
/// Initializes variableDef as a new object or collection of typeDef using instantiation.
/// </summary>
/// <param name="processor"></param>
/// <param name="variableDef"></param>

View File

@ -78,6 +78,7 @@ namespace FishNet.CodeGenerating.ILCore
modified |= CreateDeclaredSerializerDelegates(session);
modified |= CreateDeclaredSerializers(session);
modified |= CreateDeclaredComparerDelegates(session);
modified |= CreateIncludeSerializationSerializers(session);
modified |= CreateIBroadcast(session);
#if !DISABLE_QOL_ATTRIBUTES
modified |= CreateQOLAttributes(session);
@ -233,6 +234,43 @@ namespace FishNet.CodeGenerating.ILCore
return modified;
}
/// <summary>
/// Creates serializers for types that use IncludeSerialization attribute.
/// </summary>
private bool CreateIncludeSerializationSerializers(CodegenSession session)
{
string attributeName = typeof(IncludeSerializationAttribute).FullName;
WriterProcessor wp = session.GetClass<WriterProcessor>();
ReaderProcessor rp = session.GetClass<ReaderProcessor>();
bool modified = false;
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
foreach (TypeDefinition td in allTypeDefs)
{
if (!CanSerialize())
continue;
TypeReference tr = session.ImportReference(td);
if (wp.CreateWriter(tr) != null && rp.CreateReader(tr) != null)
modified = true;
else
session.LogError($"Failed to create serializers for {td.FullName}.");
bool CanSerialize()
{
foreach (CustomAttribute item in td.CustomAttributes)
{
if (item.AttributeType.FullName == attributeName)
return true;
}
return false;
}
}
return modified;
}
/// <summary>

View File

@ -302,7 +302,7 @@ namespace FishNet.CodeGenerating.Processing
{
if (inst.Operand is MethodReference mr)
{
if (mr.FullName == reconcileMd.FullName)
if (mr.Name == reconcileMd.Name)
return true;
}
}
@ -372,6 +372,8 @@ namespace FishNet.CodeGenerating.Processing
{
if (!MethodIsPrivate(methodDef) || AlreadyFound(reconcileMd))
error = true;
else
reconcileMd = methodDef;
}
if (error)
break;

View File

@ -11,6 +11,7 @@ using FishNet.CodeGenerating.Extension;
using FishNet.Utility.Performance;
using FishNet.Object;
using FishNet.Utility;
using GameKit.Dependencies.Utilities;
namespace FishNet.CodeGenerating.Helping
{
@ -220,7 +221,7 @@ namespace FishNet.CodeGenerating.Helping
return;
GeneratedReader_OnLoad_MethodDef.RemoveEndRet(base.Session);
//Check if already exist.
ILProcessor processor = GeneratedReader_OnLoad_MethodDef.Body.GetILProcessor();
TypeReference dataTypeRef = readMr.ReturnType;
@ -365,7 +366,7 @@ namespace FishNet.CodeGenerating.Helping
TypeReference genericTr = base.ImportReference(readTypeRef);
readMr = _readUnpackedMethodRef.GetMethodReference(base.Session, genericTr);
}
insts.Add(processor.Create(OpCodes.Call, readMr));
//Store into local variable.
insts.Add(processor.Create(OpCodes.Stloc, createdVariableDef));
@ -1045,8 +1046,18 @@ namespace FishNet.CodeGenerating.Helping
}
/* If here then not null. */
//Make a new instance of object type and set to objectVariableDef.
base.GetClass<GeneralHelper>().SetVariableDefinitionFromObject(processor, objectVariableDef, objectTypeDef);
//See if to use non-alloc reads.
if (objectTr.CachedResolve(base.Session).HasCustomAttribute<ReadUnallocated>())
{
//Make a new instance of object type and set to objectVariableDef.
base.GetClass<GeneralHelper>().SetVariableDefinitionFromCaches(processor, objectVariableDef, objectTypeDef);
}
else
{
//Make a new instance of object type and set to objectVariableDef.
base.GetClass<GeneralHelper>().SetVariableDefinitionFromObject(processor, objectVariableDef, objectTypeDef);
}
if (!ReadFieldsAndProperties(createdReaderMd, readerParameterDef, objectVariableDef, objectTr))
return null;
/* //codegen scriptableobjects seem to climb too high up to UnityEngine.Object when

View File

@ -218,10 +218,7 @@ namespace FishNet.CodeGenerating.Processing.Rpc
{
RpcType rpcType = base.GetClass<AttributeHelper>().GetRpcAttributeType(customAttribute);
if (rpcType != RpcType.None)
{
count++;
break;
}
}
}

View File

@ -80,7 +80,8 @@ namespace FishNet.CodeGenerating.Helping
/// </summary>
public static readonly string[] EXCLUDED_ASSEMBLY_PREFIXES = new string[]
{
"UnityEngine."
"UnityEngine.",
"Unity.Mathmatics",
};
#endregion

View File

@ -1,15 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4489d77032a81ef42b0067acf2737d4d, type: 3}
m_Name: _Authenticator_Prefabs
m_EditorClassIdentifier:
_prefabs: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: ab6a9000f5ff83f45b6761c2a3be018d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -362,7 +362,7 @@ MonoBehaviour:
_objectPool: {fileID: 0}
_persistence: 0
_logging: {fileID: 0}
_spawnablePrefabs: {fileID: 11400000, guid: ab6a9000f5ff83f45b6761c2a3be018d, type: 2}
_spawnablePrefabs: {fileID: 11400000, guid: 68e79e63a16f2c74e81f070bd36822b8, type: 2}
--- !u!1 &7443408886491481971
GameObject:
m_ObjectHideFlags: 0

View File

@ -1,16 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4489d77032a81ef42b0067acf2737d4d, type: 3}
m_Name: _ColliderRollback_Prefabs
m_EditorClassIdentifier:
_prefabs:
- {fileID: 8475222101369129519, guid: 8cf33e8e99a9b0c4c8f29ff725650de6, type: 3}

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d9693011b7416444aa06cafe900e23c0
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -309,6 +309,7 @@ MonoBehaviour:
_predictionType: 0
_graphicalObject: {fileID: 0}
_enableStateForwarding: 1
_networkTransform: {fileID: 0}
_ownerInterpolation: 1
_enableTeleport: 0
_teleportThreshold: 1
@ -833,6 +834,7 @@ MonoBehaviour:
_predictionType: 0
_graphicalObject: {fileID: 0}
_enableStateForwarding: 1
_networkTransform: {fileID: 0}
_ownerInterpolation: 1
_enableTeleport: 0
_teleportThreshold: 1
@ -1817,6 +1819,7 @@ Canvas:
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_VertexColorAlwaysGammaSpace: 0
m_AdditionalShaderChannelsFlag: 0
m_SortingLayerID: 0
m_SortingOrder: 0
@ -2099,13 +2102,14 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 6f48f002b825cbd45a19bd96d90f9edb, type: 3}
m_Name:
m_EditorClassIdentifier:
_unreliableMTU: 1023
_dontRoute: 0
_unreliableMtu: 1023
_ipv4BindAddress:
_enableIpv6: 1
_ipv6BindAddress:
_port: 7770
_maximumClients: 4095
_clientAddress: localhost
_timeout: 15
--- !u!114 &1016939666208092867
MonoBehaviour:
m_ObjectHideFlags: 0
@ -2699,6 +2703,7 @@ Canvas:
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_VertexColorAlwaysGammaSpace: 0
m_AdditionalShaderChannelsFlag: 0
m_SortingLayerID: 0
m_SortingOrder: 0
@ -2797,7 +2802,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 6d3606bfdac5a4743890fc1a5ecd8f24, type: 3}
m_Name:
m_EditorClassIdentifier:
_autoStartType: 0
_autoStartType: 2
_stoppedColor: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1}
_changingColor: {r: 0.78431374, g: 0.6862745, b: 0, a: 1}
_startedColor: {r: 0, g: 0.5882353, b: 0.64705884, a: 1}

View File

@ -1,17 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4489d77032a81ef42b0067acf2737d4d, type: 3}
m_Name: _HashGrid_Prefabs
m_EditorClassIdentifier:
_prefabs:
- {fileID: 4512293259955182956, guid: 44611030e61220d42ab7c37ba3c0ea92, type: 3}
- {fileID: 4512293259955182956, guid: 0d6d0f48b03b17f49a6340103cd9b9d0, type: 3}

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 2863463f6933f3f439a639f883f642f6
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -290,6 +290,14 @@ MonoBehaviour:
_initializeOrder: 0
_defaultDespawnType: 0
NetworkObserver: {fileID: 0}
_enablePrediction: 0
_predictionType: 0
_graphicalObject: {fileID: 0}
_enableStateForwarding: 1
_networkTransform: {fileID: 0}
_ownerInterpolation: 1
_enableTeleport: 0
_teleportThreshold: 1
<PrefabId>k__BackingField: 0
<SpawnableCollectionId>k__BackingField: 0
_scenePathHash: 1046374546
@ -429,6 +437,7 @@ MonoBehaviour:
- 90
- 100
_updateHostVisibility: 1
_maximumTimedObserversDuration: 10
_defaultConditions:
- {fileID: 11400000, guid: cc503f7541ebd424c94541e6a767efee, type: 2}
--- !u!114 &1424052073952814568
@ -854,6 +863,7 @@ Canvas:
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_VertexColorAlwaysGammaSpace: 0
m_AdditionalShaderChannelsFlag: 0
m_SortingLayerID: 0
m_SortingOrder: 0
@ -1002,7 +1012,7 @@ MonoBehaviour:
_objectPool: {fileID: 0}
_persistence: 0
_logging: {fileID: 0}
_spawnablePrefabs: {fileID: 11400000, guid: 2863463f6933f3f439a639f883f642f6, type: 2}
_spawnablePrefabs: {fileID: 11400000, guid: 68e79e63a16f2c74e81f070bd36822b8, type: 2}
--- !u!1 &7443408886575219563
GameObject:
m_ObjectHideFlags: 0
@ -1090,9 +1100,9 @@ MonoBehaviour:
_ipv4BindAddress:
_enableIpv6: 1
_ipv6BindAddress:
_port: 30681
_port: 7112
_maximumClients: 4095
_clientAddress: 4d3a4e3f1ace.pr.edgegap.net
_clientAddress: localhost
--- !u!224 &9139860295505404435
RectTransform:
m_ObjectHideFlags: 0

View File

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

View File

@ -1,15 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4489d77032a81ef42b0067acf2737d4d, type: 3}
m_Name: _IntermediateLayer_Prefabs
m_EditorClassIdentifier:
_prefabs: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: dcdf15fc89edd2546baf8ff544470626
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,17 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4489d77032a81ef42b0067acf2737d4d, type: 3}
m_Name: _Network LOD_Prefabs
m_EditorClassIdentifier:
_prefabs:
- {fileID: 4512293259955182956, guid: 300370bdf7819da41937e0beac65b32c, type: 3}
- {fileID: 4512293259955182956, guid: f32d4c19de900e64cb73cedcb8ba6f70, type: 3}

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 0078a285cda09f349b2668a721a53212
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

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

View File

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

View File

@ -1,77 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: BlueMat
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ShaderKeywords:
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 0, g: 0.363446, b: 0.5471698, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a2ca0973c8f8a4846bb7f3894c3bc5cf
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -1,202 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &303449598114786579
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 303449598114786577}
- component: {fileID: -2060332294883903109}
- component: {fileID: 201277550}
- component: {fileID: 4148834954576211901}
m_Layer: 0
m_Name: CharacterControllerPrediction
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &303449598114786577
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -4.80351, y: 0.18147132, z: 5.430528}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6952090537135875148}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &-2060332294883903109
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 53a6d84aa5d214e48a7308646e5d20da, type: 3}
m_Name:
m_EditorClassIdentifier:
_componentIndexCache: 0
_addedNetworkObject: {fileID: 201277550}
_networkObjectCache: {fileID: 201277550}
--- !u!114 &201277550
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3}
m_Name:
m_EditorClassIdentifier:
<IsNested>k__BackingField: 0
<ComponentIndex>k__BackingField: 0
<PredictedSpawn>k__BackingField: {fileID: 0}
_networkBehaviours:
- {fileID: -2060332294883903109}
<SerializedRootNetworkBehaviour>k__BackingField: {fileID: 0}
<NestedRootNetworkBehaviours>k__BackingField: []
SerializedTransformProperties:
Position: {x: -4.80351, y: 0.18147132, z: 5.430528}
Rotation: {x: 0, y: 0, z: 0, w: 1}
LocalScale: {x: 1, y: 1, z: 1}
_isNetworked: 1
_isSpawnable: 1
_isGlobal: 0
_initializeOrder: 0
_defaultDespawnType: 0
NetworkObserver: {fileID: 0}
<PrefabId>k__BackingField: 3
<SpawnableCollectionId>k__BackingField: 0
_scenePathHash: 0
<SceneId>k__BackingField: 0
<AssetPathHash>k__BackingField: 15176741689908595000
--- !u!143 &4148834954576211901
CharacterController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_Material: {fileID: 0}
m_IsTrigger: 0
m_Enabled: 1
serializedVersion: 2
m_Height: 2
m_Radius: 0.5
m_SlopeLimit: 45
m_StepOffset: 0.3
m_SkinWidth: 0.08
m_MinMoveDistance: 0.001
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &5873068582516782542
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6952090537135875148}
- component: {fileID: 6605329024375725182}
- component: {fileID: 9006911641345807741}
- component: {fileID: 4946798872430241371}
m_Layer: 0
m_Name: Capsule
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &6952090537135875148
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5873068582516782542}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0.01, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 303449598114786577}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &6605329024375725182
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5873068582516782542}
m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &9006911641345807741
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5873068582516782542}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!136 &4946798872430241371
CapsuleCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5873068582516782542}
m_Material: {fileID: 0}
m_IsTrigger: 0
m_Enabled: 1
m_Radius: 0.5
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 0, z: 0}

View File

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

View File

@ -1,152 +0,0 @@
using FishNet;
using FishNet.Object;
using FishNet.Object.Prediction;
using FishNet.Transporting;
using System.Collections.Generic;
using UnityEngine;
using static FishNet.PredictionV2.CharacterControllerPredictionV2;
namespace FishNet.PredictionV2
{
public class CharacterControllerPredictionV2 : NetworkBehaviour
{
#if !PREDICTION_1
public struct MoveData : IReplicateData
{
public uint SentTick;
public bool Jump;
public float Horizontal;
public float Vertical;
public MoveData(bool jump, float horizontal, float vertical, uint sentTick)
{
Jump = jump;
Horizontal = horizontal;
Vertical = vertical;
SentTick = sentTick;
_tick = 0;
}
private uint _tick;
public void Dispose() { }
public uint GetTick() => _tick;
public void SetTick(uint value) => _tick = value;
}
public struct ReconcileData : IReconcileData
{
public Vector3 Position;
public float VerticalVelocity;
public ReconcileData(Vector3 position, float verticalVelocity)
{
Position = position;
VerticalVelocity = verticalVelocity;
_tick = 0;
}
private uint _tick;
public void Dispose() { }
public uint GetTick() => _tick;
public void SetTick(uint value) => _tick = value;
}
[SerializeField]
private float _jumpForce = 15f;
[SerializeField]
private float _moveRate = 4f;
private CharacterController _characterController;
private bool _jump;
private float _verticalVelocity;
private void Update()
{
if (base.IsOwner)
{
if (Input.GetKeyDown(KeyCode.Space))
_jump = true;
}
}
public override void OnStartNetwork()
{
_characterController = GetComponent<CharacterController>();
base.TimeManager.OnTick += TimeManager_OnTick;
}
public override void OnStopNetwork()
{
base.TimeManager.OnTick -= TimeManager_OnTick;
}
private void TimeManager_OnTick()
{
Move(BuildMoveData());
/* The base.IsServer check is not required but does save a little
* performance by not building the reconcileData if not server. */
CreateReconcile();
}
public override void CreateReconcile()
{
if (base.IsServerStarted)
{
ReconcileData rd = new ReconcileData(transform.position, _verticalVelocity);
Reconciliation(rd);
}
}
private MoveData BuildMoveData()
{
if (!base.IsOwner)
return default;
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
MoveData md;
if (horizontal != 0 || vertical != 0)
md = new MoveData(_jump, horizontal, vertical, base.TimeManager.LocalTick);
else
md = new MoveData(_jump, horizontal, vertical, 0);
_jump = false;
return md;
}
[Replicate]
private void Move(MoveData md, ReplicateState state = ReplicateState.Invalid, Channel channel = Channel.Unreliable)
{
if (state == ReplicateState.CurrentFuture)
return;
if (md.Jump)
_verticalVelocity = _jumpForce;
float delta = (float)base.TimeManager.TickDelta;
_verticalVelocity += (Physics.gravity.y * delta);
if (_verticalVelocity < -20f)
_verticalVelocity = -20f;
Vector3 forces = new Vector3(md.Horizontal, _verticalVelocity, md.Vertical) * _moveRate;
_characterController.Move(forces * delta);
}
[Reconcile]
private void Reconciliation(ReconcileData rd, Channel channel = Channel.Unreliable)
{
transform.position = rd.Position;
_verticalVelocity = rd.VerticalVelocity;
}
#endif
}
}

View File

@ -1,5 +0,0 @@
Prediction V2 is a work in progress and so are the examples.
The Rigidbody example may not smooth properly.
The CharacterController example is expected to work.

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 43f99d5241a617843a16537c3229c093
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 7f31e665d963214449f27dc0ad466dfb
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -1,451 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &303449597948771942
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 303449597948771941}
- component: {fileID: 303449597948771939}
- component: {fileID: 303449597948771940}
m_Layer: 0
m_Name: Cube (1)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &303449597948771941
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449597948771942}
m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.5, y: 0.25, z: 0.25}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 303449599178274091}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
--- !u!33 &303449597948771939
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449597948771942}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &303449597948771940
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449597948771942}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &303449598114786579
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 303449598114786577}
- component: {fileID: 4875864441162262683}
- component: {fileID: 303449598114786578}
- component: {fileID: 201277550}
- component: {fileID: 201277549}
m_Layer: 0
m_Name: RigidbodyPrediction
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &303449598114786577
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -4.80351, y: 0.18147132, z: 5.430528}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 303449599178274091}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4875864441162262683
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1e66bfd8c92aad24e8526e7d3aae8b34, type: 3}
m_Name:
m_EditorClassIdentifier:
_componentIndexCache: 0
_addedNetworkObject: {fileID: 201277550}
_networkObjectCache: {fileID: 201277550}
--- !u!135 &303449598114786578
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_Material: {fileID: 13400000, guid: b31539408c1f95d4281bb4484bb6b78e, type: 2}
m_IsTrigger: 0
m_Enabled: 1
serializedVersion: 2
m_Radius: 0.5
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &201277550
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3}
m_Name:
m_EditorClassIdentifier:
<IsNested>k__BackingField: 0
<ComponentIndex>k__BackingField: 0
<PredictedSpawn>k__BackingField: {fileID: 0}
_networkBehaviours:
- {fileID: 4875864441162262683}
<SerializedRootNetworkBehaviour>k__BackingField: {fileID: 0}
<NestedRootNetworkBehaviours>k__BackingField: []
SerializedTransformProperties:
Position: {x: -4.80351, y: 0.18147132, z: 5.430528}
Rotation: {x: 0, y: 0, z: 0, w: 1}
LocalScale: {x: 1, y: 1, z: 1}
_isNetworked: 1
_isSpawnable: 1
_isGlobal: 0
_initializeOrder: 0
_defaultDespawnType: 0
NetworkObserver: {fileID: 0}
<PrefabId>k__BackingField: 21
<SpawnableCollectionId>k__BackingField: 0
_scenePathHash: 0
<SceneId>k__BackingField: 0
<AssetPathHash>k__BackingField: 12616697518120799840
--- !u!54 &201277549
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
serializedVersion: 2
m_Mass: 1
m_Drag: 0
m_AngularDrag: 0.05
m_UseGravity: 1
m_IsKinematic: 0
m_Interpolate: 0
m_Constraints: 112
m_CollisionDetection: 1
--- !u!1 &303449598207636365
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 303449598207636364}
- component: {fileID: 303449598207636362}
- component: {fileID: 303449598207636363}
m_Layer: 0
m_Name: Cube (2)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &303449598207636364
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598207636365}
m_LocalRotation: {x: 0.5, y: 0.5, z: 0.5, w: 0.5}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.5, y: 0.25, z: 0.25}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 303449599178274091}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 90, z: 90}
--- !u!33 &303449598207636362
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598207636365}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &303449598207636363
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598207636365}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &303449598691742042
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 303449598691742041}
- component: {fileID: 303449598691742039}
- component: {fileID: 303449598691742040}
m_Layer: 0
m_Name: Cube
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &303449598691742041
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598691742042}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.5, y: 0.25, z: 0.25}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 303449599178274091}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &303449598691742039
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598691742042}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &303449598691742040
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598691742042}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &303449599178274092
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 303449599178274091}
- component: {fileID: 303449599178274088}
- component: {fileID: 303449599178274089}
m_Layer: 0
m_Name: Sphere (1)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &303449599178274091
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449599178274092}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 303449598691742041}
- {fileID: 303449597948771941}
- {fileID: 303449598207636364}
m_Father: {fileID: 303449598114786577}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &303449599178274088
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449599178274092}
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &303449599178274089
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449599178274092}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 8ef354bfc16ca8a459074c3fa9b6727e
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -1,211 +0,0 @@
using FishNet.Object;
using FishNet.Object.Prediction;
using FishNet.Transporting;
using UnityEngine;
/*
*
* See TransformPrediction.cs for more detailed notes.
*
*/
namespace FishNet.PredictionV2
{
/* THIS CLASS IS CURRENTLY USED FOR TESTING AND IS NOT CONSIDERED
* AN EXAMPLE TO FOLLOW. */
/* THIS CLASS IS CURRENTLY USED FOR TESTING AND IS NOT CONSIDERED
* AN EXAMPLE TO FOLLOW. */
/* THIS CLASS IS CURRENTLY USED FOR TESTING AND IS NOT CONSIDERED
* AN EXAMPLE TO FOLLOW. */
/* THIS CLASS IS CURRENTLY USED FOR TESTING AND IS NOT CONSIDERED
* AN EXAMPLE TO FOLLOW. */
public class RigidbodyPredictionV2 : NetworkBehaviour
{
#if !PREDICTION_1
public struct MoveData : IReplicateData
{
public bool Jump;
public float Horizontal;
public float Vertical;
public Vector3 OtherImpulseForces;
public MoveData(bool jump, float horizontal, float vertical, Vector3 otherImpulseForces)
{
Jump = jump;
Horizontal = horizontal;
Vertical = vertical;
OtherImpulseForces = otherImpulseForces;
_tick = 0;
}
private uint _tick;
public void Dispose() { }
public uint GetTick() => _tick;
public void SetTick(uint value) => _tick = value;
}
public struct ReconcileData : IReconcileData
{
public Vector3 Position;
public Quaternion Rotation;
public Vector3 Velocity;
public Vector3 AngularVelocity;
public ReconcileData(Vector3 position, Quaternion rotation, Vector3 velocity, Vector3 angularVelocity)
{
Position = position;
Rotation = rotation;
Velocity = velocity;
AngularVelocity = angularVelocity;
_tick = 0;
}
private uint _tick;
public void Dispose() { }
public uint GetTick() => _tick;
public void SetTick(uint value) => _tick = value;
}
//[SerializeField]
//private float _jumpForce = 15f;
[SerializeField]
private float _moveRate = 15f;
public Rigidbody Rigidbody { get; private set; }
private bool _jump;
private void Update()
{
if (base.IsOwner)
{
if (Input.GetKeyDown(KeyCode.Space))
_jump = true;
}
}
public override void OnStartNetwork()
{
Rigidbody = GetComponent<Rigidbody>();
base.TimeManager.OnTick += TimeManager_OnTick;
base.TimeManager.OnPostTick += TimeManager_OnPostTick;
}
public override void OnStopNetwork()
{
base.TimeManager.OnTick -= TimeManager_OnTick;
base.TimeManager.OnPostTick -= TimeManager_OnPostTick;
}
private void TimeManager_OnTick()
{
Move(BuildMoveData());
}
private void TimeManager_OnPostTick()
{
CreateReconcile();
}
private MoveData BuildMoveData()
{
if (!IsOwner && Owner.IsValid)
return default;
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
//MoveData md = new MoveData(_jump, horizontal, vertical, (SpringForces + RocketForces));
MoveData md = new MoveData(_jump, horizontal, vertical, Vector3.zero);
//SpringForces = Vector3.zero;
//RocketForces = Vector3.zero;
_jump = false;
return md;
}
public uint LastMdTick;
[Replicate]
private void Move(MoveData md, ReplicateState state = ReplicateState.Invalid, Channel channel = Channel.Unreliable)
{
LastMdTick = md.GetTick();
//if (base.IsOwner)
// Debug.Log(PredictionManager.ClientReplayTick + " > " + md.GetTick());
//if (state == ReplicateState.Future)
//{
// /* Reduce velocity slightly. This will be slightly less accurate if
// * the object continues to move in the same direction but can drastically
// * reduce jarring visuals if the object changes path rather than predicted(future)
// * forward. */
// _rigidbody.velocity *= 0.65f;
// _rigidbody.angularVelocity *= 0.65f;
// return;
//}
//Vector3 forces = new Vector3(md.Horizontal, 0f, md.Vertical) * _moveRate;
//Rigidbody.AddForce(forces);
//if (md.Jump)
// Rigidbody.AddForce(new Vector3(0f, _jumpForce, 0f), ForceMode.Impulse);
////Add gravity to make the object fall faster.
//Rigidbody.AddForce(Physics.gravity * 3f);
Vector3 forces = new Vector3(md.Horizontal, 0f, md.Vertical) * _moveRate;
//PRB.AddForce(forces);
forces += Physics.gravity * 3f;
//if (md.Jump)
// PRB.AddForce(new Vector3(0f, _jumpForce, 0f), ForceMode.Impulse);
////Add gravity to make the object fall faster.
//PRB.AddForce(forces);
//if (IsOwner)
//{
// if (state.IsReplayed())
// Debug.Log($"{md.GetTick()} -> {transform.position.x} -> {Rigidbody.velocity.x}");
// else
// Debug.LogWarning($"{md.GetTick()} -> {transform.position.x} -> {Rigidbody.velocity.x}");
//}
//if ((!base.IsServerStarted && base.IsOwner) || (base.IsServerStarted && !base.IsOwner))
// Debug.LogWarning($"Frame {Time.frameCount}. State {state}, Horizontal {md.Horizontal}. MdTick {md.GetTick()}, PosX {transform.position.x.ToString("0.##")}. VelX {Rigidbody.velocity.x.ToString("0.###")}.");
}
public override void CreateReconcile()
{
/* The base.IsServer check is not required but does save a little
* performance by not building the reconcileData if not server. */
if (IsServerStarted)
{
ReconcileData rd = new ReconcileData(transform.position, transform.rotation, Rigidbody.velocity, Rigidbody.angularVelocity);
//if (!base.IsOwner)
// Debug.LogError($"Frame {Time.frameCount}. Reconcile, MdTick {LastMdTick}, PosX {transform.position.x.ToString("0.##")}. VelX {Rigidbody.velocity.x.ToString("0.###")}.");
Reconciliation(rd);
}
}
[Reconcile]
private void Reconciliation(ReconcileData rd, Channel channel = Channel.Unreliable)
{
transform.position = rd.Position;
transform.rotation = rd.Rotation;
Rigidbody.velocity = rd.Velocity;
Rigidbody.angularVelocity = rd.AngularVelocity;
//if (PrintForClient())
//{
// Debug.LogError($"Frame {Time.frameCount}. Reconcile, MdTick {rd.GetTick()}, PosX {transform.position.x.ToString("0.##")}. VelX {Rigidbody.velocity.x.ToString("0.###")}. RdPosX " +
// $"{rd.Position.x.ToString("0.##")}. RdVelX {Rigidbody.velocity.x.ToString("0.###")}");
//}
}
private bool PrintForClient() => ((!base.IsServerStarted && base.IsOwner) || (base.IsServerStarted && !base.IsOwner));
#endif
}
}

View File

@ -28,9 +28,9 @@ RectTransform:
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -1}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4393252311501663115}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
@ -60,7 +60,6 @@ MonoBehaviour:
m_Material: {fileID: 0}
m_Color: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
@ -105,11 +104,11 @@ RectTransform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4393252311652982283}
- {fileID: 4393252311501663115}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
@ -128,7 +127,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 6d3606bfdac5a4743890fc1a5ecd8f24, type: 3}
m_Name:
m_EditorClassIdentifier:
_autoStartType: 0
AutoStart: 0
_stoppedColor: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1}
_changingColor: {r: 0.78431374, g: 0.6862745, b: 0, a: 1}
_startedColor: {r: 0, g: 0.5882353, b: 0.64705884, a: 1}
@ -151,9 +150,7 @@ Canvas:
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_VertexColorAlwaysGammaSpace: 0
m_AdditionalShaderChannelsFlag: 0
m_UpdateRectTransformForStandalone: 0
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
@ -179,7 +176,6 @@ MonoBehaviour:
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
m_PresetInfoIsWorld: 0
--- !u!114 &4393252310969058989
MonoBehaviour:
m_ObjectHideFlags: 0
@ -226,10 +222,10 @@ RectTransform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 9139860296052841449}
m_Father: {fileID: 4393252310969058990}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
@ -259,7 +255,6 @@ MonoBehaviour:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
@ -288,7 +283,6 @@ MonoBehaviour:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_WrapAround: 0
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
@ -319,7 +313,6 @@ MonoBehaviour:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 4393252310969058994}
m_TargetAssemblyTypeName:
m_MethodName: OnClick_Client
m_Mode: 1
m_Arguments:
@ -359,10 +352,10 @@ RectTransform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7233259200663826443}
m_Father: {fileID: 4393252310969058990}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
@ -392,7 +385,6 @@ MonoBehaviour:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
@ -421,7 +413,6 @@ MonoBehaviour:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_WrapAround: 0
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
@ -452,7 +443,6 @@ MonoBehaviour:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 4393252310969058994}
m_TargetAssemblyTypeName:
m_MethodName: OnClick_Server
m_Mode: 1
m_Arguments:
@ -491,9 +481,9 @@ RectTransform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -1}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4393252311652982283}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
@ -523,7 +513,6 @@ MonoBehaviour:
m_Material: {fileID: 0}
m_Color: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:

View File

@ -0,0 +1,208 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &7443408887813606051
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7443408887813606049}
- component: {fileID: 7443408887813606050}
- component: {fileID: 934570884}
- component: {fileID: 7443408887813606060}
m_Layer: 0
m_Name: NetworkManager
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &7443408887813606049
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7443408887813606051}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 4393252310584637084}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &7443408887813606050
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7443408887813606051}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d2c95dfde7d73b54dbbdc23155d35d36, type: 3}
m_Name:
m_EditorClassIdentifier:
_logging: {fileID: 0}
_spawnablePrefabs: {fileID: 11400000, guid: ec64eb18c93ab344892891f33edbf82a, type: 2}
_refreshDefaultPrefabs: 0
_runInBackground: 1
_dontDestroyOnLoad: 1
_persistence: 0
--- !u!114 &934570884
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7443408887813606051}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7d331f979d46e8e4a9fc90070c596d44, type: 3}
m_Name:
m_EditorClassIdentifier:
_defaultConditions:
- {fileID: 11400000, guid: 2033f54fd2794464bae08fa5a55c8996, type: 2}
--- !u!114 &7443408887813606060
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7443408887813606051}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 211a9f6ec51ddc14f908f5acc0cd0423, type: 3}
m_Name:
m_EditorClassIdentifier:
_playerPrefab: {fileID: 0}
_addToDefaultScene: 1
Spawns: []
--- !u!1001 &2130063410
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 7443408887813606049}
m_Modifications:
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_Pivot.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_Pivot.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_RootOrder
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_AnchorMax.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_AnchorMin.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058995, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
propertyPath: m_Name
value: NetworkHudCanvas
objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
--- !u!224 &4393252310584637084 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
m_PrefabInstance: {fileID: 2130063410}
m_PrefabAsset: {fileID: 0}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 5b712878ecece354ba4ffb026c0a221c
guid: 0b650fca685f2eb41a86538aa883e4c1
PrefabImporter:
externalObjects: {}
userData:

View File

@ -1,16 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4489d77032a81ef42b0067acf2737d4d, type: 3}
m_Name: _SceneManager_Old_Prefabs
m_EditorClassIdentifier:
_prefabs:
- {fileID: 611616139817875448, guid: bf5f023b4017a5e41a9815ec5745df3d, type: 3}

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: b1b4d193fc9b1bb41a92600a22b3d34e
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,16 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4489d77032a81ef42b0067acf2737d4d, type: 3}
m_Name: _SceneManager_Additive Scenes_Prefabs
m_EditorClassIdentifier:
_prefabs:
- {fileID: 8192566354860284824, guid: 6331b3542e64a564c81bc39cedf70c8d, type: 3}

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 223fa01133c52c7469e6520b901bcebd
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,115 +1,115 @@
//using System.Collections;
//using System.Collections.Generic;
using System.Collections;
using System.Collections.Generic;
//namespace FishNet.Transporting.Bayou
//{
// internal class BidirectionalDictionary<T1, T2> : IEnumerable
// {
// private Dictionary<T1, T2> t1ToT2Dict = new Dictionary<T1, T2>();
// private Dictionary<T2, T1> t2ToT1Dict = new Dictionary<T2, T1>();
namespace FishNet.Transporting.Bayou
{
internal class BidirectionalDictionary<T1, T2> : IEnumerable
{
private Dictionary<T1, T2> t1ToT2Dict = new Dictionary<T1, T2>();
private Dictionary<T2, T1> t2ToT1Dict = new Dictionary<T2, T1>();
// public IEnumerable<T1> FirstTypes => t1ToT2Dict.Keys;
// public IEnumerable<T2> SecondTypes => t2ToT1Dict.Keys;
public IEnumerable<T1> FirstTypes => t1ToT2Dict.Keys;
public IEnumerable<T2> SecondTypes => t2ToT1Dict.Keys;
// public IEnumerator GetEnumerator() => t1ToT2Dict.GetEnumerator();
public IEnumerator GetEnumerator() => t1ToT2Dict.GetEnumerator();
// public int Count => t1ToT2Dict.Count;
public int Count => t1ToT2Dict.Count;
// public Dictionary<T1, T2> First => t1ToT2Dict;
// public Dictionary<T2, T1> Second => t2ToT1Dict;
public Dictionary<T1, T2> First => t1ToT2Dict;
public Dictionary<T2, T1> Second => t2ToT1Dict;
// public void Add(T1 key, T2 value)
// {
// if (t1ToT2Dict.ContainsKey(key))
// {
// Remove(key);
// }
public void Add(T1 key, T2 value)
{
if (t1ToT2Dict.ContainsKey(key))
{
Remove(key);
}
// t1ToT2Dict[key] = value;
// t2ToT1Dict[value] = key;
// }
t1ToT2Dict[key] = value;
t2ToT1Dict[value] = key;
}
// public void Add(T2 key, T1 value)
// {
// if (t2ToT1Dict.ContainsKey(key))
// {
// Remove(key);
// }
public void Add(T2 key, T1 value)
{
if (t2ToT1Dict.ContainsKey(key))
{
Remove(key);
}
// t2ToT1Dict[key] = value;
// t1ToT2Dict[value] = key;
// }
t2ToT1Dict[key] = value;
t1ToT2Dict[value] = key;
}
// public T2 Get(T1 key) => t1ToT2Dict[key];
public T2 Get(T1 key) => t1ToT2Dict[key];
// public T1 Get(T2 key) => t2ToT1Dict[key];
public T1 Get(T2 key) => t2ToT1Dict[key];
// public bool TryGetValue(T1 key, out T2 value) => t1ToT2Dict.TryGetValue(key, out value);
public bool TryGetValue(T1 key, out T2 value) => t1ToT2Dict.TryGetValue(key, out value);
// public bool TryGetValue(T2 key, out T1 value) => t2ToT1Dict.TryGetValue(key, out value);
public bool TryGetValue(T2 key, out T1 value) => t2ToT1Dict.TryGetValue(key, out value);
// public bool Contains(T1 key) => t1ToT2Dict.ContainsKey(key);
public bool Contains(T1 key) => t1ToT2Dict.ContainsKey(key);
// public bool Contains(T2 key) => t2ToT1Dict.ContainsKey(key);
public bool Contains(T2 key) => t2ToT1Dict.ContainsKey(key);
// public void Remove(T1 key)
// {
// if (First.TryGetValue(key, out T2 value))
// {
// t1ToT2Dict.Remove(key);
// t2ToT1Dict.Remove(value);
// }
// }
public void Remove(T1 key)
{
if (First.TryGetValue(key, out T2 value))
{
t1ToT2Dict.Remove(key);
t2ToT1Dict.Remove(value);
}
}
// public void Clear()
// {
// First.Clear();
// Second.Clear();
// }
public void Clear()
{
First.Clear();
Second.Clear();
}
// public void Remove(T2 key)
// {
// if (Second.TryGetValue(key, out T1 value))
// {
// t1ToT2Dict.Remove(value);
// t2ToT1Dict.Remove(key);
// }
// }
public void Remove(T2 key)
{
if (Second.TryGetValue(key, out T1 value))
{
t1ToT2Dict.Remove(value);
t2ToT1Dict.Remove(key);
}
}
// public void RemoveFirst(T1 key)
// {
// if (First.TryGetValue(key, out T2 value))
// {
// Second.Remove(value);
// First.Remove(key);
// }
// }
public void RemoveFirst(T1 key)
{
if (First.TryGetValue(key, out T2 value))
{
Second.Remove(value);
First.Remove(key);
}
}
// public void RemoveSecond(T2 key)
// {
// if (Second.TryGetValue(key, out T1 value))
// {
// First.Remove(value);
// Second.Remove(key);
// }
// }
public void RemoveSecond(T2 key)
{
if (Second.TryGetValue(key, out T1 value))
{
First.Remove(value);
Second.Remove(key);
}
}
// public T1 this[T2 key]
// {
// get => t2ToT1Dict[key];
// set
// {
// Add(key, value);
// }
// }
public T1 this[T2 key]
{
get => t2ToT1Dict[key];
set
{
Add(key, value);
}
}
// public T2 this[T1 key]
// {
// get => t1ToT2Dict[key];
// set
// {
// Add(key, value);
// }
// }
// }
//}
public T2 this[T1 key]
{
get => t1ToT2Dict[key];
set
{
Add(key, value);
}
}
}
}

View File

@ -56,7 +56,7 @@ namespace FishNet.Transporting.Bayou.Server
/// <summary>
/// Currently connected clients.
/// </summary>
private HashSet<int> _clients = new HashSet<int>();
private List<int> _clients = new List<int>();
/// <summary>
/// Server socket manager.
/// </summary>

View File

@ -129,7 +129,7 @@ namespace JamesFrowen.SimpleWeb
}
else
{
//Log.Verbose($"BufferBucket({arraySize}) create new");
Log.Verbose($"BufferBucket({arraySize}) create new");
return new ArrayBuffer(this, arraySize);
}
}
@ -145,13 +145,13 @@ namespace JamesFrowen.SimpleWeb
void IncrementCreated()
{
int next = Interlocked.Increment(ref _current);
//Log.Verbose($"BufferBucket({arraySize}) count:{next}");
Log.Verbose($"BufferBucket({arraySize}) count:{next}");
}
[Conditional("DEBUG")]
void DecrementCreated()
{
int next = Interlocked.Decrement(ref _current);
//Log.Verbose($"BufferBucket({arraySize}) count:{next}");
Log.Verbose($"BufferBucket({arraySize}) count:{next}");
}
}

View File

@ -41,7 +41,7 @@ namespace JamesFrowen.SimpleWeb
{
(Connection conn, int maxMessageSize, bool expectMask, ConcurrentQueue<Message> queue, BufferPool _) = config;
//Profiler.BeginThreadProfiling("SimpleWeb", $"ReceiveLoop {conn.connId}");
Profiler.BeginThreadProfiling("SimpleWeb", $"ReceiveLoop {conn.connId}");
byte[] readBuffer = new byte[Constants.HeaderSize + (expectMask ? Constants.MaskSize : 0) + maxMessageSize];
try
@ -95,7 +95,7 @@ namespace JamesFrowen.SimpleWeb
}
finally
{
//Profiler.EndThreadProfiling();
Profiler.EndThreadProfiling();
conn.Dispose();
}

View File

@ -39,7 +39,7 @@ namespace JamesFrowen.SimpleWeb
{
(Connection conn, int bufferSize, bool setMask) = config;
//Profiler.BeginThreadProfiling("SimpleWeb", $"SendLoop {conn.connId}");
Profiler.BeginThreadProfiling("SimpleWeb", $"SendLoop {conn.connId}");
// create write buffer for this thread
byte[] writeBuffer = new byte[bufferSize];
@ -70,11 +70,7 @@ namespace JamesFrowen.SimpleWeb
while (conn.sendQueue.TryDequeue(out ArrayBuffer msg))
{
// check if connected before sending message
if (!client.Connected)
{
//Log.Info($"SendLoop {conn} not connected");
return;
}
if (!client.Connected) { Log.Info($"SendLoop {conn} not connected"); return; }
int maxLength = msg.count + Constants.HeaderSize + Constants.MaskSize;
@ -99,11 +95,7 @@ namespace JamesFrowen.SimpleWeb
while (conn.sendQueue.TryDequeue(out ArrayBuffer msg))
{
// check if connected before sending message
if (!client.Connected)
{
//Log.Info($"SendLoop {conn} not connected");
return;
}
if (!client.Connected) { Log.Info($"SendLoop {conn} not connected"); return; }
int length = SendMessage(writeBuffer, 0, msg, setMask, maskHelper);
stream.Write(writeBuffer, 0, length);
@ -114,14 +106,8 @@ namespace JamesFrowen.SimpleWeb
Log.Info($"{conn} Not Connected");
}
catch (ThreadInterruptedException e)
{
Log.InfoException(e);
}
catch (ThreadAbortException e)
{
Log.InfoException(e);
}
catch (ThreadInterruptedException e) { Log.InfoException(e); }
catch (ThreadAbortException e) { Log.InfoException(e); }
catch (Exception e)
{
Log.Exception(e);
@ -149,7 +135,7 @@ namespace JamesFrowen.SimpleWeb
offset += msgLength;
// dump before mask on
//Log.DumpBuffer("Send", buffer, startOffset, offset);
Log.DumpBuffer("Send", buffer, startOffset, offset);
if (setMask)
{

View File

@ -46,7 +46,7 @@ namespace JamesFrowen.SimpleWeb
if (!IsGet(getHeader.array))
{
//Log.Warn($"First bytes from client was not 'GET' for handshake, instead was {Log.BufferToString(getHeader.array, 0, GetSize)}");
Log.Warn($"First bytes from client was not 'GET' for handshake, instead was {Log.BufferToString(getHeader.array, 0, GetSize)}");
return false;
}
}

View File

@ -52,7 +52,7 @@ namespace JamesFrowen.SimpleWeb
}
}
public void SendAll(HashSet<int> connectionIds, ArraySegment<byte> source)
public void SendAll(List<int> connectionIds, ArraySegment<byte> source)
{
ArrayBuffer buffer = bufferPool.Take(source.Count);
buffer.CopyFrom(source);
@ -60,7 +60,9 @@ namespace JamesFrowen.SimpleWeb
// make copy of array before for each, data sent to each client is the same
foreach (int id in connectionIds)
{
server.Send(id, buffer);
}
}
public void SendOne(int connectionId, ArraySegment<byte> source)
{
@ -80,15 +82,25 @@ namespace JamesFrowen.SimpleWeb
return server.GetClientAddress(connectionId);
}
/// <summary>
/// Processes all messages.
/// Processes all new messages
/// </summary>
public void ProcessMessageQueue()
{
ProcessMessageQueue(null);
}
/// <summary>
/// Processes all messages while <paramref name="behaviour"/> is enabled
/// </summary>
/// <param name="behaviour"></param>
public void ProcessMessageQueue(MonoBehaviour behaviour)
{
int processedCount = 0;
bool skipEnabled = behaviour == null;
// check enabled every time incase behaviour was disabled after data
while (
while (
(skipEnabled || behaviour.enabled) &&
processedCount < maxMessagesPerTick &&
// Dequeue last
server.receiveQueue.TryDequeue(out Message next)

View File

@ -1,5 +1,3 @@
using FishNet.Connection;
using FishNet.Managing;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -33,7 +31,7 @@ namespace JamesFrowen.SimpleWeb
if (_idCache.Count == 0)
GrowIdCache(1000);
int result = NetworkConnection.UNSET_CLIENTID_VALUE;
int result;
_idCache.TryDequeue(out result);
return result;
}
@ -43,11 +41,11 @@ namespace JamesFrowen.SimpleWeb
/// </summary>
private void GrowIdCache(int value)
{
int over = (_nextId + value) - NetworkConnection.MAXIMUM_CLIENTID_VALUE;
int over = (_nextId + value) - int.MaxValue;
//Prevent overflow.
if (over > 0)
value -= over;
for (int i = _nextId; i < value; i++)
_idCache.Enqueue(i);
}
@ -112,7 +110,7 @@ namespace JamesFrowen.SimpleWeb
// this might not be a problem as HandshakeAndReceiveLoop checks for stop
// and returns/disposes before sending message to queue
Connection conn = new Connection(client, AfterConnectionDisposed);
//Log.Info($"A client connected {conn}");
Log.Info($"A client connected {conn}");
// handshake needs its own thread as it needs to wait for message from client
Thread receiveThread = new Thread(() => HandshakeAndReceiveLoop(conn));
@ -142,7 +140,7 @@ namespace JamesFrowen.SimpleWeb
bool success = sslHelper.TryCreateStream(conn);
if (!success)
{
//Log.Error($"Failed to create SSL Stream {conn}");
Log.Error($"Failed to create SSL Stream {conn}");
conn.Dispose();
return;
}
@ -151,11 +149,11 @@ namespace JamesFrowen.SimpleWeb
if (success)
{
//Log.Info($"Sent Handshake {conn}");
Log.Info($"Sent Handshake {conn}");
}
else
{
//Log.Error($"Handshake Failed {conn}");
Log.Error($"Handshake Failed {conn}");
conn.Dispose();
return;
}
@ -168,13 +166,6 @@ namespace JamesFrowen.SimpleWeb
}
conn.connId = GetNextId();
if (conn.connId == NetworkConnection.UNSET_CLIENTID_VALUE)
{
NetworkManagerExtensions.LogWarning($"At maximum connections. A client attempting to connect to be rejected.");
conn.Dispose();
return;
}
connections.TryAdd(conn.connId, conn);
receiveQueue.Enqueue(new Message(conn.connId, EventType.Connected));

View File

@ -1,16 +1,12 @@
{
"name": "SimpleWebTransport",
"rootNamespace": "",
"references": [
"GUID:7c88a4a7926ee5145ad2dfa06f454c67"
],
"references": [],
"optionalUnityReferences": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
"defineConstraints": []
}

View File

@ -23,7 +23,9 @@ namespace Edgegap
public static BuildReport BuildServer()
{
IEnumerable<string> scenes = EditorBuildSettings.scenes.Select(s=>s.path);
IEnumerable<string> scenes = EditorBuildSettings.scenes
.Where(s => s.enabled)
.Select(s => s.path);
BuildPlayerOptions options = new BuildPlayerOptions
{
scenes = scenes.ToArray(),
@ -102,7 +104,7 @@ namespace Edgegap
onStatusUpdate(msg);
});
if (realErrorMessage != null)
if(realErrorMessage != null)
{
throw new Exception(realErrorMessage);
}
@ -297,4 +299,4 @@ ENTRYPOINT [ ""/root/build/ServerBuild"", ""-batchmode"", ""-nographics""]
}
}
#endif
#endif

View File

@ -44,7 +44,6 @@ namespace Edgegap.Editor
private string _deploymentRequestId;
private string _userExternalIp;
private bool _isAwaitingDeploymentReadyStatus;
private bool _registered;
#endregion // Vars
@ -151,7 +150,6 @@ namespace Edgegap.Editor
unregisterClickEvents();
unregisterFieldCallbacks();
SyncObjectWithForm();
_registered = false;
}
#endregion // Unity Funcs
@ -163,7 +161,6 @@ namespace Edgegap.Editor
/// </summary>
private void InitUIElements()
{
_registered = true;
setVisualElementsToFields();
assertVisualElementKeys();
closeDisableGroups();
@ -380,9 +377,6 @@ namespace Edgegap.Editor
/// </summary>
private void unregisterFieldCallbacks()
{
if (!_registered)
return;
_apiTokenInput.UnregisterValueChangedCallback(onApiTokenInputChanged);
_apiTokenInput.UnregisterCallback<FocusOutEvent>(onApiTokenInputFocusOut);
@ -419,8 +413,6 @@ namespace Edgegap.Editor
/// </summary>
private void unregisterClickEvents()
{
if (!_registered)
return;
_debugBtn.clickable.clicked -= onDebugBtnClick;
_apiTokenVerifyBtn.clickable.clicked -= onApiTokenVerifyBtnClick;
@ -612,8 +604,6 @@ namespace Edgegap.Editor
/// </summary>
private void SyncObjectWithForm()
{
if (_appIconSpriteObjInput == null)
return;
_appIconSpriteObj = _appIconSpriteObjInput.value as Sprite;
}

View File

@ -55,10 +55,10 @@ namespace Edgegap
switch (apiEnvironment)
{
case ApiEnvironment.Staging:
apiUrl = "https://staging-docs.edgegap.com/docs";
apiUrl = "https://staging-docs.edgegap.com/docs/category/unity";
break;
case ApiEnvironment.Console:
apiUrl = "https://docs.edgegap.com/docs";
apiUrl = "https://docs.edgegap.com/docs/category/unity";
break;
default:
apiUrl = null;

View File

@ -18,7 +18,7 @@ namespace Newtonsoft.Json
[InitializeOnLoadMethod]
private static void Initialize()
{
{
string dtStr = EditorPrefs.GetString(WARN_TIME_NAME, string.Empty);
//Somehow got cleared. Reset.
if (string.IsNullOrWhiteSpace(dtStr))
@ -43,12 +43,13 @@ namespace Newtonsoft.Json
}
ResetWarnTime();
UnityEngine.Debug.LogWarning($"Edgegap requires Json.NET to be imported to function. To import Json.NET navigate to Window -> Package Manager -> Click the + symbol and choose 'Add package by name' -> com.unity.nuget.newtonsoft-json -> Leave version blank and click Add. If you are not currently using Edgegap you may ignore this message.");
}
}
}
#endif
namespace Newtonsoft.Json
{

View File

@ -10,7 +10,12 @@ namespace FishNet.CodeGenerating
/// </summary>
public class AllowMutableSyncTypeAttribute : Attribute { }
/// <summary>
/// Type will be excluded from creating auto serializer creation.
/// Type will be included in auto serializer creation.
/// </summary>
[AttributeUsage((AttributeTargets.Class | AttributeTargets.Struct), Inherited = true, AllowMultiple = false)]
public class IncludeSerializationAttribute : Attribute { }
/// <summary>
/// Type will be excluded from auto serializer creation.
/// </summary>
public class ExcludeSerializationAttribute : Attribute { }
/// <summary>
@ -30,4 +35,10 @@ namespace FishNet.CodeGenerating
/// </summary>
[AttributeUsage((AttributeTargets.Class | AttributeTargets.Struct), Inherited = true, AllowMultiple = false)]
public class UseGlobalCustomSerializerAttribute : Attribute { }
/// <summary>
/// Uses built-in caches to retrieve read classes rather than initializing a new instance.
/// This attribute is primarily for internal use and may change at anytime without notice.
/// </summary>
[AttributeUsage((AttributeTargets.Class), Inherited = true, AllowMultiple = false)]
public class ReadUnallocated : Attribute { }
}

View File

@ -147,7 +147,7 @@ namespace FishNet.Connection
* Modify reserve after making sendLast bundle
* so that the wrong reserve is not passed into
* the sendLast bundle. */
reserve += TransportManager.TICK_BYTES;
reserve += TransportManager.UNPACKED_TICK_LENGTH;
_reserve = reserve;
//Add buffer requires the right reserve so call after setting.
AddBuffer();

View File

@ -1,6 +1,10 @@
using FishNet.Managing;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#define DEVELOPMENT
#endif
using FishNet.Managing;
using FishNet.Managing.Predicting;
using FishNet.Managing.Timing;
using FishNet.Managing.Transporting;
using FishNet.Serializing;
using FishNet.Transporting;
using System.Collections.Generic;
@ -45,20 +49,21 @@ namespace FishNet.Connection
/// <param name="data"></param>
internal void WriteState(PooledWriter data)
{
#if !DEVELOPMENT_BUILD && !UNITY_EDITOR
#if !DEVELOPMENT
//Do not send states to clientHost.
if (IsLocalClient)
return;
#endif
TimeManager tm = NetworkManager.TimeManager;
uint ticksBehind = (IsLocalClient) ? 0 : PacketTick.LocalTickDifference(tm);
TimeManager timeManager = NetworkManager.TimeManager;
TransportManager transportManager = NetworkManager.TransportManager;
uint ticksBehind = (IsLocalClient) ? 0 : PacketTick.LocalTickDifference(timeManager);
/* If it's been a really long while the client could just be setting up
* or dropping. Only send if they've communicated within 5 seconds. */
if (ticksBehind > (tm.TickRate * 5))
if (ticksBehind > (timeManager.TickRate * 5))
return;
int mtu = NetworkManager.TransportManager.GetLowestMTU((byte)Channel.Unreliable);
int mtu = transportManager.GetLowestMTU((byte)Channel.Unreliable);
PooledWriter stateWriter;
int writerCount = PredictionStateWriters.Count;
/* Conditions to create a new writer are:
@ -66,7 +71,7 @@ namespace FishNet.Connection
* - data length + currentWriter length > mtu */
Channel channel = Channel.Unreliable;
if (writerCount > 0)
NetworkManager.TransportManager.CheckSetReliableChannel((data.Length + PredictionStateWriters[writerCount - 1].Length), ref channel);
transportManager.CheckSetReliableChannel((data.Length + PredictionStateWriters[writerCount - 1].Length), ref channel);
/* If no writers or if channel would be forced reliable.
*
* By checking if channel would be reliable this is
@ -78,8 +83,11 @@ namespace FishNet.Connection
if (writerCount == 0 || channel == Channel.Reliable)
{
stateWriter = WriterPool.Retrieve(mtu);
PredictionStateWriters.Add(stateWriter);
stateWriter.Reserve(PredictionManager.STATE_HEADER_RESERVE_COUNT);
PredictionStateWriters.Add(stateWriter);
stateWriter.Reserve(PredictionManager.STATE_HEADER_RESERVE_LENGTH);
/// 2 PacketId.
/// 4 Last replicate tick run for connection.
/// 4 Length unpacked.
}
else
{

View File

@ -1,8 +1,10 @@
using FishNet.Component.Observing;
using FishNet.CodeGenerating;
using FishNet.Component.Observing;
using FishNet.Documenting;
using FishNet.Managing;
using FishNet.Managing.Timing;
using FishNet.Object;
using GameKit.Dependencies.Utilities;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@ -31,9 +33,28 @@ namespace FishNet.Connection
/// <summary>
/// A container for a connected client used to perform actions on and gather information for the declared client.
/// </summary>
public partial class NetworkConnection : IEquatable<NetworkConnection>
public partial class NetworkConnection : IResettable, IEquatable<NetworkConnection>
{
#region Internal.
/// <summary>
/// Tick when Disconnecting was set.
/// </summary>
internal uint DisconnectingTick { get; private set; }
/// <summary>
/// ObjectIds to use for predicted spawning.
/// </summary>
internal Queue<int> PredictedObjectIds = new Queue<int>();
/// <summary>
/// True if the client has sent the same version that the server is on.
/// </summary>
internal bool HasSentVersion;
/// <summary>
/// LocalTick of the server when this connection was established. This value is not set for clients.
/// </summary>
internal uint ServerConnectionTick;
#endregion
#region Public.
/// <summary>
/// Called after this connection has loaded start scenes. Boolean will be true if asServer. Available to this connection and server.
@ -66,18 +87,6 @@ namespace FishNet.Connection
return _loadedStartScenesAsClient;
}
/// <summary>
/// True if loaded start scenes as server.
/// </summary>
private bool _loadedStartScenesAsServer;
/// <summary>
/// True if loaded start scenes as client.
/// </summary>
private bool _loadedStartScenesAsClient;
/// <summary>
/// ObjectIds to use for predicted spawning.
/// </summary>
internal Queue<int> PredictedObjectIds = new Queue<int>();
/// <summary>
/// TransportIndex this connection is on.
/// For security reasons this value will be unset on clients if this is not their connection.
/// </summary>
@ -137,19 +146,11 @@ namespace FishNet.Connection
/// </summary>
public bool Disconnecting { get; private set; }
/// <summary>
/// Tick when Disconnecting was set.
/// </summary>
internal uint DisconnectingTick { get; private set; }
/// <summary>
/// Custom data associated with this connection which may be modified by the user.
/// The value of this field are not synchronized over the network.
/// </summary>
public object CustomData = null;
/// <summary>
/// LocalTick of the server when this connection was established. This value is not set for clients.
/// </summary>
internal uint ServerConnectionTick;
/// <summary>
/// Tick of the last packet received from this connection which was not out of order.
/// This value is only available on the server.
/// </summary>
@ -161,6 +162,17 @@ namespace FishNet.Connection
public EstimatedTick LocalTick { get; private set; } = new EstimatedTick();
#endregion
#region Private.
/// <summary>
/// True if loaded start scenes as server.
/// </summary>
private bool _loadedStartScenesAsServer;
/// <summary>
/// True if loaded start scenes as client.
/// </summary>
private bool _loadedStartScenesAsClient;
#endregion
#region Const.
/// <summary>
/// Value used when ClientId has not been set.
@ -169,7 +181,11 @@ namespace FishNet.Connection
/// <summary>
/// Maximum value a ClientId can be.
/// </summary>
public const int MAXIMUM_CLIENTID_VALUE = (int.MaxValue - 1);
public const int MAXIMUM_CLIENTID_VALUE = int.MaxValue;
/// <summary>
/// Maximum value a ClientId can be excluding simulated value.
/// </summary>
public const int MAXIMUM_CLIENTID_WITHOUT_SIMULATED_VALUE = (int.MaxValue - 1);
/// <summary>
/// Value to use as a ClientId when simulating a local client without actually using a socket.
/// </summary>
@ -177,7 +193,7 @@ namespace FishNet.Connection
/// <summary>
/// Number of bytes to reserve for a connectionId if writing the value uncompressed.
/// </summary>
public const int CLIENTID_UNCOMPRESSED_RESERVE_BYTES = 4;
public const int CLIENTID_UNCOMPRESSED_RESERVE_LENGTH = 4;
#endregion
#region Comparers.
@ -228,11 +244,6 @@ namespace FishNet.Connection
Initialize(manager, clientId, transportIndex, asServer);
}
internal void Dispose()
{
Deinitialize();
}
/// <summary>
/// Outputs data about this connection as a string.
/// </summary>
@ -271,40 +282,6 @@ namespace FishNet.Connection
}
}
/// <summary>
/// Deinitializes this NetworkConnection. This is called prior to resetting.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Deinitialize()
{
MatchCondition.RemoveFromMatchesWithoutRebuild(this, NetworkManager);
foreach (PacketBundle p in _toClientBundles)
p.Dispose();
_toClientBundles.Clear();
ServerConnectionTick = 0;
PacketTick.Reset();
LocalTick.Reset();
TransportIndex = -1;
ClientId = -1;
ClearObjects();
IsAuthenticated = false;
NetworkManager = null;
_loadedStartScenesAsClient = false;
_loadedStartScenesAsServer = false;
SetDisconnecting(false);
Scenes.Clear();
PredictedObjectIds.Clear();
ResetPingPong();
ResetStates_Lod();
AllowedForcedLodUpdates = 0;
LastLevelOfDetailUpdate = 0;
LevelOfDetailInfractions = 0;
Observers_Reset();
Prediction_Reset();
}
/// <summary>
/// Sets Disconnecting boolean for this connection.
/// </summary>
@ -341,18 +318,6 @@ namespace FishNet.Connection
ServerDirty();
}
private float _localTickUpdateTime = float.NegativeInfinity;
/// <summary>
/// Tries to update the LocalTick for this connection after a number of conditions are checked.
/// </summary>
internal void TryUpdateLocalTick(uint tick)
{
bool resetValue = (Time.time - _localTickUpdateTime) > 1f;
LocalTick.Update(tick, OldTickOption.Discard, resetValue);
if (resetValue)
_localTickUpdateTime = Time.time;
}
/// <summary>
/// Returns if just loaded start scenes and sets them as loaded if not.
/// </summary>
@ -468,6 +433,43 @@ namespace FishNet.Connection
return Scenes.Remove(scene);
}
/// <summary>
/// Resets all states for re-use.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetState()
{
MatchCondition.RemoveFromMatchesWithoutRebuild(this, NetworkManager);
foreach (PacketBundle p in _toClientBundles)
p.Dispose();
_toClientBundles.Clear();
ServerConnectionTick = 0;
PacketTick.Reset();
LocalTick.Reset();
TransportIndex = -1;
ClientId = -1;
ClearObjects();
IsAuthenticated = false;
HasSentVersion = false;
NetworkManager = null;
_loadedStartScenesAsClient = false;
_loadedStartScenesAsServer = false;
SetDisconnecting(false);
Scenes.Clear();
PredictedObjectIds.Clear();
ResetPingPong();
ResetStates_Lod();
AllowedForcedLodUpdates = 0;
LastLevelOfDetailUpdate = 0;
LevelOfDetailInfractions = 0;
Observers_Reset();
Prediction_Reset();
}
public void InitializeState() { }
}

View File

@ -1,5 +1,6 @@
#if UNITY_EDITOR
using FishNet.Configuring;
using FishNet.Managing;
using FishNet.Managing.Object;
using FishNet.Object;
using System.Collections.Generic;
@ -437,7 +438,7 @@ namespace FishNet.Editing.PrefabCollectionGenerator
/// <summary>
/// Returns the DefaultPrefabObjects file.
/// </summary>
private static DefaultPrefabObjects GetDefaultPrefabObjects(PrefabGeneratorConfigurations settings = null)
internal static DefaultPrefabObjects GetDefaultPrefabObjects(PrefabGeneratorConfigurations settings = null)
{
if (settings == null)
settings = Configuration.Configurations.PrefabGenerator;
@ -605,37 +606,15 @@ namespace FishNet.Editing.PrefabCollectionGenerator
_ranOnce = true;
fullRebuild = true;
}
else
//Other conditions which a full rebuild may be required.
else if (!fullRebuild)
{
CheckForVersionFile(importedAssets);
CheckForVersionFile(deletedAssets);
CheckForVersionFile(movedAssets);
CheckForVersionFile(movedFromAssetPaths);
}
/* See if any of the changed files are the version file.
* A new version file suggests an update. Granted, this could occur if
* other assets imported a new version file as well but better
* safe than sorry. */
void CheckForVersionFile(string[] arr)
{
string targetText = "VERSION.txt".ToLower();
int targetLength = targetText.Length;
for (int i = 0; i < arr.Length; i++)
const string fishnetVersionSave = "fishnet_version";
string savedVersion = EditorPrefs.GetString(fishnetVersionSave, string.Empty);
if (savedVersion != NetworkManager.FISHNET_VERSION)
{
string item = arr[i];
int itemLength = item.Length;
if (itemLength < targetLength)
continue;
item = item.ToLower();
int startIndex = (itemLength - targetLength);
if (item.Substring(startIndex, targetLength) == targetText)
{
fullRebuild = true;
return;
}
fullRebuild = true;
EditorPrefs.SetString(fishnetVersionSave, NetworkManager.FISHNET_VERSION);
}
}

View File

@ -53,6 +53,8 @@ namespace FishNet
foreach (string item in fishNetDefines)
modified |= definesHs.Add(item);
//Remove old prediction defines.
modified |= definesHs.Remove("PREDICTION_V2");
/* Remove pro define if not on pro. This might look a little
* funny because the code below varies depending on if pro or not. */

View File

@ -3,7 +3,8 @@
"rootNamespace": "",
"references": [
"GUID:894a6cc6ed5cd2645bb542978cbed6a9",
"GUID:1d82bdf40e2465b44b34adf79595e74c"
"GUID:1d82bdf40e2465b44b34adf79595e74c",
"GUID:d8b63aba1907145bea998dd612889d6b"
],
"includePlatforms": [],
"excludePlatforms": [],
@ -17,6 +18,21 @@
"name": "com.veriorpies.parrelsync",
"expression": "1.2",
"define": "PARRELSYNC"
},
{
"name": "com.unity.mathematics",
"expression": "1.2.6",
"define": "UNITYMATHEMATICS"
},
{
"name": "com.unity.mathematics",
"expression": "1.3.1",
"define": "UNITYMATHEMATICS_131"
},
{
"name": "com.unity.mathematics",
"expression": "1.3.2",
"define": "UNITYMATHEMATICS_132"
}
],
"noEngineReferences": false

View File

@ -14,7 +14,7 @@ namespace FishNet.Component.Animating.Editing
{
private SerializedProperty _animator;
private SerializedProperty _interpolation;
//private SerializedProperty _synchronizeInterval;
private SerializedProperty _synchronizeWhenDisabled;
private SerializedProperty _smoothFloats;
private SerializedProperty _clientAuthoritative;
private SerializedProperty _sendToOwner;
@ -23,13 +23,13 @@ namespace FishNet.Component.Animating.Editing
protected virtual void OnEnable()
{
_animator = serializedObject.FindProperty("_animator");
_interpolation = serializedObject.FindProperty("_interpolation");
//_synchronizeInterval = serializedObject.FindProperty("_synchronizeInterval");
_smoothFloats = serializedObject.FindProperty("_smoothFloats");
_animator = serializedObject.FindProperty(nameof(_animator));
_interpolation = serializedObject.FindProperty(nameof(_interpolation));
_synchronizeWhenDisabled = serializedObject.FindProperty(nameof(_synchronizeWhenDisabled));
_smoothFloats = serializedObject.FindProperty(nameof(_smoothFloats));
_clientAuthoritative = serializedObject.FindProperty("_clientAuthoritative");
_sendToOwner = serializedObject.FindProperty("_sendToOwner");
_clientAuthoritative = serializedObject.FindProperty(nameof(_clientAuthoritative));
_sendToOwner = serializedObject.FindProperty(nameof(_sendToOwner));
}
public override void OnInspectorGUI()
@ -50,6 +50,7 @@ namespace FishNet.Component.Animating.Editing
EditorGUILayout.LabelField("Animator", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_animator);
EditorGUILayout.PropertyField(_synchronizeWhenDisabled);
EditorGUI.indentLevel--;
EditorGUILayout.Space();
@ -57,7 +58,6 @@ namespace FishNet.Component.Animating.Editing
EditorGUILayout.LabelField("Synchronization Processing", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_interpolation);
//EditorGUILayout.PropertyField(_synchronizeInterval, new GUIContent("Synchronize Interval", "How often to synchronize this animator."));
EditorGUILayout.PropertyField(_smoothFloats);
EditorGUI.indentLevel--;
EditorGUILayout.Space();

View File

@ -1,3 +1,6 @@
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#define DEVELOPMENT
#endif
using FishNet.Component.Transforming;
using FishNet.Connection;
using FishNet.Documenting;
@ -285,6 +288,12 @@ namespace FishNet.Component.Animating
/// </summary>
public Animator Animator { get { return _animator; } }
/// <summary>
/// True to synchronize changes even when the animator component is disabled.
/// </summary>
[Tooltip("True to synchronize changes even when the animator component is disabled.")]
[SerializeField]
private bool _synchronizeWhenDisabled;
/// <summary>
/// True to smooth float value changes for spectators.
/// </summary>
[Tooltip("True to smooth float value changes for spectators.")]
@ -297,13 +306,6 @@ namespace FishNet.Component.Animating
[Range(1, NetworkTransform.MAX_INTERPOLATION)]
[SerializeField]
private ushort _interpolation = 2;
///// <summary>
///// How often to synchronize this animator.
///// </summary>
//[Tooltip("How often to synchronize this animator.")]
//[Range(0.01f, 0.5f)]
//[SerializeField]
//private float _synchronizeInterval = 0.1f;
/// <summary>
///
/// </summary>
@ -356,13 +358,25 @@ namespace FishNet.Component.Animating
/// </summary>
private List<byte[]> _toClientsBuffer = new List<byte[]>();
/// <summary>
/// Returns if the animator is exist and is active.
/// Returns if the animator is exist and can be synchronized.
/// </summary>
private bool _isAnimatorEnabled
private bool _canSynchronizeAnimator
{
get
{
bool failedChecks = (_animator == null || !_animator.enabled || _animator.runtimeAnimatorController == null);
bool enabled = (_animator.enabled || _synchronizeWhenDisabled);
bool failedChecks = (!_isAnimatorSet || !enabled);
return !failedChecks;
}
}
/// <summary>
/// True if the animator is valid but not enabled.
/// </summary>
private bool _isAnimatorSet
{
get
{
bool failedChecks = (_animator == null || _animator.runtimeAnimatorController == null);
return !failedChecks;
}
}
@ -396,10 +410,6 @@ namespace FishNet.Component.Animating
/// </summary>
private Dictionary<int, StateChange> _unsynchronizedLayerStates = new Dictionary<int, StateChange>();
/// <summary>
/// Layers which need to have their state blend synchronized. Key is ParameterIndex, Value is next state hash.
/// </summary>
//private Dictionary<int, int> _unsynchronizedLayerStates = new HashSet<int>();
/// <summary>
/// Last animator set.
/// </summary>
private Animator _lastAnimator;
@ -469,7 +479,7 @@ namespace FishNet.Component.Animating
[APIExclude]
public override void OnSpawnServer(NetworkConnection connection)
{
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return;
if (AnimatorUpdated(out ArraySegment<byte> updatedBytes, true))
TargetAnimatorUpdated(connection, updatedBytes);
@ -531,7 +541,7 @@ namespace FishNet.Component.Animating
/// </summary>
private void TimeManager_OnPreTick()
{
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
{
_fromServerBuffer.Clear();
return;
@ -565,7 +575,7 @@ namespace FishNet.Component.Animating
private void TimeManager_OnPostTick()
{
//One check rather than per each method.
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return;
CheckSendToServer();
@ -574,7 +584,7 @@ namespace FishNet.Component.Animating
private void Update()
{
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return;
if (base.IsClientStarted)
@ -593,7 +603,7 @@ namespace FishNet.Component.Animating
if (!ApplicationState.IsPlaying())
return;
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
{
//Debug.LogWarning("Animator is null or not enabled; unable to initialize for animator. Use SetAnimator if animator was changed or enable the animator.");
return;
@ -1025,7 +1035,7 @@ namespace FishNet.Component.Animating
/// <param name="changedParameters"></param>
private void ApplyParametersUpdated(ref ArraySegment<byte> updatedParameters)
{
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return;
if (_layerWeights == null)
return;
@ -1153,7 +1163,7 @@ namespace FishNet.Component.Animating
stateHash = 0;
normalizedTime = 0f;
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return false;
AnimatorStateInfo st = _animator.GetCurrentAnimatorStateInfo(layerIndex);
@ -1214,7 +1224,7 @@ namespace FishNet.Component.Animating
/// </summary>
public void Play(int hash, int layer, float normalizedTime)
{
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return;
if (_animator.HasState(layer, hash) || hash == 0)
{
@ -1249,7 +1259,7 @@ namespace FishNet.Component.Animating
/// </summary>
public void PlayInFixedTime(int hash, int layer, float fixedTime)
{
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return;
if (_animator.HasState(layer, hash) || hash == 0)
{
@ -1282,7 +1292,7 @@ namespace FishNet.Component.Animating
/// <param name="normalizedTransitionTime"></param>
public void CrossFade(int hash, float normalizedTransitionDuration, int layer, float normalizedTimeOffset = 0.0f, float normalizedTransitionTime = 0.0f)
{
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return;
if (_animator.HasState(layer, hash) || hash == 0)
{
@ -1312,7 +1322,7 @@ namespace FishNet.Component.Animating
/// <param name="normalizedTransitionTime"></param>
public void CrossFadeInFixedTime(int hash, float fixedTransitionDuration, int layer, float fixedTimeOffset = 0.0f, float normalizedTransitionTime = 0.0f)
{
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return;
if (_animator.HasState(layer, hash) || hash == 0)
{
@ -1329,7 +1339,7 @@ namespace FishNet.Component.Animating
/// <param name="hash"></param>
public void SetTrigger(int hash)
{
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return;
UpdateTrigger(hash, true);
}
@ -1365,7 +1375,7 @@ namespace FishNet.Component.Animating
/// <param name="set"></param>
private void UpdateTrigger(int hash, bool set)
{
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return;
bool clientAuth = ClientAuthoritative;
@ -1422,12 +1432,12 @@ namespace FishNet.Component.Animating
[TargetRpc(ValidateTarget = false)]
private void TargetAnimatorUpdated(NetworkConnection connection, ArraySegment<byte> data)
{
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return;
#if DEVELOPMENT
#if !DEVELOPMENT
//If receiver is client host then do nothing, clientHost need not process.
if (base.IsServer && conn.IsLocalClient)
if (base.IsServerInitialized && connection.IsLocalClient)
return;
#endif
bool clientAuth = ClientAuthoritative;
@ -1457,7 +1467,7 @@ namespace FishNet.Component.Animating
[ServerRpc]
private void ServerAnimatorUpdated(ArraySegment<byte> data)
{
if (!_isAnimatorEnabled)
if (!_canSynchronizeAnimator)
return;
if (!ClientAuthoritative)
{

View File

@ -336,6 +336,10 @@ namespace FishNet.Component.Transforming
/// True if the local client used TakeOwnership and is awaiting an ownership change.
/// </summary>
public bool TakenOwnership { get; private set; }
/// <summary>
/// NetworkBehaviour this transform is a child of.
/// </summary>
public NetworkBehaviour ParentBehaviour { get; private set; }
#endregion
#region Serialized.
@ -519,10 +523,6 @@ namespace FishNet.Component.Transforming
/// </summary>
private bool _lastReceiveReliable = true;
/// <summary>
/// NetworkBehaviour this transform is a child of.
/// </summary>
private NetworkBehaviour _parentBehaviour;
/// <summary>
/// Last transform which this object was a child of.
/// </summary>
private Transform _parentTransform;
@ -1098,7 +1098,7 @@ namespace FishNet.Component.Transforming
else
{
_parentTransform = transform.parent;
_parentBehaviour = parentBehaviour;
ParentBehaviour = parentBehaviour;
}
}
}
@ -1297,10 +1297,10 @@ namespace FishNet.Component.Transforming
}
//Childed.
if (ChangedContains(changed, ChangedDelta.Childed) && _parentBehaviour != null)
if (ChangedContains(changed, ChangedDelta.Childed) && ParentBehaviour != null)
{
flagsB |= UpdateFlagB.Child;
writer.WriteNetworkBehaviour(_parentBehaviour);
writer.WriteNetworkBehaviour(ParentBehaviour);
}
writer.FastInsertByte((byte)flagsB, startIndexB);
@ -1459,7 +1459,7 @@ namespace FishNet.Component.Transforming
if (base.NetworkObject.RuntimeParentNetworkBehaviour != null)
Debug.LogWarning($"{gameObject.name} parent object was removed without calling UnsetParent. Use networkObject.UnsetParent() to remove a NetworkObject from it's parent. This is being made a requirement in Fish-Networking v4.");
_parentBehaviour = null;
ParentBehaviour = null;
_parentTransform = null;
}
//Has a parent, see if eligible.
@ -1470,15 +1470,18 @@ namespace FishNet.Component.Transforming
return;
_parentTransform = parent;
parent.TryGetComponent<NetworkBehaviour>(out _parentBehaviour);
if (_parentBehaviour == null)
NetworkBehaviour outParentBehaviour;
if (!parent.TryGetComponent<NetworkBehaviour>(out outParentBehaviour))
{
ParentBehaviour = null;
LogInvalidParent();
}
else
{
ParentBehaviour = outParentBehaviour;
//Check for being set without using nob.SetParent.
if (base.NetworkObject.CurrentParentNetworkBehaviour != _parentBehaviour)
if (base.NetworkObject.CurrentParentNetworkBehaviour != ParentBehaviour)
Debug.LogWarning($"{gameObject.name} parent was set without calling SetParent. Use networkObject.SetParent(obj) to assign a NetworkObject a new parent. This is being made a requirement in Fish-Networking v4.");
}
}
@ -1689,7 +1692,7 @@ namespace FishNet.Component.Transforming
Transform t = transform;
/* If here a send for transform values will occur. Update last values.
* Tick doesn't need to be set for whoever controls transform. */
lastSentData.Update(0, t.localPosition, t.localRotation, t.localScale, t.localPosition, _parentBehaviour);
lastSentData.Update(0, t.localPosition, t.localRotation, t.localScale, t.localPosition, ParentBehaviour);
SerializeChanged(changed, writer, lodIndex);
}
@ -1764,7 +1767,7 @@ namespace FishNet.Component.Transforming
/* If here a send for transform values will occur. Update last values.
* Tick doesn't need to be set for whoever controls transform. */
Transform t = transform;
lastSentTransformData.Update(0, t.localPosition, t.localRotation, t.localScale, t.localPosition, _parentBehaviour);
lastSentTransformData.Update(0, t.localPosition, t.localRotation, t.localScale, t.localPosition, ParentBehaviour);
//Send latest.
PooledWriter writer = WriterPool.Retrieve();
@ -1876,7 +1879,7 @@ namespace FishNet.Component.Transforming
changed |= ChangedDelta.ScaleZ;
//if (lastParentBehaviour != _parentBehaviour)
if (_parentBehaviour != null)
if (ParentBehaviour != null)
changed |= ChangedDelta.Childed;
//If added scale or childed then also add extended.
@ -2271,7 +2274,6 @@ namespace FishNet.Component.Transforming
{
SetCalculatedRates(lodIndex, prevTd, prevRd, nextGd, changedFull, hasChanged, channel, asServer);
}
prevTd.Update(nextTd);
_lastReceiveReliable = (channel == Channel.Reliable);
/* If channel is reliable then this is a settled packet.

View File

@ -59,16 +59,25 @@ namespace FishNet.Component.Prediction
[HideInInspector]
protected bool IsTrigger;
/// <summary>
/// The maximum number of simultaneous hits to check for.
/// Maximum number of simultaneous hits to check for. Larger values decrease performance but allow detection to work for more overlapping colliders. Typically the default value of 16 is more than sufficient.
/// </summary>
[Tooltip("Maximum number of simultaneous hits to check for. Larger values decrease performance but allow detection to work for more overlapping colliders. Typically the default value of 16 is more than sufficient.")]
[SerializeField]
private ushort _maximumSimultaneousHits = 16;
/// <summary>
/// The duration of the history.
/// How long of collision history to keep. Lower values will result in marginally better memory usage at the cost of collision histories desynchronizing on clients with excessive latency.
/// </summary>
[Tooltip("How long of collision history to keep. Lower values will result in marginally better memory usage at the cost of collision histories desynchronizing on clients with excessive latency.")]
[Range(0.1f, 2f)]
[SerializeField]
private float _historyDuration = 0.5f;
/// <summary>
/// Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough.
/// </summary>
[Tooltip("Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough.")]
[Range(0f, 100f)]
[SerializeField]
private float _additionalSize = 0.1f;
/// <summary>
/// The colliders on this object.
@ -102,7 +111,8 @@ namespace FishNet.Component.Prediction
protected virtual void Awake()
{
_colliderDataHistory = ResettableCollectionCaches<ColliderData>.RetrieveRingBuffer();
//_colliderDataHistory = ResettableCollectionCaches<ColliderData>.RetrieveRingBuffer();
_colliderDataHistory = new();
_hits = CollectionCaches<Collider>.RetrieveArray();
if (_hits.Length < _maximumSimultaneousHits)
_hits = new Collider[_maximumSimultaneousHits];
@ -110,8 +120,8 @@ namespace FishNet.Component.Prediction
private void OnDestroy()
{
ResettableCollectionCaches<ColliderData>.StoreAndDefault(ref _colliderDataHistory);
CollectionCaches<Collider>.StoreAndDefault(ref _hits, -_hits.Length);
//ResettableCollectionCaches<ColliderData>.StoreAndDefault(ref _colliderDataHistory);
CollectionCaches<Collider>.StoreAndDefault(ref _hits, _hits.Length);
}
public override void OnStartNetwork()
@ -193,7 +203,7 @@ namespace FishNet.Component.Prediction
/// <summary>
/// Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough.
/// </summary>
public virtual float GetAdditionalSize() => 0f;
public virtual float GetAdditionalSize() => _additionalSize;
/// <summary>
/// Checks for any trigger changes;
@ -416,7 +426,7 @@ namespace FishNet.Component.Prediction
{
sphereCollider.GetSphereOverlapParams(out Vector3 center, out float radius);
radius += GetAdditionalSize();
return Physics.OverlapSphereNonAlloc(center, radius, _hits, layerMask);
return gameObject.scene.GetPhysicsScene().OverlapSphere(center, radius, _hits, layerMask, QueryTriggerInteraction.UseGlobal);
}
/// <summary>
@ -427,7 +437,7 @@ namespace FishNet.Component.Prediction
{
capsuleCollider.GetCapsuleCastParams(out Vector3 start, out Vector3 end, out float radius);
radius += GetAdditionalSize();
return Physics.OverlapCapsuleNonAlloc(start, end, radius, _hits, layerMask);
return gameObject.scene.GetPhysicsScene().OverlapCapsule(start, end, radius, _hits, layerMask, QueryTriggerInteraction.UseGlobal);
}
/// <summary>
@ -440,7 +450,7 @@ namespace FishNet.Component.Prediction
boxCollider.GetBoxOverlapParams(out Vector3 center, out Vector3 halfExtents);
Vector3 additional = (Vector3.one * GetAdditionalSize());
halfExtents += additional;
return Physics.OverlapBoxNonAlloc(center, halfExtents, _hits, rotation, layerMask);
return gameObject.scene.GetPhysicsScene().OverlapBox(center, halfExtents, _hits, rotation, layerMask, QueryTriggerInteraction.UseGlobal);
}
/// <summary>

View File

@ -59,16 +59,25 @@ namespace FishNet.Component.Prediction
[HideInInspector]
protected bool IsTrigger;
/// <summary>
/// The maximum number of simultaneous hits to check for.
/// Maximum number of simultaneous hits to check for. Larger values decrease performance but allow detection to work for more overlapping colliders. Typically the default value of 16 is more than sufficient.
/// </summary>
[Tooltip("Maximum number of simultaneous hits to check for. Larger values decrease performance but allow detection to work for more overlapping colliders. Typically the default value of 16 is more than sufficient.")]
[SerializeField]
private ushort _maximumSimultaneousHits = 16;
/// <summary>
/// The duration of the history.
/// How long of collision history to keep. Lower values will result in marginally better memory usage at the cost of collision histories desynchronizing on clients with excessive latency.
/// </summary>
[Tooltip("How long of collision history to keep. Lower values will result in marginally better memory usage at the cost of collision histories desynchronizing on clients with excessive latency.")]
[Range(0.1f, 2f)]
[SerializeField]
private float _historyDuration = 0.5f;
/// <summary>
/// Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough.
/// </summary>
[Tooltip("Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough.")]
[Range(0f, 100f)]
[SerializeField]
private float _additionalSize = 0.1f;
/// <summary>
/// The colliders on this object.
@ -102,7 +111,8 @@ namespace FishNet.Component.Prediction
protected virtual void Awake()
{
_colliderDataHistory = ResettableCollectionCaches<Collider2DData>.RetrieveRingBuffer();
_colliderDataHistory = new();
//_colliderDataHistory = ResettableCollectionCaches<Collider2DData>.RetrieveRingBuffer();
_hits = CollectionCaches<Collider2D>.RetrieveArray();
if (_hits.Length < _maximumSimultaneousHits)
_hits = new Collider2D[_maximumSimultaneousHits];
@ -110,8 +120,8 @@ namespace FishNet.Component.Prediction
private void OnDestroy()
{
ResettableCollectionCaches<Collider2DData>.StoreAndDefault(ref _colliderDataHistory);
CollectionCaches<Collider2D>.StoreAndDefault(ref _hits, -_hits.Length);
//ResettableCollectionCaches<Collider2DData>.StoreAndDefault(ref _colliderDataHistory);
CollectionCaches<Collider2D>.StoreAndDefault(ref _hits, _hits.Length);
}
public override void OnStartNetwork()
@ -205,11 +215,11 @@ namespace FishNet.Component.Prediction
}
}
/// <summary>
/// Returns the size multiplier.
/// Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough.
/// </summary>
/// <returns></returns>
protected virtual float GetSizeMultiplier() => 1f;
protected virtual float GetAdditionalSize() => _additionalSize;
/// <summary>
/// Checks for any trigger changes;
@ -429,8 +439,8 @@ namespace FishNet.Component.Prediction
private int GetCircleCollider2DHits(CircleCollider2D circleCollider, int layerMask)
{
circleCollider.GetCircleOverlapParams(out Vector3 center, out float radius);
radius *= GetSizeMultiplier();
return Physics2D.OverlapCircleNonAlloc(center, radius, _hits, layerMask);
radius += GetAdditionalSize();
return gameObject.scene.GetPhysicsScene2D().OverlapCircle(center, radius, _hits, layerMask);
}
/// <summary>
@ -440,8 +450,9 @@ namespace FishNet.Component.Prediction
private int GetBoxCollider2DHits(BoxCollider2D boxCollider, Quaternion rotation, int layerMask)
{
boxCollider.GetBox2DOverlapParams(out Vector3 center, out Vector3 halfExtents);
halfExtents *= GetSizeMultiplier();
return Physics2D.OverlapBoxNonAlloc(center, halfExtents, rotation.z, _hits, layerMask);
Vector3 additional = (Vector3.one * GetAdditionalSize());
halfExtents += additional;
return gameObject.scene.GetPhysicsScene2D().OverlapBox(center, halfExtents, rotation.z, _hits, layerMask);
}
/// <summary>

View File

@ -5,21 +5,11 @@ namespace FishNet.Component.Prediction
public sealed class NetworkCollision : NetworkCollider
{
#if !PREDICTION_1
[Tooltip("Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough.")]
[Range(0f, 100f)]
[SerializeField]
private float _additionalSize = 0.1f;
protected override void Awake()
{
base.IsTrigger = false;
base.Awake();
}
/// <summary>
/// Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough.
/// </summary>
public override float GetAdditionalSize() => _additionalSize;
#endif
}

View File

@ -5,22 +5,11 @@ namespace FishNet.Component.Prediction
public sealed class NetworkCollision2D : NetworkCollider2D
{
#if !PREDICTION_1
/// <summary>
/// Percentage larger than each collider for each overlap test. This is used to prevent missed overlaps when colliders do not intersect enough.
/// </summary>
[Tooltip("Percentage larger than each collider for each overlap test. This is used to prevent missed overlaps when colliders do not intersect enough.")]
[Range(0f, 0.2f)]
[SerializeField]
private float _sizeMultiplier = 0.05f;
protected override void Awake()
{
base.IsTrigger = false;
base.Awake();
}
protected override float GetSizeMultiplier() => (1f + _sizeMultiplier);
#endif
}

View File

@ -280,7 +280,7 @@ namespace FishNet.Component.Prediction
/* If host then initialize owner smoother.
* Host will use owner smoothing settings for more
* accurate results. */
if (base.IsHostInitialized)
if (base.IsHostStarted)
InitializeSmoother(true);
UpdateRigidbodiesCount(true);

View File

@ -245,7 +245,7 @@ namespace FishNet.Component.Prediction
_rigidbodyDatas[index] = rbData;
rb.collisionDetectionMode = CollisionDetectionMode.Discrete;
rb.isKinematic = true;
rb.detectCollisions = false;
//rb.detectCollisions = false;
return true;
}
@ -313,7 +313,7 @@ namespace FishNet.Component.Prediction
return false;
rb.isKinematic = rbData.IsKinematic;
rb.detectCollisions = rbData.DetectCollisions;
//rb.detectCollisions = rbData.DetectCollisions;
rb.collisionDetectionMode = rbData.CollisionDetectionMode;
if (!rb.isKinematic)
{

View File

@ -1,4 +1,7 @@
using FishNet.Connection;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#define DEVELOPMENT
#endif
using FishNet.Connection;
using FishNet.Managing.Debugging;
using FishNet.Managing.Logging;
using FishNet.Managing.Server;
@ -23,6 +26,11 @@ namespace FishNet.Managing.Client
{
#region Public.
/// <summary>
/// This is set true if the server has notified the client it is using a development build.
/// Value is set before authentication.
/// </summary>
public bool IsServerDevelopment { get; private set; }
/// <summary>
/// Called after local client has authenticated.
/// </summary>
public event Action OnAuthenticated;
@ -127,7 +135,7 @@ namespace FishNet.Managing.Client
/// Used to read splits.
/// </summary>
private SplitReader _splitReader = new SplitReader();
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
/// <summary>
/// Logs data about parser to help debug.
/// </summary>
@ -179,7 +187,7 @@ namespace FishNet.Managing.Client
OnRemoteConnectionState?.Invoke(rcs);
if (Clients.TryGetValue(args.Id, out NetworkConnection c))
{
c.Dispose();
c.ResetState();
Clients.Remove(args.Id);
}
}
@ -304,6 +312,12 @@ namespace FishNet.Managing.Client
else
{
_lastPacketTime = Time.unscaledTime;
//Send version.
PooledWriter writer = WriterPool.Retrieve();
writer.WritePacketId(PacketId.Version);
writer.WriteString(NetworkManager.FISHNET_VERSION);
NetworkManager.TransportManager.SendToServer((byte)Channel.Reliable, writer.GetArraySegment());
WriterPool.Store(writer);
}
if (NetworkManager.CanLog(LoggingType.Common))
@ -349,7 +363,7 @@ namespace FishNet.Managing.Client
/// </summary>
private void ParseReceived(ClientReceivedDataArgs args)
{
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
_parseLogger.Reset();
#endif
_lastPacketTime = Time.unscaledTime;
@ -361,7 +375,7 @@ namespace FishNet.Managing.Client
segment = args.Data;
NetworkManager.StatisticsManager.NetworkTraffic.LocalClientReceivedData((ulong)segment.Count);
if (segment.Count <= TransportManager.TICK_BYTES)
if (segment.Count <= TransportManager.UNPACKED_TICK_LENGTH)
return;
PooledReader reader = ReaderPool.Retrieve(segment, NetworkManager, Reader.DataSource.Server);
@ -375,7 +389,7 @@ namespace FishNet.Managing.Client
internal void ParseReader(PooledReader reader, Channel channel, bool print = false)
{
PacketId packetId = PacketId.Unset;
#if !UNITY_EDITOR && !DEVELOPMENT_BUILD
#if !DEVELOPMENT
try
{
#endif
@ -404,7 +418,7 @@ namespace FishNet.Managing.Client
while (reader.Remaining > 0)
{
packetId = reader.ReadPacketId();
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
if (print)
Debug.Log($"PacketId {packetId} - Remaining {reader.Remaining}.");
_parseLogger.AddPacket(packetId);
@ -480,7 +494,7 @@ namespace FishNet.Managing.Client
}
else if (packetId == PacketId.TimingUpdate)
{
NetworkManager.TimeManager.ParseTimingUpdate();
NetworkManager.TimeManager.ParseTimingUpdate(reader);
}
else if (packetId == PacketId.OwnershipChange)
{
@ -495,18 +509,22 @@ namespace FishNet.Managing.Client
reader.Clear();
StopConnection();
}
else if (packetId == PacketId.Version)
{
ParseVersion(reader);
}
else
{
NetworkManager.LogError($"Client received an unhandled PacketId of {(ushort)packetId} on channel {channel}. Remaining data has been purged.");
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
_parseLogger.Print(NetworkManager);
#endif
return;
}
}
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
if (print)
Debug.Log($"Reader remaining {reader.Remaining}");
#endif
@ -519,7 +537,7 @@ namespace FishNet.Managing.Client
* in doing this check multiple times as there's
* an exit early check. */
Objects.IterateObjectCache();
#if !UNITY_EDITOR && !DEVELOPMENT_BUILD
#if !DEVELOPMENT
}
catch (Exception e)
{
@ -539,6 +557,17 @@ namespace FishNet.Managing.Client
NetworkManager.TimeManager.ModifyPing(clientTick);
}
/// <summary>
/// Parses a Version packet.
/// </summary>
/// <param name="reader"></param>
private void ParseVersion(PooledReader reader)
{
IsServerDevelopment = reader.ReadBoolean();
}
/// <summary>
/// Parses a received connectionId. This is received before client receives connection state change.
/// </summary>
@ -578,7 +607,7 @@ namespace FishNet.Managing.Client
}
//If predicted spawning is enabled also get reserved Ids.
if (NetworkManager.PredictionManager.GetAllowPredictedSpawning())
if (NetworkManager.ServerManager.GetAllowPredictedSpawning())
{
byte count = reader.ReadByte();
Queue<int> q = Connection.PredictedObjectIds;
@ -629,7 +658,7 @@ namespace FishNet.Managing.Client
return;
if (_remoteServerTimeout == RemoteTimeoutType.Disabled)
return;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
//If development but not set to development return.
else if (_remoteServerTimeout != RemoteTimeoutType.Development)
return;

View File

@ -94,7 +94,7 @@ namespace FishNet.Managing.Client
{
foreach (NetworkObject n in Spawned.Values)
{
n.InvokeStopCallbacks(false);
n.InvokeStopCallbacks(false, true);
n.SetInitializedStatus(false, false);
}
}
@ -434,8 +434,9 @@ namespace FishNet.Managing.Client
if (sceneObject)
{
ReadSceneObject(reader, out sceneId);
#if UNITY_EDITOR || DEVELOPMENT_BUILD
base.CheckReadSceneObjectDetails(reader, ref sceneName, ref objectName);
#if DEVELOPMENT
if (NetworkManager.ClientManager.IsServerDevelopment)
base.CheckReadSceneObjectDetails(reader, ref sceneName, ref objectName);
#endif
}
else

View File

@ -578,7 +578,7 @@ namespace FishNet.Managing.Client
public Quaternion? LocalRotation;
public Vector3? LocalScale;
public ulong SceneId;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
public string SceneName = string.Empty;
public string ObjectName = string.Empty;
#endif
@ -626,7 +626,7 @@ namespace FishNet.Managing.Client
LocalRotation = localRotation;
LocalScale = localScale;
SceneId = sceneId;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
SceneName = sceneName;
ObjectName = objectName;
#endif

View File

@ -1,4 +1,7 @@
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#define DEVELOPMENT
#endif
#if DEVELOPMENT
using FishNet.Managing.Logging;
using FishNet.Object;
using FishNet.Serializing;

View File

@ -1,4 +1,7 @@
using FishNet.Documenting;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#define DEVELOPMENT
#endif
using FishNet.Documenting;
using System;
using System.Runtime.CompilerServices;
using UnityEngine;
@ -61,13 +64,11 @@ namespace FishNet.Managing.Logging
public override void InitializeOnce()
{
byte currentHighest = (byte)LoggingType.Off;
#if UNITY_SERVER //if headless.
#if UNITY_SERVER
currentHighest = Math.Max(currentHighest, (byte)_headlessLogging);
#endif
#if UNITY_EDITOR || DEVELOPMENT_BUILD //if editor or development.
#elif DEVELOPMENT
currentHighest = Math.Max(currentHighest, (byte)_developmentLogging);
#endif
#if !UNITY_EDITOR && !UNITY_SERVER //if a build.
#else
currentHighest = Math.Max(currentHighest, (byte)_guiLogging);
#endif
_highestLoggingType = (LoggingType)currentHighest;
@ -86,7 +87,7 @@ namespace FishNet.Managing.Logging
if (!_initialized)
{
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
if (Application.isPlaying)
Debug.LogError("CanLog called before being initialized.");
else

View File

@ -23,8 +23,10 @@ using FishNet.Component.ColliderRollback;
using FishNet.Managing.Predicting;
using System.Runtime.CompilerServices;
using GameKit.Dependencies.Utilities;
#if UNITY_EDITOR
using FishNet.Editing.PrefabCollectionGenerator;
using UnityEditor;
#endif
namespace FishNet.Managing
@ -191,6 +193,10 @@ namespace FishNet.Managing
#region Const.
/// <summary>
/// Version of this release.
/// </summary>
public const string FISHNET_VERSION = "4.3.1";
/// <summary>
/// Maximum framerate allowed.
/// </summary>
internal const ushort MAXIMUM_FRAMERATE = 500;
@ -393,6 +399,19 @@ namespace FishNet.Managing
//If null and object is in a scene.
if (SpawnablePrefabs == null && !string.IsNullOrEmpty(gameObject.scene.name))
{
//First try to fetch the file, only if editor and not in play mode.
#if UNITY_EDITOR
if (!ApplicationState.IsPlaying())
{
SpawnablePrefabs = Generator.GetDefaultPrefabObjects();
if (SpawnablePrefabs != null)
{
Debug.Log($"SpawnablePrefabs was set to DefaultPrefabObjects automatically on object {gameObject.name} in scene {gameObject.scene.name}.");
EditorUtility.SetDirty(this);
return true;
}
}
#endif
//Always throw an error as this would cause failure.
if (print)
Debug.LogError($"SpawnablePrefabs is null on {gameObject.name}. Select the NetworkManager in scene {gameObject.scene.name} and choose a prefabs file. Choosing DefaultPrefabObjects will automatically populate prefabs for you.");
@ -455,13 +474,13 @@ namespace FishNet.Managing
if (value.TransportIndex == transportIndex)
{
cache.Add(kvp.Key);
value.Dispose();
value.ResetState();
}
}
//Not using transport index, no check required.
else
{
value.Dispose();
value.ResetState();
}
}

View File

@ -24,19 +24,19 @@ namespace FishNet.Managing.Object
protected void ReadTransformProperties(Reader reader, out Vector3? localPosition, out Quaternion? localRotation, out Vector3? localScale)
{
//Read changed.
ChangedTransformProperties ctp = (ChangedTransformProperties)reader.ReadByte();
TransformPropertiesFlag tpf = (TransformPropertiesFlag)reader.ReadByte();
//Position.
if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalPosition))
if (tpf.FastContains(TransformPropertiesFlag.Position))
localPosition = reader.ReadVector3();
else
localPosition = null;
//Rotation.
if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalRotation))
if (tpf.FastContains(TransformPropertiesFlag.Rotation))
localRotation = reader.ReadQuaternion(NetworkManager.ServerManager.SpawnPacking.Rotation);
else
localRotation = null;
//Scale.
if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalScale))
if (tpf.FastContains(TransformPropertiesFlag.LocalScale))
localScale = reader.ReadVector3();
else
localScale = null;
@ -213,28 +213,28 @@ namespace FishNet.Managing.Object
protected void WriteChangedTransformProperties(NetworkObject nob, bool sceneObject, bool nested, Writer headerWriter)
{
/* Write changed transform properties. */
ChangedTransformProperties ctp;
TransformPropertiesFlag tpf;
//If a scene object then get it from scene properties.
if (sceneObject || nested)
{
ctp = nob.GetTransformChanges(nob.SerializedTransformProperties);
tpf = nob.GetTransformChanges(nob.SerializedTransformProperties);
}
else
{
PrefabObjects po = NetworkManager.GetPrefabObjects<PrefabObjects>(nob.SpawnableCollectionId, false);
ctp = nob.GetTransformChanges(po.GetObject(true, nob.PrefabId).gameObject);
tpf = nob.GetTransformChanges(po.GetObject(true, nob.PrefabId).gameObject);
}
headerWriter.WriteByte((byte)ctp);
headerWriter.WriteByte((byte)tpf);
//If properties have changed.
if (ctp != ChangedTransformProperties.Unset)
if (tpf != TransformPropertiesFlag.Unset)
{
//Write any changed properties.
if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalPosition))
if (tpf.FastContains(TransformPropertiesFlag.Position))
headerWriter.WriteVector3(nob.transform.localPosition);
if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalRotation))
if (tpf.FastContains(TransformPropertiesFlag.Rotation))
headerWriter.WriteQuaternion(nob.transform.localRotation, NetworkManager.ServerManager.SpawnPacking.Rotation);
if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalScale))
if (tpf.FastContains(TransformPropertiesFlag.LocalScale))
headerWriter.WriteVector3(nob.transform.localScale);
}

View File

@ -1,4 +1,74 @@
#if UNITY_EDITOR
#if !PREDICTION_1
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace FishNet.Managing.Predicting.Editing
{
[CustomEditor(typeof(PredictionManager), true)]
[CanEditMultipleObjects]
public class PredictionManagerEditor : Editor
{
// private SerializedProperty _queuedInputs;
private SerializedProperty _dropExcessiveReplicates;
private SerializedProperty _maximumServerReplicates;
private SerializedProperty _maximumConsumeCount;
private SerializedProperty _stateInterpolation;
// private SerializedProperty _serverInterpolation;
protected virtual void OnEnable()
{
_dropExcessiveReplicates = serializedObject.FindProperty(nameof(_dropExcessiveReplicates));
_maximumServerReplicates = serializedObject.FindProperty(nameof(_maximumServerReplicates));
_maximumConsumeCount = serializedObject.FindProperty(nameof(_maximumConsumeCount));
_stateInterpolation = serializedObject.FindProperty(nameof(_stateInterpolation));
// _serverInterpolation = serializedObject.FindProperty(nameof(_serverInterpolation));
}
public override void OnInspectorGUI()
{
serializedObject.Update();
GUI.enabled = false;
EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((PredictionManager)target), typeof(PredictionManager), false);
GUI.enabled = true;
EditorGUILayout.LabelField("Client", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_stateInterpolation);
EditorGUI.indentLevel--;
EditorGUILayout.Space();
EditorGUILayout.LabelField("Server", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
// EditorGUILayout.PropertyField(_serverInterpolation);
EditorGUILayout.PropertyField(_dropExcessiveReplicates);
EditorGUI.indentLevel++;
if (_dropExcessiveReplicates.boolValue == true)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_maximumServerReplicates);
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
serializedObject.ApplyModifiedProperties();
}
}
}
#endif
#else
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
@ -13,21 +83,14 @@ namespace FishNet.Managing.Predicting.Editing
private SerializedProperty _queuedInputs;
private SerializedProperty _dropExcessiveReplicates;
private SerializedProperty _maximumServerReplicates;
private SerializedProperty _maximumConsumeCount;
private SerializedProperty _redundancyCount;
private SerializedProperty _allowPredictedSpawning;
private SerializedProperty _reservedObjectIds;
protected virtual void OnEnable()
{
_queuedInputs = serializedObject.FindProperty(nameof(_queuedInputs));
_dropExcessiveReplicates = serializedObject.FindProperty(nameof(_dropExcessiveReplicates));
_maximumServerReplicates = serializedObject.FindProperty(nameof(_maximumServerReplicates));
_maximumConsumeCount = serializedObject.FindProperty(nameof(_maximumConsumeCount));
_redundancyCount = serializedObject.FindProperty(nameof(_redundancyCount));
_allowPredictedSpawning = serializedObject.FindProperty(nameof(_allowPredictedSpawning));
_reservedObjectIds = serializedObject.FindProperty(nameof(_reservedObjectIds));
}
public override void OnInspectorGUI()
@ -41,16 +104,8 @@ namespace FishNet.Managing.Predicting.Editing
EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_queuedInputs);
EditorGUILayout.PropertyField(_redundancyCount);
EditorGUILayout.PropertyField(_allowPredictedSpawning);
if (_allowPredictedSpawning.boolValue == true)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_reservedObjectIds);
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(_queuedInputs);
EditorGUI.indentLevel--;
EditorGUILayout.Space();
@ -59,16 +114,7 @@ namespace FishNet.Managing.Predicting.Editing
EditorGUILayout.PropertyField(_dropExcessiveReplicates);
EditorGUI.indentLevel++;
if (_dropExcessiveReplicates.boolValue == true)
{
EditorGUILayout.PropertyField(_maximumServerReplicates);
}
else
{
#if PREDICTION_1
EditorGUILayout.PropertyField(_maximumConsumeCount);
#endif
}
EditorGUI.indentLevel--;
EditorGUI.indentLevel--;
@ -77,4 +123,7 @@ namespace FishNet.Managing.Predicting.Editing
}
}
#endif
#endif

View File

@ -9,7 +9,7 @@ using GameKit.Dependencies.Utilities;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
namespace FishNet.Managing.Predicting
{
@ -96,24 +96,24 @@ namespace FishNet.Managing.Predicting
#endregion
#region Serialized.
/// <summary>
///
/// </summary>
[Tooltip("Number of inputs to keep in queue for server and clients. " +
"Higher values will increase the likeliness of continous user created data to arrive successfully. " +
"Lower values will increase processing rate of received replicates. +" +
"This value cannot be higher than MaximumServerReplicates.")]
[Range(0, 15)]
[SerializeField]
private byte _queuedInputs = 1;
/// <summary>
/// Number of inputs to keep in queue for server and clients.
/// Higher values will increase the likeliness of continous user created data to arrive successfully.
/// Lower values will increase processing rate of received replicates.
/// This value cannot be higher than MaximumServerReplicates.
/// </summary>
//TODO: this is 0 until the rework on it is completed.
public byte QueuedInputs => 0;// _queuedInputs;
///// <summary>
/////
///// </summary>
//[Tooltip("Number of inputs to keep in queue for server and clients. " +
// "Higher values will increase the likeliness of continous user created data to arrive successfully. " +
// "Lower values will increase processing rate of received replicates. +" +
// "This value cannot be higher than MaximumServerReplicates.")]
//[Range(0, 15)]
//[SerializeField]
//private byte _queuedInputs = 1;
///// <summary>
///// Number of inputs to keep in queue for server and clients.
///// Higher values will increase the likeliness of continous user created data to arrive successfully.
///// Lower values will increase processing rate of received replicates.
///// This value cannot be higher than MaximumServerReplicates.
///// </summary>
////TODO: this is 0 until the rework on it is completed.
//public byte QueuedInputs => 0;// _queuedInputs;
/// <summary>
///
/// </summary>
@ -144,39 +144,37 @@ namespace FishNet.Managing.Predicting
_maximumServerReplicates = (byte)Mathf.Clamp(value, MINIMUM_REPLICATE_QUEUE_SIZE, MAXIMUM_REPLICATE_QUEUE_SIZE);
}
/// <summary>
/// Clients should store no more than 2 seconds worth of replicates.
/// No more than this value of replicates should be stored as a buffer.
/// </summary>
internal ushort MaximumClientReplicates => (ushort)(_networkManager.TimeManager.TickRate * 5);
internal ushort MaximumPastReplicates => (ushort)(_networkManager.TimeManager.TickRate * 5);
/// <summary>
///
/// </summary>
[Tooltip("Maximum number of past inputs which may send.")]
[Range(MINIMUM_PAST_INPUTS, MAXIMUM_PAST_INPUTS)]
[Tooltip("How many states to try and hold in a buffer before running them on clients. Larger values add resilience against network issues at the cost of running states later.")]
[Range(0, MAXIMUM_PAST_INPUTS)]
[FormerlySerializedAs("_redundancyCount")] //Remove on V5.
[FormerlySerializedAs("_interpolation")] //Remove on V5.
[SerializeField]
private byte _redundancyCount = 2;
private byte _stateInterpolation = 1;
/// <summary>
/// Maximum number of past inputs which may send and resend redundancy.
/// </summary>
internal byte RedundancyCount => _redundancyCount;
/// How many states to try and hold in a buffer before running them. Larger values add resilience against network issues at the cost of running states later.
/// </summary>
internal byte StateInterpolation => _stateInterpolation;
/// <summary>
/// True to allow clients to use predicted spawning. While true, each NetworkObject prefab you wish to predicted spawn must be marked as to allow this feature.
/// Number of past inputs to send, which is also the number of times to resend final datas.
/// </summary>
internal bool GetAllowPredictedSpawning() => _allowPredictedSpawning;
[Tooltip("True to allow clients to use predicted spawning and despawning. While true, each NetworkObject prefab you wish to predicted spawn must be marked as to allow this feature.")]
[SerializeField]
private bool _allowPredictedSpawning = false;
/// <summary>
///
/// </summary>
[Tooltip("Maximum number of Ids to reserve on clients for predicted spawning. Higher values will allow clients to send more predicted spawns per second but may reduce availability of ObjectIds with high player counts.")]
[Range(1, 100)]
[SerializeField]
private byte _reservedObjectIds = 15;
/// <summary>
/// Maximum number of Ids to reserve on clients for predicted spawning. Higher values will allow clients to send more predicted spawns per second but may reduce availability of ObjectIds with high player counts.
/// </summary>
/// <returns></returns>
internal byte GetReservedObjectIds() => _reservedObjectIds;
internal byte RedundancyCount => (byte)(_stateInterpolation + 1);
///// <summary>
/////
///// </summary>
//[Tooltip("How many states to try and hold in a buffer before running them on server. Larger values add resilience against network issues at the cost of running states later.")]
//[Range(0, MAXIMUM_PAST_INPUTS + 30)]
//[SerializeField]
//private byte _serverInterpolation = 1;
///// <summary>
///// How many states to try and hold in a buffer before running them on server. Larger values add resilience against network issues at the cost of running states later.
///// </summary>
//internal byte ServerInterpolation => _serverInterpolation;
#endregion
#region Private.
@ -228,7 +226,7 @@ namespace FishNet.Managing.Predicting
internal void InitializeOnce(NetworkManager manager)
{
_networkManager = manager;
ClampQueuedInputs();
ClampInterpolation();
_networkManager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState;
}
@ -238,30 +236,27 @@ namespace FishNet.Managing.Predicting
private void ClientManager_OnClientConnectionState(ClientConnectionStateArgs obj)
{
_droppedReconcilesCount = 0;
_lastOrderedReadReconcileTick = 0;
}
/// <summary>
/// Amount to reserve for the header of a state update.
/// 2 PacketId.
/// 4 Last replicate tick run for connection.
/// 4 Length unpacked.
/// </summary>
internal const int STATE_HEADER_RESERVE_COUNT = 10;
internal const int STATE_HEADER_RESERVE_LENGTH = (TransportManager.PACKETID_LENGTH + TransportManager.UNPACKED_TICK_LENGTH + TransportManager.UNPACKED_SIZE_LENGTH);
/// <summary>
/// Clamps queued inputs to a valid value.
/// </summary>
private void ClampQueuedInputs()
private void ClampInterpolation()
{
ushort startingValue = _queuedInputs;
ushort startingValue = _stateInterpolation;
//Check for setting if dropping.
if (_dropExcessiveReplicates && _queuedInputs > _maximumServerReplicates)
_queuedInputs = (byte)(_maximumServerReplicates - 1);
if (_dropExcessiveReplicates && _stateInterpolation > _maximumServerReplicates)
_stateInterpolation = (byte)(_maximumServerReplicates - 1);
//If changed.
if (_queuedInputs != startingValue)
_networkManager.Log($"QueuedInputs has been set to {_queuedInputs}.");
if (_stateInterpolation != startingValue)
_networkManager.Log($"Interpolation has been set to {_stateInterpolation}.");
}
internal class StatePacket : IResettable
@ -321,205 +316,174 @@ namespace FishNet.Managing.Predicting
TimeManager tm = _networkManager.TimeManager;
uint localTick = tm.LocalTick;
uint estimatedLastRemoteTick = tm.LastPacketTick.Value();
//NOTESSTART
/* Don't run a reconcile unless it's possible for ticks queued
* that tick to be run already. Otherwise you are not replaying inputs
* at all, just snapping to corrections. This means states which arrive late or out of order
* will be ignored since they're before the reconcile, which means important actions
* could have gone missed.
*
* A system which synchronized all current states rather than what's only needed to correct
* the inputs would likely solve this. */
//NOTESEND
/* Only use the latest reconcile which passes the conditions to run.
* This will drop any excessive reconciles which built up from latency. */
StatePacket sp = null;
/* If here then 'peeked' has met conditions.
* Check if the next state also meets, if so then
* skip ahead to the next state. */
/* 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.
* Limit the number of states consumed per tick so the clients
* computer doesn't catch fire. */
int iterations = 0;
while (_reconcileStates.Count > 0)
{
//If next matches then set peeked to new.
if (ConditionsMet(_reconcileStates.Peek()))
{
//Since this is being replaced, reset state first.
if (sp != null)
DisposeOfStatePacket(sp);
sp = _reconcileStates.Dequeue();
break;
}
/* Conditions are not met on the next one, exit loop.
* This will use the latest peeked. */
iterations++;
/* Typically there should only be 'interpolation' amount in queue but
* there can be more if the clients network is unstable and they are
* arriving in burst.
* If there's more than interpolation (+1 for as a leniency buffer) then begin to
* consume multiple. */
byte stateInterpolation = StateInterpolation;
int maxIterations = (_reconcileStates.Count > (stateInterpolation + 1)) ? 2 : 1;
//At most 2 iterations.
if (iterations > maxIterations)
return;
StatePacket sp;
if (!ConditionsMet(_reconcileStates.Peek()))
return;
else
{
break;
}
sp = _reconcileStates.Dequeue();
//Condition met. See if the next one matches condition, if so drop current.
//Returns if a state has it's conditions met.
bool ConditionsMet(StatePacket spChecked)
{
return ((spChecked != null) && (spChecked.ServerTick <= (estimatedLastRemoteTick - QueuedInputs - RedundancyCount - 1)) && spChecked.ClientTick < (localTick - QueuedInputs));
if (spChecked == null)
return false;
bool serverPass = (spChecked.ServerTick <= (estimatedLastRemoteTick - stateInterpolation));
bool clientPass = spChecked.ClientTick < (localTick - stateInterpolation);
return (serverPass && clientPass);
}
}
//If state is not valid then it was never set, thus condition is not met.
if (sp == null)
return;
//StatePacket sp = _reconcileStates.Dequeue();
bool dropReconcile = false;
bool dropReconcile = false;
uint clientTick = sp.ClientTick;
uint serverTick = sp.ServerTick;
uint clientTick = sp.ClientTick;
uint serverTick = sp.ServerTick;
//uint ticksDifference = (localTick - clientTick);
////Target ticks are based on QueuedInputs, redundancy count, and latency. An extra bit is added as a buffer for variance.
//uint varianceAllowance = tm.TimeToTicks(0.2f, TickRounding.RoundUp);
//uint targetTicks = (varianceAllowance + (uint)QueuedInputs + (uint)RedundancyCount + tm.TimeToTicks((double)((double)tm.RoundTripTime / 1000d), TickRounding.RoundDown));
//long ticksOverTarget = (long)ticksDifference - (long)targetTicks;
////ReduceClientTiming = (ticksOverTarget > 0);
///* If the reconcile is behind more ticks than hoped then slow
// * down the client simulation so it ticks very slightly
// * slower allowing fewer replays. This typically is only required after
// * the player encounters a sudden ping drop, such as a spike in latency,
// * then ping returns to norrmal. */
//if (ticksOverTarget > 0)
//{
/* If client has a low frame rate
* then limit the number of reconciles to prevent further performance loss. */
if (_networkManager.TimeManager.LowFrameRate)
{
/* Limit 3 drops a second. DropValue will be roughly the same
* as every 330ms. */
int reconcileValue = Mathf.Max(1, (_networkManager.TimeManager.TickRate / 3));
//If cannot drop then reset dropcount.
if (_droppedReconcilesCount >= reconcileValue)
/* If client has a low frame rate
* then limit the number of reconciles to prevent further performance loss. */
if (_networkManager.TimeManager.LowFrameRate)
{
/* Limit 3 drops a second. DropValue will be roughly the same
* as every 330ms. */
int reconcileValue = Mathf.Max(1, (_networkManager.TimeManager.TickRate / 3));
//If cannot drop then reset dropcount.
if (_droppedReconcilesCount >= reconcileValue)
{
_droppedReconcilesCount = 0;
}
//If can drop...
else
{
dropReconcile = true;
_droppedReconcilesCount++;
}
}
//}
//No reason to believe client is struggling, allow reconcile.
else
{
_droppedReconcilesCount = 0;
}
//If can drop...
else
if (!dropReconcile)
{
dropReconcile = true;
_droppedReconcilesCount++;
}
}
//}
//No reason to believe client is struggling, allow reconcile.
else
{
_droppedReconcilesCount = 0;
}
IsReconciling = true;
if (!dropReconcile)
{
IsReconciling = true;
ClientStateTick = clientTick;
/* This is the tick which the reconcile is for.
* Since reconciles are performed after replicate, if
* the replicate was on tick 100 then this reconcile is the state
* on tick 100, after the replicate is performed. */
ServerStateTick = serverTick;
ClientStateTick = clientTick;
/* This is the tick which the reconcile is for.
* Since reconciles are performed after replicate, if
* the replicate was on tick 100 then this reconcile is the state
* on tick 100, after the replicate is performed. */
ServerStateTick = serverTick;
//Have the reader get processed.
foreach (StatePacket.IncomingData item in sp.Datas)
{
PooledReader reader = ReaderPool.Retrieve(item.Data, _networkManager, Reader.DataSource.Server);
_networkManager.ClientManager.ParseReader(reader, item.Channel);
ReaderPool.Store(reader);
}
//Have the reader get processed.
foreach (StatePacket.IncomingData item in sp.Datas)
{
PooledReader reader = ReaderPool.Retrieve(item.Data, _networkManager, Reader.DataSource.Server);
_networkManager.ClientManager.ParseReader(reader, item.Channel);
ReaderPool.Store(reader);
}
bool timeManagerPhysics = (tm.PhysicsMode == PhysicsMode.TimeManager);
float tickDelta = ((float)tm.TickDelta * _networkManager.TimeManager.GetPhysicsTimeScale());
bool timeManagerPhysics = (tm.PhysicsMode == PhysicsMode.TimeManager);
float tickDelta = (float)tm.TickDelta;
OnPreReconcile?.Invoke(ClientStateTick, ServerStateTick);
OnReconcile?.Invoke(ClientStateTick, ServerStateTick);
OnPreReconcile?.Invoke(ClientStateTick, ServerStateTick);
OnReconcile?.Invoke(ClientStateTick, ServerStateTick);
if (timeManagerPhysics)
{
OnPrePhysicsTransformSync?.Invoke(ClientStateTick, ServerStateTick);
Physics.SyncTransforms();
Physics2D.SyncTransforms();
OnPostPhysicsTransformSync?.Invoke(ClientStateTick, ServerStateTick);
}
/* Set first replicate to be the 1 tick
* after reconcile. This is because reconcile calcs
* 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.
*
* Replay up to localtick, excluding localtick. There will
* be no input for localtick since reconcile runs before
* OnTick. */
ClientReplayTick = ClientStateTick;
ServerReplayTick = ServerStateTick;
int replays = 0;
/* Only replay up to this tick excluding queuedInputs.
* This will prevent the client from replaying into
* it's authorative/owned inputs which have not run
* yet.
*
* An additional value is subtracted to prevent
* client from running 1 local tick into the future
* since the OnTick has not run yet. */
while (ClientReplayTick < localTick - 1)
{
replays++;
OnPreReplicateReplay?.Invoke(ClientReplayTick, ServerReplayTick);
OnReplicateReplay?.Invoke(ClientReplayTick, ServerReplayTick);
if (timeManagerPhysics)
{
Physics.Simulate(tickDelta);
Physics2D.Simulate(tickDelta);
OnPrePhysicsTransformSync?.Invoke(ClientStateTick, ServerStateTick);
Physics.SyncTransforms();
Physics2D.SyncTransforms();
OnPostPhysicsTransformSync?.Invoke(ClientStateTick, ServerStateTick);
}
OnPostReplicateReplay?.Invoke(ClientReplayTick, ServerReplayTick);
ClientReplayTick++;
ServerReplayTick++;
/* Set first replicate to be the 1 tick
* after reconcile. This is because reconcile calcs
* 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.
*
* Replay up to localtick, excluding localtick. There will
* be no input for localtick since reconcile runs before
* OnTick. */
ClientReplayTick = ClientStateTick;
ServerReplayTick = ServerStateTick;
/* Only replay up to this tick excluding queuedInputs.
* This will prevent the client from replaying into
* it's authorative/owned inputs which have not run
* yet.
*
* An additional value is subtracted to prevent
* client from running 1 local tick into the future
* since the OnTick has not run yet. */
while (ClientReplayTick < localTick - 1)
{
OnPreReplicateReplay?.Invoke(ClientReplayTick, ServerReplayTick);
OnReplicateReplay?.Invoke(ClientReplayTick, ServerReplayTick);
if (timeManagerPhysics)
{
Physics.Simulate(tickDelta);
Physics2D.Simulate(tickDelta);
}
OnPostReplicateReplay?.Invoke(ClientReplayTick, ServerReplayTick);
ClientReplayTick++;
ServerReplayTick++;
}
OnPostReconcile?.Invoke(ClientStateTick, ServerStateTick);
ClientStateTick = TimeManager.UNSET_TICK;
ServerStateTick = TimeManager.UNSET_TICK;
ClientReplayTick = TimeManager.UNSET_TICK;
ServerReplayTick = TimeManager.UNSET_TICK;
IsReconciling = false;
}
OnPostReconcile?.Invoke(ClientStateTick, ServerStateTick);
ClientStateTick = TimeManager.UNSET_TICK;
ServerStateTick = TimeManager.UNSET_TICK;
ClientReplayTick = TimeManager.UNSET_TICK;
ServerReplayTick = TimeManager.UNSET_TICK;
IsReconciling = false;
DisposeOfStatePacket(sp);
}
DisposeOfStatePacket(sp);
}
/// <summary>
/// Sends written states for clients.
/// </summary>
internal void SendStateUpdate()
{
byte stateInterpolation = StateInterpolation;
uint recentReplicateToTicks = _networkManager.TimeManager.TimeToTicks(0.25f, TickRounding.RoundUp);
TransportManager tm = _networkManager.TransportManager;
foreach (NetworkConnection nc in _networkManager.ServerManager.Clients.Values)
{
uint lastReplicateTick;
//If client has performed a replicate.
if (!nc.ReplicateTick.IsUnset)
{
/* If it's been longer than queued inputs since
* server has received a replicate then
* use estimated value. Otherwise use LastRemoteTick. */
if (nc.ReplicateTick.LocalTickDifference(_networkManager.TimeManager) > QueuedInputs)
lastReplicateTick = nc.ReplicateTick.Value();
else
lastReplicateTick = nc.ReplicateTick.LastRemoteTick;
}
//If client has performed a replicate recently.
if (!nc.ReplicateTick.IsUnset && nc.ReplicateTick.LocalTickDifference(_networkManager.TimeManager) < recentReplicateToTicks)
lastReplicateTick = nc.ReplicateTick.Value();
/* If not then use what is estimated to be the clients
* current tick along with desired prediction queue count.
* This should be just about the same as if the client used replicate,
* but even if it's not it doesn't matter because the client
* isn't replicating himself, just reconciling and replaying other objects. */
else
{
lastReplicateTick = (nc.PacketTick.Value() + QueuedInputs);
}
lastReplicateTick = nc.LocalTick.Value() - stateInterpolation;
foreach (PooledWriter writer in nc.PredictionStateWriters)
{
@ -536,7 +500,7 @@ namespace FishNet.Managing.Predicting
* the reserve count of the header. The header reserve
* count will always be the same so that can be parsed
* off immediately upon receiving. */
int dataLength = (segment.Count - STATE_HEADER_RESERVE_COUNT);
int dataLength = (segment.Count - STATE_HEADER_RESERVE_LENGTH);
//Write length.
writer.WriteInt32(dataLength, AutoPackType.Unpacked);
//Channel is defaulted to unreliable.
@ -576,7 +540,7 @@ namespace FishNet.Managing.Predicting
* 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 maxAllowedStates = Mathf.Max(QueuedInputs * 4, 4);
int maxAllowedStates = Mathf.Max(StateInterpolation * 4, 4);
while (_reconcileStates.Count > maxAllowedStates)
{
StatePacket oldSp = _reconcileStates.Dequeue();
@ -622,7 +586,7 @@ namespace FishNet.Managing.Predicting
#if UNITY_EDITOR
private void OnValidate()
{
ClampQueuedInputs();
ClampInterpolation();
}
#endif
@ -772,28 +736,9 @@ namespace FishNet.Managing.Predicting
[SerializeField]
private byte _redundancyCount = 2;
/// <summary>
/// Maximum number of past inputs which may send and resend redundancy.
/// Maximum number of past inputs which may send.
/// </summary>
internal byte RedundancyCount => _redundancyCount;
/// <summary>
/// True to allow clients to use predicted spawning. While true, each NetworkObject prefab you wish to predicted spawn must be marked as to allow this feature.
/// </summary>
internal bool GetAllowPredictedSpawning() => _allowPredictedSpawning;
[Tooltip("True to allow clients to use predicted spawning and despawning. While true, each NetworkObject prefab you wish to predicted spawn must be marked as to allow this feature.")]
[SerializeField]
private bool _allowPredictedSpawning = false;
/// <summary>
///
/// </summary>
[Tooltip("Maximum number of Ids to reserve on clients for predicted spawning. Higher values will allow clients to send more predicted spawns per second but may reduce availability of ObjectIds with high player counts.")]
[Range(1, 100)]
[SerializeField]
private byte _reservedObjectIds = 15;
/// <summary>
/// Maximum number of Ids to reserve on clients for predicted spawning. Higher values will allow clients to send more predicted spawns per second but may reduce availability of ObjectIds with high player counts.
/// </summary>
/// <returns></returns>
internal byte GetReservedObjectIds() => _reservedObjectIds;
#endregion
#region Private.

View File

@ -782,7 +782,7 @@ namespace FishNet.Managing.Scened
return WarnAndReturnFalse($"NetworkObject {nob.name} cannot be moved because it is not the root object. Unity can only move root objects between scenes.");
//In DDOL and IsGlobal.
if (nob.IsGlobal && (nob.gameObject.scene.name == DDOL.GetDDOL().gameObject.scene.name))
return WarnAndReturnFalse("NetworkObject {nob.name} cannot be moved because it is global. Global objects must remain in the DontDestroyOnLoad scene.");
return WarnAndReturnFalse($"NetworkObject {nob.name} cannot be moved because it is global. Global objects must remain in the DontDestroyOnLoad scene.");
//Fall through success.
return true;

View File

@ -19,6 +19,8 @@ namespace FishNet.Managing.Server.Editing
private SerializedProperty _frameRate;
private SerializedProperty _shareIds;
private SerializedProperty _startOnHeadless;
private SerializedProperty _allowPredictedSpawning;
private SerializedProperty _reservedObjectIds;
protected virtual void OnEnable()
{
@ -31,6 +33,9 @@ namespace FishNet.Managing.Server.Editing
_frameRate = serializedObject.FindProperty(nameof(_frameRate));
_shareIds = serializedObject.FindProperty(nameof(_shareIds));
_startOnHeadless = serializedObject.FindProperty(nameof(_startOnHeadless));
_allowPredictedSpawning = serializedObject.FindProperty(nameof(_allowPredictedSpawning));
_reservedObjectIds = serializedObject.FindProperty(nameof(_reservedObjectIds));
}
public override void OnInspectorGUI()
@ -43,15 +48,6 @@ namespace FishNet.Managing.Server.Editing
EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_authenticator);
EditorGUILayout.PropertyField(_remoteClientTimeout);
if ((RemoteTimeoutType)_remoteClientTimeout.intValue != RemoteTimeoutType.Disabled)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_remoteClientTimeoutDuration,new GUIContent("Timeout"));
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(_syncTypeRate);
EditorGUILayout.PropertyField(SpawnPacking);
EditorGUILayout.PropertyField(_changeFrameRate);
@ -61,11 +57,36 @@ namespace FishNet.Managing.Server.Editing
EditorGUILayout.PropertyField(_frameRate);
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(_shareIds);
EditorGUILayout.PropertyField(_startOnHeadless);
EditorGUI.indentLevel--;
EditorGUILayout.Space();
EditorGUILayout.LabelField("Connections", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_remoteClientTimeout);
if ((RemoteTimeoutType)_remoteClientTimeout.intValue != RemoteTimeoutType.Disabled)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_remoteClientTimeoutDuration,new GUIContent("Timeout"));
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(_shareIds);
EditorGUI.indentLevel--;
EditorGUILayout.Space();
EditorGUILayout.LabelField("Security", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_authenticator);
EditorGUILayout.PropertyField(_allowPredictedSpawning);
if (_allowPredictedSpawning.boolValue == true)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_reservedObjectIds);
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
EditorGUILayout.Space();
serializedObject.ApplyModifiedProperties();
}

View File

@ -251,7 +251,7 @@ namespace FishNet.Managing.Server
/* Only shuffle when NOT in editor and not
* development build.
* Debugging could be easier when Ids are ordered. */
#if !UNITY_EDITOR && !DEVELOPMENT_BUILD
#if !DEVELOPMENT
shuffledCache.Shuffle();
#endif
//Add shuffled to objectIdCache.
@ -458,7 +458,7 @@ namespace FishNet.Managing.Server
return;
}
//Server has predicted spawning disabled.
if (!NetworkManager.PredictionManager.GetAllowPredictedSpawning())
if (!NetworkManager.ServerManager.GetAllowPredictedSpawning())
{
base.NetworkManager.LogWarning("Cannot spawn object because server is not active and predicted spawning is not enabled.");
return;
@ -809,7 +809,7 @@ namespace FishNet.Managing.Server
return;
}
//Server has predicted spawning disabled.
if (!NetworkManager.PredictionManager.GetAllowPredictedSpawning())
if (!NetworkManager.ServerManager.GetAllowPredictedSpawning())
{
base.NetworkManager.LogWarning("Cannot despawn object because server is not active and predicted spawning is not enabled.");
return;

View File

@ -1,4 +1,7 @@
using FishNet.Authenticating;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#define DEVELOPMENT
#endif
using FishNet.Authenticating;
using FishNet.Component.Observing;
using FishNet.Connection;
using FishNet.Managing.Debugging;
@ -102,6 +105,26 @@ namespace FishNet.Managing.Server
_remoteClientTimeoutDuration = duration;
}
/// <summary>
/// True to allow clients to use predicted spawning. While true, each NetworkObject you wish this feature to apply towards must have a PredictedSpawn component.
/// Predicted spawns can have custom validation on the server.
/// </summary>
internal bool GetAllowPredictedSpawning() => _allowPredictedSpawning;
[Tooltip("True to allow clients to use predicted spawning. While true, each NetworkObject you wish this feature to apply towards must have a PredictedSpawn component. Predicted spawns can have custom validation on the server.")]
[SerializeField]
private bool _allowPredictedSpawning = false;
/// <summary>
///
/// </summary>
[Tooltip("Maximum number of Ids to reserve on clients for predicted spawning. Higher values will allow clients to send more predicted spawns per second but may reduce availability of ObjectIds with high player counts.")]
[Range(1, 100)]
[SerializeField]
private byte _reservedObjectIds = 15;
/// <summary>
/// Maximum number of Ids to reserve on clients for predicted spawning. Higher values will allow clients to send more predicted spawns per second but may reduce availability of ObjectIds with high player counts.
/// </summary>
/// <returns></returns>
internal byte GetReservedObjectIds() => _reservedObjectIds;
/// <summary>
/// Default send rate for SyncTypes. A value of 0f will send changed values every tick.
/// SyncTypeRate cannot yet be changed at runtime because this would require recalculating rates on SyncBase, which is not yet implemented.
/// </summary>
@ -180,7 +203,7 @@ namespace FishNet.Managing.Server
/// Used to read splits.
/// </summary>
private SplitReader _splitReader = new SplitReader();
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
/// <summary>
/// Logs data about parser to help debug.
/// </summary>
@ -329,7 +352,7 @@ namespace FishNet.Managing.Server
{
if (_remoteClientTimeout == RemoteTimeoutType.Disabled)
return;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
//If development but not set to development return.
else if (_remoteClientTimeout != RemoteTimeoutType.Development)
return;
@ -505,6 +528,54 @@ namespace FishNet.Managing.Server
OnServerConnectionState?.Invoke(args);
}
/// <summary>
/// Checks to make sure the client is on the same version.
/// This is to help developers make sure their builds are on the same FishNet version.
/// </summary>
private void ParseVersion(PooledReader reader, NetworkConnection conn, int transportId)
{
//Cannot be authenticated if havent sent version yet. This is a duplicate version send, likely exploit attempt.
if (conn.HasSentVersion)
{
conn.Kick(reader, KickReason.ExploitAttempt, LoggingType.Common, $"Connection {conn.ToString()} has sent their FishNet version after being authenticated; this is not possible under normal conditions.");
return;
}
conn.HasSentVersion = true;
string version = reader.ReadString();
//Version match.
if (version == NetworkManager.FISHNET_VERSION)
{
/* Send to client if server is in development build or not.
* This is to allow the client to utilize some features/information
* received from the server only when it's in dev mode. */
bool isDevelopmentBuild;
#if DEVELOPMENT
isDevelopmentBuild = true;
#else
isDevelopmentBuild = false;
#endif
PooledWriter writer = WriterPool.Retrieve();
writer.WritePacketId(PacketId.Version);
writer.WriteBoolean(isDevelopmentBuild);
conn.SendToClient((byte)Channel.Reliable, writer.GetArraySegment());
WriterPool.Store(writer);
/* If there is an authenticator
* and the transport is not a local transport. */
Authenticator auth = GetAuthenticator();
if (auth != null && !NetworkManager.TransportManager.IsLocalTransport(transportId))
auth.OnRemoteConnection(conn);
else
ClientAuthenticated(conn);
}
else
{
conn.Kick(reader, KickReason.UnexpectedProblem, LoggingType.Warning, $"Connection {conn.ToString()} has been kicked for being on FishNet version {version}. Server version is {NetworkManager.FISHNET_VERSION}.");
}
}
/// <summary>
/// Called when a connection state changes for a remote client.
/// </summary>
@ -512,10 +583,9 @@ namespace FishNet.Managing.Server
{
//Sanity check to make sure transports are following proper types/ranges.
int id = args.ConnectionId;
int maxIdValue = short.MaxValue;
if (id < 0 || id > maxIdValue)
if (id < 0 || id > NetworkConnection.MAXIMUM_CLIENTID_VALUE)
{
Kick(args.ConnectionId, KickReason.UnexpectedProblem, LoggingType.Error, $"The transport you are using supplied an invalid connection Id of {id}. Connection Id values must range between 0 and {maxIdValue}. The client has been disconnected.");
Kick(args.ConnectionId, KickReason.UnexpectedProblem, LoggingType.Error, $"The transport you are using supplied an invalid connection Id of {id}. Connection Id values must range between 0 and {NetworkConnection.MAXIMUM_CLIENTID_VALUE}. The client has been disconnected.");
return;
}
//Valid Id.
@ -528,16 +598,8 @@ namespace FishNet.Managing.Server
NetworkConnection conn = new NetworkConnection(NetworkManager, id, args.TransportIndex, true);
Clients.Add(args.ConnectionId, conn);
OnRemoteConnectionState?.Invoke(conn, args);
//Connection is no longer valid. This can occur if the user changes the state using the OnRemoteConnectionState event.
if (!conn.IsValid)
return;
/* If there is an authenticator
* and the transport is not a local transport. */
Authenticator auth = GetAuthenticator();
if (auth != null && !NetworkManager.TransportManager.IsLocalTransport(id))
auth.OnRemoteConnection(conn);
else
ClientAuthenticated(conn);
//Do nothing else until the client sends it's version.
}
//If stopping.
else if (args.ConnectionState == RemoteConnectionState.Stopped)
@ -556,7 +618,7 @@ namespace FishNet.Managing.Server
while (pqId.Count > 0)
Objects.CacheObjectId(pqId.Dequeue());
conn.Dispose();
conn.ResetState();
NetworkManager.Log($"Remote connection stopped for Id {id}.");
}
}
@ -576,9 +638,9 @@ namespace FishNet.Managing.Server
* reserved objectIds. */
;
PredictionManager pm = NetworkManager.PredictionManager;
if (pm.GetAllowPredictedSpawning())
if (GetAllowPredictedSpawning())
{
int count = Mathf.Min(Objects.GetObjectIdCache().Count, pm.GetReservedObjectIds());
int count = Mathf.Min(Objects.GetObjectIdCache().Count, GetReservedObjectIds());
writer.WriteByte((byte)count);
for (int i = 0; i < count; i++)
@ -606,7 +668,7 @@ namespace FishNet.Managing.Server
/// <param name="args"></param>
private void ParseReceived(ServerReceivedDataArgs args)
{
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
_parseLogger.Reset();
#endif
@ -621,7 +683,7 @@ namespace FishNet.Managing.Server
segment = args.Data;
NetworkManager.StatisticsManager.NetworkTraffic.LocalServerReceivedData((ulong)segment.Count);
if (segment.Count <= TransportManager.TICK_BYTES)
if (segment.Count <= TransportManager.UNPACKED_TICK_LENGTH)
return;
//FishNet internally splits packets so nothing should ever arrive over MTU.
@ -633,17 +695,19 @@ namespace FishNet.Managing.Server
return;
}
TimeManager timeManager = NetworkManager.TimeManager;
bool hasIntermediateLayer = NetworkManager.TransportManager.HasIntermediateLayer;
PacketId packetId = PacketId.Unset;
PooledReader reader = null;
#if !UNITY_EDITOR && !DEVELOPMENT_BUILD
#if !DEVELOPMENT
try
{
#endif
Reader.DataSource dataSource = Reader.DataSource.Client;
reader = ReaderPool.Retrieve(segment, NetworkManager, dataSource);
uint tick = reader.ReadTickUnpacked();
NetworkManager.TimeManager.LastPacketTick.Update(tick);
timeManager.LastPacketTick.Update(tick);
/* This is a special condition where a message may arrive split.
* When this occurs buffer each packet until all packets are
* received. */
@ -654,8 +718,8 @@ namespace FishNet.Managing.Server
int expectedMessages;
_splitReader.GetHeader(reader, out expectedMessages);
//If here split message can be written.
_splitReader.Write(NetworkManager.TimeManager.LastPacketTick.LastRemoteTick, reader, expectedMessages);
//If here split message is to be read into splitReader.
_splitReader.Write(tick, reader, expectedMessages);
/* If fullMessage returns 0 count then the split
* has not written fully yet. Otherwise, if there is
@ -681,7 +745,7 @@ namespace FishNet.Managing.Server
while (reader.Remaining > 0)
{
packetId = reader.ReadPacketId();
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
_parseLogger.AddPacket(packetId);
#endif
NetworkConnection conn;
@ -693,20 +757,20 @@ namespace FishNet.Managing.Server
Kick(args.ConnectionId, KickReason.UnexpectedProblem, LoggingType.Error, $"ConnectionId {args.ConnectionId} not found within Clients. Connection will be kicked immediately.");
return;
}
conn.TryUpdateLocalTick(tick);
conn.PacketTick.Update(NetworkManager.TimeManager, tick, Timing.EstimatedTick.OldTickOption.SetLastRemoteTick);
conn.LocalTick.Update(timeManager, tick, EstimatedTick.OldTickOption.Discard);
conn.PacketTick.Update(timeManager, tick, EstimatedTick.OldTickOption.SetLastRemoteTick);
/* If connection isn't authenticated and isn't a broadcast
* then disconnect client. If a broadcast then process
* normally; client may still become disconnected if the broadcast
* does not allow to be called while not authenticated. */
if (!conn.IsAuthenticated && packetId != PacketId.Broadcast)
if (!conn.IsAuthenticated && packetId != PacketId.Version && packetId != PacketId.Broadcast)
{
conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} sent a Broadcast without being authenticated. Connection will be kicked immediately.");
conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} sent packetId {packetId} without being authenticated. Connection will be kicked immediately.");
return;
}
//Only check if not developer build because users pay pause editor.
#if !DEVELOPMENT_BUILD && !UNITY_EDITOR
#if !DEVELOPMENT
/* If hasn't sent LOD recently enough. LODs are sent every half a second, so
* by multiplaying interval by 60 this gives the client a 30 second window. */
if (_cachedUseLod && conn.IsLateForLevelOfDetail(_cachedLevelOfDetailInterval * 60))
@ -725,7 +789,7 @@ namespace FishNet.Managing.Server
}
else if (packetId == PacketId.ObjectSpawn)
{
if (!NetworkManager.PredictionManager.GetAllowPredictedSpawning())
if (!GetAllowPredictedSpawning())
{
conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} sent a predicted spawn while predicted spawning is not enabled. Connection will be kicked immediately.");
return;
@ -734,7 +798,7 @@ namespace FishNet.Managing.Server
}
else if (packetId == PacketId.ObjectDespawn)
{
if (!NetworkManager.PredictionManager.GetAllowPredictedSpawning())
if (!GetAllowPredictedSpawning())
{
conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} sent a predicted spawn while predicted spawning is not enabled. Connection will be kicked immediately.");
return;
@ -753,9 +817,13 @@ namespace FishNet.Managing.Server
{
ParsePingPong(reader, conn);
}
else if (packetId == PacketId.Version)
{
ParseVersion(reader, conn, args.TransportIndex);
}
else
{
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
NetworkManager.LogError($"Server received an unhandled PacketId of {(ushort)packetId} on channel {args.Channel} from connectionId {args.ConnectionId}. Remaining data has been purged.");
_parseLogger.Print(NetworkManager);
#else
@ -765,7 +833,7 @@ namespace FishNet.Managing.Server
return;
}
}
#if !UNITY_EDITOR && !DEVELOPMENT_BUILD
#if !DEVELOPMENT
}
catch (Exception e)
{
@ -831,6 +899,9 @@ namespace FishNet.Managing.Server
/// </summary>
private void BroadcastClientConnectionChange(bool connected, NetworkConnection conn)
{
//Only send if the connection was authenticated.
if (!conn.IsAuthenticated)
return;
//If sharing Ids then send all connected client Ids first if is a connected state.
if (ShareIds)
{

View File

@ -60,9 +60,11 @@ namespace FishNet.Managing.Timing.Editing
//Physics.
EditorGUILayout.LabelField("Physics", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
if (_physicsMode.intValue == (int)FishNet.Managing.Timing.PhysicsMode.TimeManager)
EditorGUILayout.HelpBox($"Time.fixedDeltaTime will be overriden with TimeManager.TickDelta ({(1f / (float)_tickRate.intValue).ToString("0.###")})", MessageType.Info);
else
EditorGUILayout.HelpBox("If you are using physics interactions be sure to change the PhysicsMode to TimeManager and implement physics within the TimeManager tick events. NetworkTransform may also jitter when not using PhysicsMode.TimeManager.", MessageType.Warning);
EditorGUILayout.PropertyField(_physicsMode);
if (_physicsMode.intValue != (int)FishNet.Managing.Timing.PhysicsMode.TimeManager)
EditorGUILayout.HelpBox("If you are using physics interactions be sure to change the PhysicsMode to TimeManager and implement physics within the TimeManager tick events.", MessageType.None);
EditorGUI.indentLevel--;
////Prediction.

View File

@ -1,12 +1,6 @@
using FishNet.Connection;
using FishNet.Documenting;
using FishNet.Managing.Transporting;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Serializing.Helping;
using FishNet.Transporting;
using FishNet.Utility;
using FishNet.Utility.Extension;
using GameKit.Dependencies.Utilities;
using System;
using System.Runtime.CompilerServices;
@ -47,63 +41,6 @@ namespace FishNet.Managing.Timing
BeforeTick = 0,
AfterTick = 1,
}
/// <summary>
/// Synchronizes tick timing between server and client.
/// </summary>
private class TimingSync
{
/// <summary>
/// Last server tick passed in.
/// </summary>
private uint _lastServerTick;
/// <summary>
/// Last local tick passed in.
/// </summary>
private uint _lastLocalTick;
/// <summary>
/// Last difference between server and client.
/// </summary>
private long? _lastServerClientDifference;
/// <summary>
/// Sets differences between last server and local tick.
/// </summary>
/// <returns>True if values exist, false if unable to process.</returns>
public bool GetTickDifference(uint currentServerTick, uint currentLocalTick, out long tickDifference)
{
if (currentServerTick < _lastServerTick)
{
tickDifference = 0;
return false;
}
else
{
uint serverDifference = (currentServerTick - _lastServerTick);
uint localDifference = (currentLocalTick - _lastLocalTick);
long td = ((long)serverDifference - (long)localDifference);
//Average tick differences over 2 updates to help with unstable connections.
if (_lastServerClientDifference.HasValue)
{
long totalTd = (td + _lastServerClientDifference.Value);
tickDifference = (totalTd / (long)2);
}
//Last tick difference not set yet, use current values only.
else
{
tickDifference = td;
}
//Also update last values.
_lastServerTick = currentServerTick;
_lastLocalTick = currentLocalTick;
_lastServerClientDifference = td;
return true;
}
}
}
#endregion
#region Public.
@ -210,14 +147,14 @@ namespace FishNet.Managing.Timing
/// </summary>
[Tooltip("While true clients may drop local ticks if their devices are unable to maintain the tick rate. This could result in a temporary desynchronization but will prevent the client falling further behind on ticks by repeatedly running the logic cycle multiple times per frame.")]
[SerializeField]
private bool _allowTickDropping;
private bool _allowTickDropping = true;
/// <summary>
/// Maximum number of ticks which may occur in a single frame before remainder are dropped for the frame.
/// </summary>
[Tooltip("Maximum number of ticks which may occur in a single frame before remainder are dropped for the frame.")]
[Range(1, 25)]
[SerializeField]
private byte _maximumFrameTicks = 2;
private byte _maximumFrameTicks = 3;
/// <summary>
///
/// </summary>
@ -308,9 +245,24 @@ namespace FishNet.Managing.Timing
/// </summary>
private bool _fixedUpdateTimeStep;
/// <summary>
/// Synchronizes tick deltas between server and client.
///
/// </summary>
private TimingSync _timing = new TimingSync();
private float _physicsTimeScale = 1f;
/// <summary>
/// Gets the current physics time scale.
/// </summary>
/// <returns></returns>
public float GetPhysicsTimeScale() => _physicsTimeScale;
/// <summary>
/// Sets the physics time scale.
/// This is not automatically synchronized.
/// </summary>
/// <param name="value">New value.</param>
public void SetPhysicsTimeScale(float value)
{
value = Mathf.Clamp(value, 0f, float.PositiveInfinity);
_physicsTimeScale = value;
}
#endregion
#region Const.
@ -538,8 +490,8 @@ namespace FishNet.Managing.Timing
//Preserve user tick rate.
PlayerPrefs.SetFloat(SAVED_FIXED_TIME_TEXT, Time.fixedDeltaTime);
//Let the player know.
if (Time.fixedDeltaTime != (float)TickDelta)
Debug.LogWarning("Time.fixedDeltaTime is being overriden with TimeManager.TickDelta");
//if (Time.fixedDeltaTime != (float)TickDelta)
// Debug.LogWarning("Time.fixedDeltaTime is being overriden with TimeManager.TickDelta");
#endif
Time.fixedDeltaTime = (float)TickDelta;
/* Only check this if network manager
@ -668,8 +620,7 @@ namespace FishNet.Managing.Timing
bool isClient = NetworkManager.IsClientStarted;
bool isServer = NetworkManager.IsServerStarted;
double tickDelta = TickDelta;
double timePerSimulation = (isServer) ? tickDelta : _adjustedTickDelta;
double timePerSimulation = (isServer) ? TickDelta : _adjustedTickDelta;
if (timePerSimulation == 0d)
{
Debug.LogWarning($"Simulation delta cannot be 0. Network timing will not continue.");
@ -692,7 +643,7 @@ namespace FishNet.Managing.Timing
if (ticksCount > 1)
_lastMultipleTicksTime = Time.unscaledDeltaTime;
if (_allowTickDropping && !NetworkManager.IsServerStarted)
if (_allowTickDropping)
{
//If ticks require dropping. Set exactly to maximum ticks.
if (ticksCount > _maximumFrameTicks)
@ -701,6 +652,7 @@ namespace FishNet.Managing.Timing
bool variableTiming = (_timingType == TimingType.Variable);
bool frameTicked = FrameTicked;
float tickDelta = ((float)TickDelta * GetPhysicsTimeScale());
do
{
@ -727,11 +679,10 @@ namespace FishNet.Managing.Timing
if (PhysicsMode == PhysicsMode.TimeManager)
{
float tick = (float)TickDelta;
OnPrePhysicsSimulation?.Invoke(tick);
Physics.Simulate(tick);
Physics2D.Simulate(tick);
OnPostPhysicsSimulation?.Invoke(tick);
OnPrePhysicsSimulation?.Invoke(tickDelta);
Physics.Simulate(tickDelta);
Physics2D.Simulate(tickDelta);
OnPostPhysicsSimulation?.Invoke(tickDelta);
}
OnPostTick?.Invoke();
@ -1097,12 +1048,16 @@ namespace FishNet.Managing.Timing
{
//Now send using a packetId.
PooledWriter writer = WriterPool.Retrieve();
writer.WritePacketId(PacketId.TimingUpdate);
foreach (NetworkConnection item in NetworkManager.ServerManager.Clients.Values)
{
if (!item.IsAuthenticated)
continue;
writer.WritePacketId(PacketId.TimingUpdate);
writer.WriteTickUnpacked(item.PacketTick.Value());
item.SendToClient((byte)Channel.Unreliable, writer.GetArraySegment());
writer.Reset();
}
writer.Store();
@ -1113,12 +1068,17 @@ namespace FishNet.Managing.Timing
/// Called on client when server sends a timing update.
/// </summary>
/// <param name="ta"></param>
internal void ParseTimingUpdate()
internal void ParseTimingUpdate(Reader reader)
{
uint clientTick = reader.ReadTickUnpacked();
//Don't adjust timing on server.
if (NetworkManager.IsServerStarted)
return;
/* This should never be possible since the server is sending a tick back
* that the client previously sent. In other words, the value returned should
* always be in the past. */
if (LocalTick < clientTick)
return;
/* Use the last ordered remote tick rather than
* lastPacketTick. This will help with out of order
* packets where the timing update sent before
@ -1127,42 +1087,26 @@ namespace FishNet.Managing.Timing
* ticks really passed rather than the difference
* between the out of order/late packet. */
uint lastPacketTick = LastPacketTick.RemoteTick;
//If difference could not be updated then something went wrong. Most likely an old timing update.
if (!_timing.GetTickDifference(lastPacketTick, LocalTick, out long tickDifference))
return;
//Set Tick based on difference between localTick and clientTick, added onto lastPacketTick.
uint prevTick = Tick;
uint nextTick = (LocalTick - clientTick) + lastPacketTick;
long difference = ((long)nextTick - (long)prevTick);
Tick = nextTick;
//Maximum difference allowed before resetting values.
const int maximumDifference = 4;
TryRecalculateTick();
////Do not change timing if client is slowing down due to latency issues.
//if (Time.unscaledTime - NetworkManager.PredictionManager.SlowDownTime > 3f)
//{
//Pefect!
if (tickDifference == 0) { }
//Difference is extreme, reset to default timings. Client probably had an issue.
else if (Mathf.Abs(tickDifference) > maximumDifference)
{
_adjustedTickDelta = TickDelta;
}
//Otherwise adjust the delta marginally.
else
{
/* A negative tickDifference indicates the client is
* moving too fast, while positive indicates too slow. */
bool speedUp = (tickDifference > 0);
ChangeAdjustedTickDelta(speedUp);
}
// }
//Recalculates Tick value if it exceeds maximum difference.
void TryRecalculateTick()
//Difference is extreme, reset to default timings. Client probably had an issue.
if (Mathf.Abs(difference) > maximumDifference)
{
uint rttTicks = TimeToTicks((RoundTripTime / 2) / 1000f);
uint newValue = lastPacketTick + rttTicks;
long difference = (long)Mathf.Abs((long)Tick - (long)newValue);
if (difference > maximumDifference)
Tick = newValue;
_adjustedTickDelta = TickDelta;
}
//Otherwise adjust the delta marginally.
else if (difference != 0)
{
/* A negative tickDifference indicates the client is
* moving too fast, while positive indicates too slow. */
bool speedUp = (difference > 0);
ChangeAdjustedTickDelta(speedUp);
}
}
#endregion

View File

@ -1,4 +1,7 @@
using FishNet.Connection;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#define DEVELOPMENT
#endif
using FishNet.Connection;
using FishNet.Managing.Timing;
using FishNet.Object;
using FishNet.Serializing;
@ -128,27 +131,31 @@ namespace FishNet.Managing.Transporting
/// <summary>
/// Number of bytes sent for PacketId.
/// </summary>
public const byte PACKET_ID_BYTES = 2;
public const byte PACKETID_LENGTH = 2;
/// <summary>
/// Number of bytes sent for ObjectId.
/// </summary>
public const byte OBJECT_ID_BYTES = 2;
public const byte OBJECT_ID_LENGTH = 2;
/// <summary>
/// Number of bytes sent for ComponentIndex.
/// </summary>
public const byte COMPONENT_INDEX_BYTES = 1;
public const byte COMPONENT_INDEX_LENGTH = 1;
/// <summary>
/// Number of bytes sent for Tick.
/// </summary>
public const byte TICK_BYTES = 4;
public const byte UNPACKED_TICK_LENGTH = 4;
/// <summary>
/// Number of bytes sent for an unpacked size, such as a collection or array size.
/// </summary>
public const byte UNPACKED_SIZE_LENGTH = 4;
/// <summary>
/// Number of bytes sent to indicate split count.
/// </summary>
private const byte SPLIT_COUNT_BYTES = 4;
private const byte SPLIT_COUNT_LENGTH = 4;
/// <summary>
/// Number of bytes required for split data.
/// </summary> //todo: This shouldn't have to include TickBytes but there is a parse error if it's not included. Figure out why.
public const byte SPLIT_INDICATOR_SIZE = (TICK_BYTES + PACKET_ID_BYTES + SPLIT_COUNT_BYTES);
public const byte SPLIT_INDICATOR_LENGTH = (UNPACKED_TICK_LENGTH + PACKETID_LENGTH + SPLIT_COUNT_LENGTH);
/// <summary>
/// Number of channels supported.
/// </summary>
@ -176,7 +183,7 @@ namespace FishNet.Managing.Transporting
InitializeToServerBundles();
if (_intermediateLayer != null)
_intermediateLayer.InitializeOnce(this);
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
_latencySimulator.Initialize(manager, Transport);
#endif
}
@ -621,7 +628,7 @@ namespace FishNet.Managing.Transporting
/// </summary>
private int GetRequiredMessageCount(byte channelId, int segmentSize, out int maxMessageSize)
{
maxMessageSize = GetLowestMTU(channelId) - SPLIT_INDICATOR_SIZE;
maxMessageSize = GetLowestMTU(channelId) - SPLIT_INDICATOR_LENGTH;
return Mathf.CeilToInt((float)segmentSize / maxMessageSize);
}
@ -718,7 +725,7 @@ namespace FishNet.Managing.Transporting
OnIterateOutgoingStart?.Invoke();
int channelCount = CHANNEL_COUNT;
ulong sentBytes = 0;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
bool latencySimulatorEnabled = LatencySimulator.CanSimulate;
#endif
/* If sending to the client. */
@ -756,7 +763,7 @@ namespace FishNet.Managing.Transporting
ArraySegment<byte> segment = new ArraySegment<byte>(bb.Data, 0, bb.Length);
if (HasIntermediateLayer)
segment = ProcessIntermediateOutgoing(segment, false);
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
if (latencySimulatorEnabled)
_latencySimulator.AddOutgoing(channel, segment, false, conn.ClientId);
else
@ -829,7 +836,7 @@ namespace FishNet.Managing.Transporting
ArraySegment<byte> segment = new ArraySegment<byte>(bb.Data, 0, bb.Length);
if (HasIntermediateLayer)
segment = ProcessIntermediateOutgoing(segment, true);
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
if (latencySimulatorEnabled)
_latencySimulator.AddOutgoing(channel, segment);
else
@ -847,7 +854,7 @@ namespace FishNet.Managing.Transporting
_networkManager.StatisticsManager.NetworkTraffic.LocalClientSentData(sentBytes);
}
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
if (latencySimulatorEnabled)
_latencySimulator.IterateOutgoing(toServer);
#endif

View File

@ -1,35 +1 @@
using FishNet.Documenting;
namespace FishNet.Object
{
/// <summary>
/// Properties which have changed on a transform.
/// </summary>
[System.Flags]
[APIExclude]
internal enum ChangedTransformProperties : byte
{
Unset = 0,
LocalPosition = 1,
LocalRotation = 2,
LocalScale = 4,
}
[APIExclude]
internal static partial class ChangedTransformPropertiesEnum
{
/// <summary>
/// Returns if whole contains part.
/// </summary>
/// <param name="whole"></param>
/// <param name="part"></param>
/// <returns></returns>
public static bool Contains(ChangedTransformProperties whole, ChangedTransformProperties part)
{
return (whole & part) == part;
}
}
}
//Remove on V5

View File

@ -1,5 +1,6 @@
#if UNITY_EDITOR
#if !PREDICTION_1
using FishNet.Object.Prediction;
using UnityEditor;
using UnityEngine;
@ -21,11 +22,17 @@ namespace FishNet.Object.Editing
private SerializedProperty _networkTransform;
private SerializedProperty _predictionType;
private SerializedProperty _graphicalObject;
private SerializedProperty _detachGraphicalObject;
private SerializedProperty _ownerSmoothedProperties;
private SerializedProperty _spectatorSmoothedProperties;
private SerializedProperty _ownerInterpolation;
private SerializedProperty _adaptiveInterpolation;
private SerializedProperty _spectatorInterpolation;
private SerializedProperty _enableTeleport;
private SerializedProperty _teleportThreshold;
protected virtual void OnEnable()
{
@ -40,11 +47,15 @@ namespace FishNet.Object.Editing
_networkTransform = serializedObject.FindProperty(nameof(_networkTransform));
_predictionType = serializedObject.FindProperty(nameof(_predictionType));
_graphicalObject = serializedObject.FindProperty(nameof(_graphicalObject));
_detachGraphicalObject = serializedObject.FindProperty(nameof(_detachGraphicalObject));
_ownerSmoothedProperties = serializedObject.FindProperty(nameof(_ownerSmoothedProperties));
_ownerInterpolation = serializedObject.FindProperty(nameof(_ownerInterpolation));
_adaptiveInterpolation = serializedObject.FindProperty(nameof(_adaptiveInterpolation));
_spectatorSmoothedProperties = serializedObject.FindProperty(nameof(_spectatorSmoothedProperties));
_spectatorInterpolation = serializedObject.FindProperty(nameof(_spectatorInterpolation));
_enableTeleport = serializedObject.FindProperty(nameof(_enableTeleport));
_teleportThreshold = serializedObject.FindProperty(nameof(_teleportThreshold));
_ownerInterpolation = serializedObject.FindProperty(nameof(_ownerInterpolation));
}
public override void OnInspectorGUI()
@ -80,19 +91,51 @@ namespace FishNet.Object.Editing
EditorGUILayout.PropertyField(_networkTransform);
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(_graphicalObject);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_ownerInterpolation, new GUIContent("Interpolation"));
EditorGUILayout.PropertyField(_enableTeleport);
if (_enableTeleport.boolValue == true)
bool graphicalSet = (_graphicalObject.objectReferenceValue != null);
EditorGUILayout.PropertyField(_graphicalObject);
if (graphicalSet)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_teleportThreshold, new GUIContent("Teleport Threshold"));
EditorGUILayout.PropertyField(_detachGraphicalObject);
EditorGUI.indentLevel--;
}
EditorGUILayout.LabelField("Smoothing", EditorStyles.boldLabel);
if (!graphicalSet)
{
EditorGUILayout.HelpBox($"More smoothing settings will be displayed when a graphicalObject is set.", MessageType.Info);
}
else
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_enableTeleport);
if (_enableTeleport.boolValue == true)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_teleportThreshold, new GUIContent("Teleport Threshold"));
EditorGUI.indentLevel--;
}
EditorGUILayout.LabelField("Owner", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_ownerInterpolation, new GUIContent("Interpolation"));
EditorGUILayout.PropertyField(_ownerSmoothedProperties, new GUIContent("Smoothed Properties"));
EditorGUI.indentLevel--;
EditorGUILayout.LabelField("Spectator", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_adaptiveInterpolation);
if (_adaptiveInterpolation.intValue == (int)AdaptiveInterpolationType.Off)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_spectatorInterpolation, new GUIContent("Interpolation"));
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(_spectatorSmoothedProperties, new GUIContent("Smoothed Properties"));
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;

View File

@ -1,4 +1,7 @@
#if !PREDICTION_1
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#define DEVELOPMENT
#endif
#if !PREDICTION_1
using FishNet.CodeGenerating;
using FishNet.Connection;
using FishNet.Documenting;
@ -324,7 +327,7 @@ namespace FishNet.Object
methodWriter.Write(reconcileData);
PooledWriter writer;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
if (NetworkManager.DebugManager.ReconcileRpcLinks && _rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link))
#else
if (_rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link))
@ -348,49 +351,49 @@ namespace FishNet.Object
writer.Store();
}
// /// <summary>
// /// Returns if there is a chance the transform may change after the tick.
// /// </summary>
// /// <returns></returns>
// protected internal bool PredictedTransformMayChange()
// {
// if (TimeManager.PhysicsMode == PhysicsMode.Disabled)
// return false;
// /// <summary>
// /// Returns if there is a chance the transform may change after the tick.
// /// </summary>
// /// <returns></returns>
// protected internal bool PredictedTransformMayChange()
// {
// if (TimeManager.PhysicsMode == PhysicsMode.Disabled)
// return false;
// if (!_predictionInitialized)
// {
// _predictionInitialized = true;
// _predictionRigidbody = GetComponentInParent<Rigidbody>();
// _predictionRigidbody2d = GetComponentInParent<Rigidbody2D>();
// }
// if (!_predictionInitialized)
// {
// _predictionInitialized = true;
// _predictionRigidbody = GetComponentInParent<Rigidbody>();
// _predictionRigidbody2d = GetComponentInParent<Rigidbody2D>();
// }
// /* Use distance when checking if changed because rigidbodies can twitch
//* or move an extremely small amount. These small moves are not worth
//* resending over because they often fix themselves each frame. */
// float changeDistance = 0.000004f;
// /* Use distance when checking if changed because rigidbodies can twitch
//* or move an extremely small amount. These small moves are not worth
//* resending over because they often fix themselves each frame. */
// float changeDistance = 0.000004f;
// bool positionChanged = (transform.position - _lastMayChangePosition).sqrMagnitude > changeDistance;
// bool rotationChanged = (transform.rotation.eulerAngles - _lastMayChangeRotation.eulerAngles).sqrMagnitude > changeDistance;
// bool scaleChanged = (transform.localScale - _lastMayChangeScale).sqrMagnitude > changeDistance;
// bool transformChanged = (positionChanged || rotationChanged || scaleChanged);
// /* Returns true if transform.hasChanged, or if either
//* of the rigidbodies have velocity. */
// bool changed = (
// transformChanged ||
// (_predictionRigidbody != null && (_predictionRigidbody.velocity != Vector3.zero || _predictionRigidbody.angularVelocity != Vector3.zero)) ||
// (_predictionRigidbody2d != null && (_predictionRigidbody2d.velocity != Vector2.zero || _predictionRigidbody2d.angularVelocity != 0f))
// );
// bool positionChanged = (transform.position - _lastMayChangePosition).sqrMagnitude > changeDistance;
// bool rotationChanged = (transform.rotation.eulerAngles - _lastMayChangeRotation.eulerAngles).sqrMagnitude > changeDistance;
// bool scaleChanged = (transform.localScale - _lastMayChangeScale).sqrMagnitude > changeDistance;
// bool transformChanged = (positionChanged || rotationChanged || scaleChanged);
// /* Returns true if transform.hasChanged, or if either
//* of the rigidbodies have velocity. */
// bool changed = (
// transformChanged ||
// (_predictionRigidbody != null && (_predictionRigidbody.velocity != Vector3.zero || _predictionRigidbody.angularVelocity != Vector3.zero)) ||
// (_predictionRigidbody2d != null && (_predictionRigidbody2d.velocity != Vector2.zero || _predictionRigidbody2d.angularVelocity != 0f))
// );
// //If transform changed update last values.
// if (transformChanged)
// {
// _lastMayChangePosition = transform.position;
// _lastMayChangeRotation = transform.rotation;
// _lastMayChangeScale = transform.localScale;
// }
// //If transform changed update last values.
// if (transformChanged)
// {
// _lastMayChangePosition = transform.position;
// _lastMayChangeRotation = transform.rotation;
// _lastMayChangeScale = transform.localScale;
// }
// return changed;
// }
// return changed;
// }
/// <summary>
@ -438,26 +441,37 @@ namespace FishNet.Object
/// </summary>
protected internal void Replicate_Replay_NonAuthoritative<T>(uint replayTick, ReplicateUserLogicDelegate<T> del, List<T> replicatesHistory, Channel channel) where T : IReplicateData
{
ReplicateTickFinder.DataPlacementResult findResult;
int replicateIndex = ReplicateTickFinder.GetReplicateHistoryIndex<T>(replayTick, replicatesHistory, out findResult);
T data;
ReplicateState state;
//If found then the replicate has been received by the server.
if (findResult == ReplicateTickFinder.DataPlacementResult.Exact)
//If the first replay.
if (replayTick == (_networkObjectCache.PredictionManager.ServerStateTick + 1))
{
data = replicatesHistory[replicateIndex];
state = ReplicateState.ReplayedCreated;
ReplicateTickFinder.DataPlacementResult findResult;
int replicateIndex = ReplicateTickFinder.GetReplicateHistoryIndex<T>(replayTick, replicatesHistory, out findResult);
//If not found then something went wrong.
if (findResult == ReplicateTickFinder.DataPlacementResult.Exact)
{
data = replicatesHistory[replicateIndex];
state = ReplicateState.ReplayedCreated;
}
else
{
SetDataToDefault();
}
}
//If not not found then it's being run as predicted.
//Not the first replay tick.
else
{
SetDataToDefault();
}
void SetDataToDefault()
{
data = default;
data.SetTick(replayTick);
if (replicatesHistory.Count == 0 || replicatesHistory[^1].GetTick() < replayTick)
state = ReplicateState.ReplayedFuture;
else
state = ReplicateState.ReplayedCreated;
state = ReplicateState.ReplayedFuture;
}
del.Invoke(data, state, channel);
@ -581,36 +595,19 @@ namespace FishNet.Object
PredictionManager pm = NetworkManager.PredictionManager;
uint localTick = TimeManager.LocalTick;
data.SetTick(localTick);
replicatesHistory.Add(data);
//Check to reset resends.
bool isDefault = isDefaultDel.Invoke(data);
bool mayChange = false;// PredictedTransformMayChange();
bool resetResends = (mayChange || !isDefault);
/* If remaining resends is more than 0 then that means
* redundancy is still in effect. When redundancy is not
* in effect then histories to send can be 1 for this iteration. */
int pastInputs = (_remainingResends > 0) ? PredictionManager.RedundancyCount : 1;
//pastInputs = PredictionManager.RedundancyCount;
if (resetResends)
_remainingResends = pm.RedundancyCount;
bool sendData = (_remainingResends > 0);
if (sendData)
/* The following code is to remove replicates from replicatesHistory
* which exceed the buffer allowance. Replicates are kept for up to
* x seconds to clients can re-run them during a reconcile. The reconcile
* method removes old histories but given the server does not reconcile,
* it will never perform that operation.
* The server would not actually need to keep replicates history except
* when it is also client(clientHost). This is because the clientHost must
* send redundancies to other clients still, therefor that redundancyCount
* must be the allowance when clientHost. */
if (IsHostStarted)
{
int replicatesHistoryCount = replicatesHistory.Count;
/* Remove the number of replicates which are over maximum.
*
* The clientHost object must keep redundancy count
* to send past inputs to others.
*
* Otherwise use maximum client replicates which will be a variable
* rate depending on tick rate. The value returned is several seconds
* worth of owner inputs to be able to replay during a reconcile.
*
* Server does not reconcile os it only needs enough for redundancy.
*/
int maxCount = (IsServerStarted) ? pm.RedundancyCount : pm.MaximumClientReplicates;
int maxCount = pm.RedundancyCount;
//Number to remove which is over max count.
int removeCount = (replicatesHistoryCount - maxCount);
//If there are any to remove.
@ -623,11 +620,26 @@ namespace FishNet.Object
//Then remove range.
replicatesHistory.RemoveRange(0, removeCount);
}
}
data.SetTick(localTick);
replicatesHistory.Add(data);
//Check to reset resends.
bool isDefault = isDefaultDel.Invoke(data);
bool mayChange = false;// PredictedTransformMayChange();
bool resetResends = (mayChange || !isDefault);
byte redundancyCount = PredictionManager.RedundancyCount;
if (resetResends)
_remainingResends = redundancyCount;
bool sendData = (_remainingResends > 0);
if (sendData)
{
/* If not server then send to server.
* If server then send to clients. */
bool toServer = !IsServerStarted;
Replicate_SendAuthoritative(toServer, methodHash, pastInputs, replicatesHistory, localTick, channel);
Replicate_SendAuthoritative(toServer, methodHash, redundancyCount, replicatesHistory, localTick, channel);
_remainingResends--;
}
@ -635,7 +647,6 @@ namespace FishNet.Object
_networkObjectCache.SetReplicateTick(data.GetTick(), true);
//Owner always replicates with new data.
del.Invoke(data, ReplicateState.CurrentCreated, channel);
//TODO: dispose replicate datas from history on replays.
}
/// <summary>
@ -771,7 +782,6 @@ namespace FishNet.Object
/// <summary>
/// Sends data from a reader which only contains the replicate packet.
/// </summary>
/// <param name="tick">Tick of the last replicate entry.</param>
[MakePublic]
internal void Replicate_SendNonAuthoritative<T>(uint hash, BasicQueue<T> replicatesQueue, Channel channel) where T : IReplicateData
{
@ -781,12 +791,11 @@ namespace FishNet.Object
return;
int queueCount = replicatesQueue.Count;
//Limit history count to max of queued amount, or queued inputs, whichever is lesser.
int historyCount = (int)Mathf.Min(_networkObjectCache.PredictionManager.RedundancyCount, queueCount);
//None to send.
if (historyCount == 0)
if (queueCount == 0)
return;
int redundancyCount = (int)Mathf.Min(_networkObjectCache.PredictionManager.RedundancyCount, queueCount);
//If the only observer is the owner then there is no need to write.
int observersCount = Observers.Count;
//Quick exit for no observers other than owner.
@ -810,7 +819,7 @@ namespace FishNet.Object
//Write the run tick now.
methodWriter.WriteTickUnpacked(runTickOflastEntry);
//Write the replicates.
methodWriter.WriteReplicate<T>(replicatesQueue, historyCount, runTickOflastEntry);
methodWriter.WriteReplicate<T>(replicatesQueue, redundancyCount, runTickOflastEntry);
PooledWriter writer = CreateRpc(hash, methodWriter, PacketId.Replicate, channel);
@ -830,14 +839,14 @@ namespace FishNet.Object
/// Handles a received replicate packet.
/// </summary>
private void Replicate_EnqueueReceivedReplicate<T>(int receivedReplicatesCount, T[] arrBuffer, BasicQueue<T> replicatesQueue, List<T> replicatesHistory, Channel channel) where T : IReplicateData
{
{
int startQueueCount = replicatesQueue.Count;
/* Owner never gets this for their own object so
* this can be processed under the assumption data is only
* handled on unowned objects. */
PredictionManager pm = PredictionManager;
//Maximum number of replicates allowed to be queued at once.
int maximmumReplicates = (IsServerStarted) ? pm.GetMaximumServerReplicates() : pm.MaximumClientReplicates;
int maximmumReplicates = (IsServerStarted) ? pm.GetMaximumServerReplicates() : pm.MaximumPastReplicates;
for (int i = 0; i < receivedReplicatesCount; i++)
{
T entry = arrBuffer[i];
@ -917,7 +926,8 @@ namespace FishNet.Object
* by holding reconcile x ticks rather than not running received
* x ticks. */
if (_networkObjectCache.IsServerInitialized && startQueueCount == 0 && replicatesQueue.Count > 0)
_replicateStartTick = (_networkObjectCache.TimeManager.LocalTick + pm.QueuedInputs);
//_replicateStartTick = (_networkObjectCache.TimeManager.LocalTick);
_replicateStartTick = (_networkObjectCache.TimeManager.LocalTick + pm.StateInterpolation);
}
@ -1336,7 +1346,7 @@ namespace FishNet.Object
methodWriter.Write(reconcileData);
PooledWriter writer;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
if (NetworkManager.DebugManager.ReconcileRpcLinks && _rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link))
#else
if (_rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link))

View File

@ -1,4 +1,7 @@
using FishNet.CodeGenerating;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#define DEVELOPMENT
#endif
using FishNet.CodeGenerating;
using FishNet.Connection;
using FishNet.Documenting;
using FishNet.Managing;
@ -249,7 +252,7 @@ namespace FishNet.Object
_transportManagerCache.CheckSetReliableChannel(methodWriter.Length + MAXIMUM_RPC_HEADER_SIZE, ref channel);
PooledWriter writer;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
if (NetworkManager.DebugManager.ObserverRpcLinks && _rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link))
#else
if (_rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link))
@ -314,7 +317,7 @@ namespace FishNet.Object
PooledWriter writer;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#if DEVELOPMENT
if (NetworkManager.DebugManager.TargetRpcLinks && _rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link))
#else
if (_rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link))

View File

@ -6,11 +6,22 @@ namespace FishNet.Object
{
public partial class NetworkObject : MonoBehaviour
{
#region Private.
/// <summary>
/// True if OnStartServer was called.
/// </summary>
private bool _onStartServerCalled;
/// <summary>
/// True if OnStartClient was called.
/// </summary>
private bool _onStartClientCalled;
#endregion
/// <summary>
/// Called after all data is synchronized with this NetworkObject.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void InitializeCallbacks(bool asServer, bool invokeSyncTypeCallbacks)
private void InvokeStartCallbacks(bool asServer, bool invokeSyncTypeCallbacks)
{
/* Note: When invoking OnOwnership here previous owner will
* always be an empty connection, since the object is just
@ -25,6 +36,7 @@ namespace FishNet.Object
{
for (int i = 0; i < NetworkBehaviours.Length; i++)
NetworkBehaviours[i].OnStartServer_Internal();
_onStartServerCalled = true;
for (int i = 0; i < NetworkBehaviours.Length; i++)
NetworkBehaviours[i].OnOwnershipServer_Internal(FishNet.Managing.NetworkManager.EmptyConnection);
}
@ -33,12 +45,17 @@ namespace FishNet.Object
{
for (int i = 0; i < NetworkBehaviours.Length; i++)
NetworkBehaviours[i].OnStartClient_Internal();
_onStartClientCalled = true;
for (int i = 0; i < NetworkBehaviours.Length; i++)
NetworkBehaviours[i].OnOwnershipClient_Internal(FishNet.Managing.NetworkManager.EmptyConnection);
}
if (invokeSyncTypeCallbacks)
InvokeOnStartSyncTypeCallbacks(true);
#if !PREDICTION_1
InvokeStartCallbacks_Prediction(asServer);
#endif
}
@ -88,48 +105,35 @@ namespace FishNet.Object
/// Invokes OnStop callbacks.
/// </summary>
/// <param name="asServer"></param>
internal void InvokeStopCallbacks(bool asServer)
internal void InvokeStopCallbacks(bool asServer, bool invokeSyncTypeCallbacks)
{
for (int i = 0; i < NetworkBehaviours.Length; i++)
NetworkBehaviours[i].InvokeSyncTypeOnStopCallbacks(asServer);
#if !PREDICTION_1
InvokeStopCallbacks_Prediction(asServer);
#endif
if (invokeSyncTypeCallbacks)
InvokeOnStopSyncTypeCallbacks(asServer);
if (asServer)
bool invokeOnNetwork = (!asServer || (asServer && !_onStartClientCalled));
if (asServer && _onStartServerCalled)
{
for (int i = 0; i < NetworkBehaviours.Length; i++)
NetworkBehaviours[i].OnStopServer_Internal();
_onStartServerCalled = false;
}
else
else if (!asServer && _onStartClientCalled)
{
for (int i = 0; i < NetworkBehaviours.Length; i++)
NetworkBehaviours[i].OnStopClient_Internal();
_onStartClientCalled = false;
}
/* Several conditions determine if OnStopNetwork can
* be called.
*
* - If asServer and pending destroy from clientHost.
* - If !asServer and not ServerInitialized. */
bool callStopNetwork;
if (asServer)
{
if (!IsClientStarted)
callStopNetwork = true;
else
callStopNetwork = (ServerManager.Objects.GetFromPending(ObjectId) == null);
}
else
{
/* When not as server only perform OnStopNetwork if
* not initialized for the server. The object could be
* server initialized if it were spawned, despawned, then spawned again
* before client ran this method. */
callStopNetwork = !IsServerInitialized;
}
if (callStopNetwork)
if (invokeOnNetwork)
{
for (int i = 0; i < NetworkBehaviours.Length; i++)
NetworkBehaviours[i].InvokeOnNetwork(false);
}
}
/// <summary>

View File

@ -33,6 +33,10 @@ namespace FishNet.Object
/// True if a reconcile is occuring on any NetworkBehaviour that is on or nested of this NetworkObject. Runtime NetworkBehaviours are not included, such as if you child a NetworkObject to another at runtime.
/// </summary>
public bool IsObjectReconciling { get; internal set; }
/// <summary>
/// Graphical smoother to use when using set for owner.
/// </summary>
public ChildTransformTickSmoother PredictionSmoother { get; private set; }
#endif
/// <summary>
/// Last tick this object replicated.
@ -74,6 +78,28 @@ namespace FishNet.Object
[SerializeField]
private Transform _graphicalObject;
/// <summary>
/// Gets the current graphical object for prediction.
/// </summary>
/// <returns></returns>
public Transform GetGraphicalObject() => _graphicalObject;
/// <summary>
/// Sets a new graphical object for prediction.
/// </summary>
/// <param name="t"></param>
public void SetGraphicalObject(Transform t)
{
_graphicalObject = t;
InitializeTickSmoother();
}
/// <summary>
/// True to detach and re-attach the graphical object at runtime when the client initializes/deinitializes the item.
/// This can resolve camera jitter or be helpful objects child of the graphical which do not handle reconiliation well, such as certain animation rigs.
/// Transform is detached after OnStartClient, and reattached before OnStopClient.
/// </summary>
[Tooltip("True to detach and re-attach the graphical object at runtime when the client initializes/deinitializes the item. This can resolve camera jitter or be helpful objects child of the graphical which do not handle reconiliation well, such as certain animation rigs. Transform is detached after OnStartClient, and reattached before OnStopClient.")]
[SerializeField]
private bool _detachGraphicalObject;
/// <summary>
/// True to forward replicate and reconcile states to all clients. This is ideal with games where you want all clients and server to run the same inputs. False to only use prediction on the owner, and synchronize to spectators using other means such as a NetworkTransform.
/// </summary>
public bool EnableStateForwarding => (_enablePrediction && _enableStateForwarding);
@ -94,6 +120,29 @@ namespace FishNet.Object
[SerializeField]
private byte _ownerInterpolation = 1;
/// <summary>
/// Properties of the graphicalObject to smooth when owned.
/// </summary>
[SerializeField]
private TransformPropertiesFlag _ownerSmoothedProperties = (TransformPropertiesFlag)~(-1 << 8);
/// <summary>
/// Interpolation amount of adaptive interpolation to use on non-owned objects. Higher levels result in more interpolation. When off spectatorInterpolation is used; when on interpolation based on strength and local client latency is used.
/// </summary>
[Tooltip("Interpolation amount of adaptive interpolation to use on non-owned objects. Higher levels result in more interpolation. When off spectatorInterpolation is used; when on interpolation based on strength and local client latency is used.")]
[SerializeField]
private AdaptiveInterpolationType _adaptiveInterpolation = AdaptiveInterpolationType.Medium;
/// <summary>
/// Properties of the graphicalObject to smooth when the object is spectated.
/// </summary>
[SerializeField]
private TransformPropertiesFlag _spectatorSmoothedProperties = (TransformPropertiesFlag)~(-1 << 8);
/// <summary>
/// How many ticks to interpolate graphics on objects when not owned by the client.
/// </summary>
[Tooltip("How many ticks to interpolate graphics on objects when not owned by the client.")]
[Range(1, byte.MaxValue)]
[SerializeField]
private byte _spectatorInterpolation = 2;
/// <summary>
/// True to enable teleport threshhold.
/// </summary>
[Tooltip("True to enable teleport threshhold.")]
@ -111,10 +160,6 @@ namespace FishNet.Object
#region Private.
/// <summary>
/// Graphical smoother to use when using set for owner.
/// </summary>
private LocalTransformTickSmoother _tickSmoother;
/// <summary>
/// NetworkBehaviours which use prediction.
/// </summary>
private List<NetworkBehaviour> _predictionBehaviours = new List<NetworkBehaviour>();
@ -125,16 +170,7 @@ namespace FishNet.Object
if (!_enablePrediction)
return;
_tickSmoother?.Update();
}
private void TimeManager_OnPreTick()
{
_tickSmoother?.OnPreTick();
}
private void TimeManager_OnPostTick()
{
_tickSmoother?.OnPostTick();
PredictionSmoother?.Update();
}
private void Prediction_Preinitialize(NetworkManager manager, bool asServer)
@ -146,19 +182,14 @@ namespace FishNet.Object
_networkTransform.ConfigureForPrediction(_predictionType);
ReplicateTick.Initialize(manager.TimeManager);
InitializeSmoothers();
if (!asServer)
InitializeSmoothers();
if (asServer)
return;
if (_predictionBehaviours.Count > 0)
{
manager.PredictionManager.OnReconcile += PredictionManager_OnReconcile;
manager.PredictionManager.OnReplicateReplay += PredictionManager_OnReplicateReplay;
manager.PredictionManager.OnPostReconcile += PredictionManager_OnPostReconcile;
manager.TimeManager.OnPreTick += TimeManager_OnPreTick;
manager.TimeManager.OnPostTick += TimeManager_OnPostTick;
}
ChangePredictionSubscriptions(true, manager);
}
private void Prediction_Deinitialize(bool asServer)
@ -170,13 +201,37 @@ namespace FishNet.Object
/* Only the client needs to unsubscribe from these but
* asServer may not invoke as false if the client is suddenly
* dropping their connection. */
if (_predictionBehaviours.Count > 0 && NetworkManager != null)
if (_predictionBehaviours.Count > 0)
ChangePredictionSubscriptions(false, NetworkManager);
}
/// <summary>
/// Changes subscriptions to use callbacks for prediction.
/// </summary>
private void ChangePredictionSubscriptions(bool subscribe, NetworkManager manager)
{
if (manager == null)
return;
if (subscribe)
{
NetworkManager.PredictionManager.OnReconcile -= PredictionManager_OnReconcile;
NetworkManager.PredictionManager.OnReplicateReplay -= PredictionManager_OnReplicateReplay;
NetworkManager.PredictionManager.OnPostReconcile -= PredictionManager_OnPostReconcile;
NetworkManager.TimeManager.OnPreTick -= TimeManager_OnPreTick;
NetworkManager.TimeManager.OnPostTick -= TimeManager_OnPostTick;
manager.PredictionManager.OnPreReconcile += PredictionManager_OnPreReconcile;
manager.PredictionManager.OnReconcile += PredictionManager_OnReconcile;
manager.PredictionManager.OnReplicateReplay += PredictionManager_OnReplicateReplay;
manager.PredictionManager.OnPostReplicateReplay += PredictionManager_OnPostReplicateReplay;
manager.PredictionManager.OnPostReconcile += PredictionManager_OnPostReconcile;
manager.TimeManager.OnPreTick += TimeManager_OnPreTick;
manager.TimeManager.OnPostTick += TimeManager_OnPostTick;
}
else
{
manager.PredictionManager.OnPreReconcile -= PredictionManager_OnPreReconcile;
manager.PredictionManager.OnReconcile -= PredictionManager_OnReconcile;
manager.PredictionManager.OnReplicateReplay -= PredictionManager_OnReplicateReplay;
manager.PredictionManager.OnPostReplicateReplay -= PredictionManager_OnPostReplicateReplay;
manager.PredictionManager.OnPostReconcile -= PredictionManager_OnPostReconcile;
manager.TimeManager.OnPreTick -= TimeManager_OnPreTick;
manager.TimeManager.OnPostTick -= TimeManager_OnPostTick;
}
}
@ -201,25 +256,75 @@ namespace FishNet.Object
}
else
{
if (_tickSmoother == null)
_tickSmoother = ResettableObjectCaches<LocalTransformTickSmoother>.Retrieve();
float teleportT = (_enableTeleport) ? _teleportThreshold : MoveRatesCls.UNSET_VALUE;
_tickSmoother.InitializeOnce(_graphicalObject, teleportT, (float)TimeManager.TickDelta, _ownerInterpolation);
if (PredictionSmoother == null)
PredictionSmoother = ResettableObjectCaches<ChildTransformTickSmoother>.Retrieve();
InitializeTickSmoother();
}
}
/// <summary>
/// Initializes the tick smoother.
/// </summary>
private void InitializeTickSmoother()
{
if (PredictionSmoother == null)
return;
float teleportT = (_enableTeleport) ? _teleportThreshold : MoveRatesCls.UNSET_VALUE;
PredictionSmoother.Initialize(this, _graphicalObject, _detachGraphicalObject, teleportT, (float)TimeManager.TickDelta, _ownerInterpolation, _ownerSmoothedProperties, _spectatorInterpolation, _spectatorSmoothedProperties, _adaptiveInterpolation);
}
/// <summary>
/// Initializes tick smoothing.
/// </summary>
private void DeinitializeSmoothers()
{
if (_tickSmoother != null)
if (PredictionSmoother != null)
{
ResettableObjectCaches<LocalTransformTickSmoother>.StoreAndDefault(ref _tickSmoother);
PredictionSmoother.Deinitialize();
ResettableObjectCaches<ChildTransformTickSmoother>.Store(PredictionSmoother);
PredictionSmoother = null;
ResettableObjectCaches<RigidbodyPauser>.StoreAndDefault(ref _rigidbodyPauser);
}
}
private void InvokeStartCallbacks_Prediction(bool asServer)
{
if (_predictionBehaviours.Count == 0)
return;
if (!asServer)
PredictionSmoother?.OnStartClient();
}
private void InvokeStopCallbacks_Prediction(bool asServer)
{
if (_predictionBehaviours.Count == 0)
return;
if (!asServer)
PredictionSmoother?.OnStopClient();
}
private void TimeManager_OnPreTick()
{
PredictionSmoother?.OnPreTick();
}
private void PredictionManager_OnPostReplicateReplay(uint clientTick, uint serverTick)
{
PredictionSmoother?.OnPostReplay(clientTick);
}
private void TimeManager_OnPostTick()
{
PredictionSmoother?.OnPostTick(NetworkManager.TimeManager.LocalTick);
}
private void PredictionManager_OnPreReconcile(uint clientTick, uint serverTick)
{
PredictionSmoother?.OnPreReconcile();
}
private void PredictionManager_OnReconcile(uint clientReconcileTick, uint serverReconcileTick)
{
if (!IsObjectReconciling)

View File

@ -337,9 +337,25 @@ namespace FishNet.Object
private void OnDestroy()
{
//Already being deinitialized by FishNet.
/* If already deinitializing then FishNet is in the process of,
* or has finished cleaning up this object. */
//callStopNetwork = (ServerManager.Objects.GetFromPending(ObjectId) == null);
if (IsDeinitializing)
return;
{
/* There is however chance the object can get destroyed before deinitializing
* as clientHost. If not clientHost its safe to skip deinitializing again.
* But if clientHost, check if the client has deinitialized. If not then do
* so now for the client side. */
if (IsHostStarted)
{
if (!_onStartClientCalled)
return;
}
else
{
return;
}
}
Owner?.RemoveObject(this);
NetworkObserver?.Deinitialize(true);
@ -349,12 +365,12 @@ namespace FishNet.Object
//Was destroyed without going through the proper methods.
if (NetworkManager.IsServerStarted)
{
DeinitializePrediction_V2(true);
Deinitialize_Prediction(true);
NetworkManager.ServerManager.Objects.NetworkObjectUnexpectedlyDestroyed(this, true);
}
if (NetworkManager.IsClientStarted)
{
DeinitializePrediction_V2(false);
Deinitialize_Prediction(false);
NetworkManager.ClientManager.Objects.NetworkObjectUnexpectedlyDestroyed(this, false);
}
}
@ -364,9 +380,9 @@ namespace FishNet.Object
* the server or client side, so send callbacks
* for both. */
if (IsServerStarted)
InvokeStopCallbacks(true);
InvokeStopCallbacks(true, true);
if (IsClientStarted)
InvokeStopCallbacks(false);
InvokeStopCallbacks(false, true);
/* If owner exist then remove object from owner.
* This has to be called here as well OnDisable because
@ -392,7 +408,7 @@ namespace FishNet.Object
//Do not need to set state if being destroyed.
//Don't need to reset sync types if object is being destroyed.
void DeinitializePrediction_V2(bool asServer)
void Deinitialize_Prediction(bool asServer)
{
#if !PREDICTION_1
Prediction_Deinitialize(asServer);
@ -817,7 +833,7 @@ namespace FishNet.Object
internal void Initialize(bool asServer, bool invokeSyncTypeCallbacks)
{
SetInitializedStatus(true, asServer);
InitializeCallbacks(asServer, invokeSyncTypeCallbacks);
InvokeStartCallbacks(asServer, invokeSyncTypeCallbacks);
}
/// <summary>
@ -828,7 +844,7 @@ namespace FishNet.Object
#if !PREDICTION_1
Prediction_Deinitialize(asServer);
#endif
InvokeStopCallbacks(asServer);
InvokeStopCallbacks(asServer, true);
for (int i = 0; i < NetworkBehaviours.Length; i++)
NetworkBehaviours[i].Deinitialize(asServer);
@ -946,7 +962,7 @@ namespace FishNet.Object
//After changing owners invoke callbacks.
InvokeOwnershipChange(prevOwner, asServer);
//If asServer send updates to clients as needed.
if (asServer)
{
@ -1025,35 +1041,35 @@ namespace FishNet.Object
/// Returns if this NetworkObject is a scene object, and has changed.
/// </summary>
/// <returns></returns>
internal ChangedTransformProperties GetTransformChanges(TransformProperties stp)
internal TransformPropertiesFlag GetTransformChanges(TransformProperties stp)
{
ChangedTransformProperties ctp = ChangedTransformProperties.Unset;
TransformPropertiesFlag tpf = TransformPropertiesFlag.Unset;
if (transform.localPosition != stp.Position)
ctp |= ChangedTransformProperties.LocalPosition;
tpf |= TransformPropertiesFlag.Position;
if (transform.localRotation != stp.Rotation)
ctp |= ChangedTransformProperties.LocalRotation;
tpf |= TransformPropertiesFlag.Rotation;
if (transform.localScale != stp.LocalScale)
ctp |= ChangedTransformProperties.LocalScale;
tpf |= TransformPropertiesFlag.LocalScale;
return ctp;
return tpf;
}
/// <summary>
/// Returns if this NetworkObject is a scene object, and has changed.
/// </summary>
/// <returns></returns>
internal ChangedTransformProperties GetTransformChanges(GameObject prefab)
internal TransformPropertiesFlag GetTransformChanges(GameObject prefab)
{
Transform t = prefab.transform;
ChangedTransformProperties ctp = ChangedTransformProperties.Unset;
TransformPropertiesFlag tpf = TransformPropertiesFlag.Unset;
if (transform.position != t.position)
ctp |= ChangedTransformProperties.LocalPosition;
tpf |= TransformPropertiesFlag.Position;
if (transform.rotation != t.rotation)
ctp |= ChangedTransformProperties.LocalRotation;
tpf |= TransformPropertiesFlag.Rotation;
if (transform.localScale != t.localScale)
ctp |= ChangedTransformProperties.LocalScale;
tpf |= TransformPropertiesFlag.LocalScale;
return ctp;
return tpf;
}
#region Editor.

Some files were not shown because too many files have changed in this diff Show More