chore: updated FishNet
This commit is contained in:
parent
79a147dead
commit
b69f2aff47
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1525dbf8ebd59e438b504fa19c4fd6c
|
||||
guid: baec5a367bebced4da1b56dbcedde312
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
@ -234,6 +235,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>
|
||||
/// Creaters serializers and calls for IBroadcast.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
{
|
||||
@ -1045,8 +1046,18 @@ namespace FishNet.CodeGenerating.Helping
|
||||
}
|
||||
|
||||
/* If here then not null. */
|
||||
//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
|
||||
|
||||
@ -218,10 +218,7 @@ namespace FishNet.CodeGenerating.Processing.Rpc
|
||||
{
|
||||
RpcType rpcType = base.GetClass<AttributeHelper>().GetRpcAttributeType(customAttribute);
|
||||
if (rpcType != RpcType.None)
|
||||
{
|
||||
count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -80,7 +80,8 @@ namespace FishNet.CodeGenerating.Helping
|
||||
/// </summary>
|
||||
public static readonly string[] EXCLUDED_ASSEMBLY_PREFIXES = new string[]
|
||||
{
|
||||
"UnityEngine."
|
||||
"UnityEngine.",
|
||||
"Unity.Mathmatics",
|
||||
};
|
||||
#endregion
|
||||
|
||||
|
||||
@ -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: []
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab6a9000f5ff83f45b6761c2a3be018d
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
|
||||
@ -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}
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9693011b7416444aa06cafe900e23c0
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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}
|
||||
|
||||
@ -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}
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2863463f6933f3f439a639f883f642f6
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90949fa81eb0c194080a75b84fa699d9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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: []
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dcdf15fc89edd2546baf8ff544470626
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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}
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0078a285cda09f349b2668a721a53212
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e04426ec3811c4439a20fc6f21abb62
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: accc5b93ebf3b0040baaec2298c7d394
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bdae6fa333d2d94449c27856e21d936a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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}
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2ca0973c8f8a4846bb7f3894c3bc5cf
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50d54accc2af0c746b0729b097981b93
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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}
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eaf414b899169f04bb4b0d65424a40bf
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@ -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.
|
||||
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43f99d5241a617843a16537c3229c093
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1603123a7ba837c4892564f087decd80
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f31e665d963214449f27dc0ad466dfb
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c147d08de81079a4c8f17246c4e49dc5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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}
|
||||
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ef354bfc16ca8a459074c3fa9b6727e
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf02fdf4a8ce43c489e5b5d8e65cb87d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@ -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:
|
||||
|
||||
208
Assets/FishNet/Demos/Prefabs/NetworkManager.prefab
Normal file
208
Assets/FishNet/Demos/Prefabs/NetworkManager.prefab
Normal 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}
|
||||
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b712878ecece354ba4ffb026c0a221c
|
||||
guid: 0b650fca685f2eb41a86538aa883e4c1
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
@ -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}
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1b4d193fc9b1bb41a92600a22b3d34e
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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}
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 223fa01133c52c7469e6520b901bcebd
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,8 +60,10 @@ 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)
|
||||
{
|
||||
ArrayBuffer buffer = bufferPool.Take(source.Count);
|
||||
@ -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 (
|
||||
(skipEnabled || behaviour.enabled) &&
|
||||
processedCount < maxMessagesPerTick &&
|
||||
// Dequeue last
|
||||
server.receiveQueue.TryDequeue(out Message next)
|
||||
|
||||
@ -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,7 +41,7 @@ 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;
|
||||
@ -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));
|
||||
|
||||
@ -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": []
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -43,6 +43,7 @@ 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.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 { }
|
||||
}
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
@ -79,7 +84,10 @@ namespace FishNet.Connection
|
||||
{
|
||||
stateWriter = WriterPool.Retrieve(mtu);
|
||||
PredictionStateWriters.Add(stateWriter);
|
||||
stateWriter.Reserve(PredictionManager.STATE_HEADER_RESERVE_COUNT);
|
||||
stateWriter.Reserve(PredictionManager.STATE_HEADER_RESERVE_LENGTH);
|
||||
/// 2 PacketId.
|
||||
/// 4 Last replicate tick run for connection.
|
||||
/// 4 Length unpacked.
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -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() { }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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++)
|
||||
{
|
||||
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)
|
||||
const string fishnetVersionSave = "fishnet_version";
|
||||
string savedVersion = EditorPrefs.GetString(fishnetVersionSave, string.Empty);
|
||||
if (savedVersion != NetworkManager.FISHNET_VERSION)
|
||||
{
|
||||
fullRebuild = true;
|
||||
return;
|
||||
}
|
||||
EditorPrefs.SetString(fishnetVersionSave, NetworkManager.FISHNET_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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. */
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,7 +434,8 @@ namespace FishNet.Managing.Client
|
||||
if (sceneObject)
|
||||
{
|
||||
ReadSceneObject(reader, out sceneId);
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
#if DEVELOPMENT
|
||||
if (NetworkManager.ClientManager.IsServerDevelopment)
|
||||
base.CheckReadSceneObjectDetails(reader, ref sceneName, ref objectName);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
#define DEVELOPMENT
|
||||
#endif
|
||||
#if DEVELOPMENT
|
||||
using FishNet.Managing.Logging;
|
||||
using FishNet.Object;
|
||||
using FishNet.Serializing;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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--;
|
||||
|
||||
|
||||
@ -78,3 +124,6 @@ namespace FishNet.Managing.Predicting.Editing
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@ -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.
|
||||
/// 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 RedundancyCount => _redundancyCount;
|
||||
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,70 +316,48 @@ 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;
|
||||
|
||||
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. */
|
||||
@ -432,7 +405,7 @@ namespace FishNet.Managing.Predicting
|
||||
}
|
||||
|
||||
bool timeManagerPhysics = (tm.PhysicsMode == PhysicsMode.TimeManager);
|
||||
float tickDelta = (float)tm.TickDelta;
|
||||
float tickDelta = ((float)tm.TickDelta * _networkManager.TimeManager.GetPhysicsTimeScale());
|
||||
|
||||
OnPreReconcile?.Invoke(ClientStateTick, ServerStateTick);
|
||||
OnReconcile?.Invoke(ClientStateTick, ServerStateTick);
|
||||
@ -456,7 +429,6 @@ namespace FishNet.Managing.Predicting
|
||||
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
|
||||
@ -467,7 +439,6 @@ namespace FishNet.Managing.Predicting
|
||||
* since the OnTick has not run yet. */
|
||||
while (ClientReplayTick < localTick - 1)
|
||||
{
|
||||
replays++;
|
||||
OnPreReplicateReplay?.Invoke(ClientReplayTick, ServerReplayTick);
|
||||
OnReplicateReplay?.Invoke(ClientReplayTick, ServerReplayTick);
|
||||
if (timeManagerPhysics)
|
||||
@ -491,35 +462,28 @@ namespace FishNet.Managing.Predicting
|
||||
|
||||
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)
|
||||
//If client has performed a replicate recently.
|
||||
if (!nc.ReplicateTick.IsUnset && nc.ReplicateTick.LocalTickDifference(_networkManager.TimeManager) < recentReplicateToTicks)
|
||||
lastReplicateTick = nc.ReplicateTick.Value();
|
||||
else
|
||||
lastReplicateTick = nc.ReplicateTick.LastRemoteTick;
|
||||
}
|
||||
/* 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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,43 +1087,27 @@ 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)
|
||||
if (Mathf.Abs(difference) > maximumDifference)
|
||||
{
|
||||
_adjustedTickDelta = TickDelta;
|
||||
}
|
||||
//Otherwise adjust the delta marginally.
|
||||
else
|
||||
else if (difference != 0)
|
||||
{
|
||||
/* A negative tickDifference indicates the client is
|
||||
* moving too fast, while positive indicates too slow. */
|
||||
bool speedUp = (tickDifference > 0);
|
||||
bool speedUp = (difference > 0);
|
||||
ChangeAdjustedTickDelta(speedUp);
|
||||
}
|
||||
// }
|
||||
//Recalculates Tick value if it exceeds maximum difference.
|
||||
void TryRecalculateTick()
|
||||
{
|
||||
uint rttTicks = TimeToTicks((RoundTripTime / 2) / 1000f);
|
||||
uint newValue = lastPacketTick + rttTicks;
|
||||
long difference = (long)Mathf.Abs((long)Tick - (long)newValue);
|
||||
if (difference > maximumDifference)
|
||||
Tick = newValue;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -1,5 +1,6 @@
|
||||
#if UNITY_EDITOR
|
||||
#if !PREDICTION_1
|
||||
using FishNet.Object.Prediction;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
@ -21,7 +22,13 @@ 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;
|
||||
|
||||
@ -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,9 +91,23 @@ namespace FishNet.Object.Editing
|
||||
EditorGUILayout.PropertyField(_networkTransform);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
bool graphicalSet = (_graphicalObject.objectReferenceValue != null);
|
||||
EditorGUILayout.PropertyField(_graphicalObject);
|
||||
if (graphicalSet)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
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(_ownerInterpolation, new GUIContent("Interpolation"));
|
||||
EditorGUILayout.PropertyField(_enableTeleport);
|
||||
if (_enableTeleport.boolValue == true)
|
||||
{
|
||||
@ -91,7 +116,25 @@ namespace FishNet.Object.Editing
|
||||
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--;
|
||||
|
||||
@ -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))
|
||||
@ -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 the first replay.
|
||||
if (replayTick == (_networkObjectCache.PredictionManager.ServerStateTick + 1))
|
||||
{
|
||||
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;
|
||||
}
|
||||
//If not not found then it's being run as predicted.
|
||||
else
|
||||
{
|
||||
SetDataToDefault();
|
||||
}
|
||||
}
|
||||
//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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -837,7 +846,7 @@ namespace FishNet.Object
|
||||
* 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))
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
if (manager == null)
|
||||
return;
|
||||
|
||||
if (subscribe)
|
||||
{
|
||||
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)
|
||||
|
||||
@ -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)
|
||||
{
|
||||
/* 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);
|
||||
|
||||
@ -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
Loading…
Reference in New Issue
Block a user