Added Reflection based SyncVar system

This commit is contained in:
Albin Corén 2018-03-07 20:56:24 +01:00
parent 352c827b5d
commit dc6afc1331
7 changed files with 463 additions and 2 deletions

View File

@ -0,0 +1,19 @@
namespace MLAPI.Attributes
{
internal enum FieldType
{
Bool,
Byte,
Char,
Double,
Single,
Int,
Long,
SByte,
Short,
UInt,
ULong,
UShort,
String
}
}

View File

@ -0,0 +1,10 @@
using System;
namespace MLAPI.Attributes
{
[AttributeUsage(AttributeTargets.Field)]
public class SyncedVar : Attribute
{
}
}

View File

@ -41,7 +41,7 @@
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@ -56,6 +56,8 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Attributes\FieldType.cs" />
<Compile Include="Attributes\SyncedVar.cs" />
<Compile Include="Data\NetworkPool.cs" />
<Compile Include="Data\TrackedPointData.cs" />
<Compile Include="MonoBehaviours\Prototyping\NetworkedAnimator.cs" />
@ -71,6 +73,7 @@
<Compile Include="NetworkingManagerComponents\NetworkPoolManager.cs" />
<Compile Include="NetworkingManagerComponents\NetworkSceneManager.cs" />
<Compile Include="NetworkingManagerComponents\SpawnManager.cs" />
<Compile Include="NetworkingManagerComponents\SyncedVarManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@ -2,11 +2,16 @@
using System.Collections.Generic;
using UnityEngine;
using MLAPI.NetworkingManagerComponents;
using System.Reflection;
using MLAPI.Attributes;
using System.Linq;
using System.IO;
namespace MLAPI
{
public abstract class NetworkedBehaviour : MonoBehaviour
{
public float SyncVarSyncDelay = 0.1f;
public bool isLocalPlayer
{
get
@ -79,6 +84,7 @@ namespace MLAPI
{
_networkedObject = GetComponentInParent<NetworkedObject>();
}
NetworkedObject.networkedBehaviours.Add(this);
}
internal bool networkedStartInvoked = false;
@ -109,6 +115,11 @@ namespace MLAPI
MessageManager.RemoveIncomingMessageHandler(name, counter, networkId);
}
private void OnDisable()
{
NetworkedObject.networkedBehaviours.Remove(this);
}
private void OnDestroy()
{
foreach (KeyValuePair<string, int> pair in registeredMessageHandlers)
@ -117,6 +128,291 @@ namespace MLAPI
}
}
#region SYNC_VAR
private List<FieldInfo> syncedFields = new List<FieldInfo>();
internal List<FieldType> syncedFieldTypes = new List<FieldType>();
private List<object> syncedFieldValues = new List<object>();
//A dirty field is a field that's not synced.
public bool[] dirtyFields;
protected static ushort networkedBehaviourId;
//This is just for the unity editor. if you turn the editor to DEBUG mode you can see what the networkedBehaviourId is.
private ushort _networkedBehaviourId = networkedBehaviourId;
internal void SyncVarInit()
{
FieldInfo[] sortedFields = GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance).OrderBy(x => x.Name).ToArray();
for (byte i = 0; i < sortedFields.Length; i++)
{
if(sortedFields[i].IsDefined(typeof(SyncedVar), true))
{
if (sortedFields[i].FieldType == typeof(bool))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.Bool);
}
else if(sortedFields[i].FieldType == typeof(byte))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.Byte);
}
else if (sortedFields[i].FieldType == typeof(char))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.Char);
}
else if (sortedFields[i].FieldType == typeof(double))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.Double);
}
else if (sortedFields[i].FieldType == typeof(float))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.Single);
}
else if (sortedFields[i].FieldType == typeof(int))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.Int);
}
else if (sortedFields[i].FieldType == typeof(long))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.Long);
}
else if (sortedFields[i].FieldType == typeof(sbyte))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.SByte);
}
else if (sortedFields[i].FieldType == typeof(short))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.Short);
}
else if (sortedFields[i].FieldType == typeof(uint))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.UInt);
}
else if (sortedFields[i].FieldType == typeof(ulong))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.ULong);
}
else if (sortedFields[i].FieldType == typeof(ushort))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.UShort);
}
else if(sortedFields[i].FieldType == typeof(string))
{
syncedFields.Add(sortedFields[i]);
syncedFieldValues.Add(sortedFields[i].GetValue(this));
syncedFieldTypes.Add(FieldType.String);
}
else
{
Debug.LogError("MLAPI: The type " + sortedFields[i].FieldType.ToString() + " can not be used as a syncvar");
}
}
}
if(dirtyFields.Length > 255)
{
Debug.LogError("MLAPI: You can not have more than 255 SyncVar's per NetworkedBehaviour!");
}
dirtyFields = new bool[syncedFields.Count];
}
internal void OnSyncVarUpdate(object value, byte fieldIndex)
{
if (isServer)
return;
syncedFields[fieldIndex].SetValue(this, value);
}
private float lastSyncTime = 0f;
internal void SyncvarUpdate()
{
if (!isServer)
return;
SetDirtyness();
if(Time.time - lastSyncTime >= SyncVarSyncDelay)
{
byte dirtyCount = (byte)dirtyFields.Count(x => x == true);
if (dirtyCount == 0)
return; //All up to date!
//It's sync time!
using (MemoryStream stream = new MemoryStream())
{
using(BinaryWriter writer = new BinaryWriter(stream))
{
//Write all indexes
writer.Write(dirtyCount);
for (byte i = 0; i < dirtyFields.Length; i++)
{
//Writes all the indexes of the dirty syncvars.
if (dirtyFields[i] == true)
{
writer.Write(networkId);
writer.Write(networkedObject.GetOrderIndex(this));
writer.Write(networkedBehaviourId);
writer.Write(i); //Index
switch (syncedFieldTypes[i])
{
case FieldType.Bool:
writer.Write((bool)syncedFields[i].GetValue(this));
break;
case FieldType.Byte:
writer.Write((byte)syncedFields[i].GetValue(this));
break;
case FieldType.Char:
writer.Write((char)syncedFields[i].GetValue(this));
break;
case FieldType.Double:
writer.Write((double)syncedFields[i].GetValue(this));
break;
case FieldType.Single:
writer.Write((float)syncedFields[i].GetValue(this));
break;
case FieldType.Int:
writer.Write((int)syncedFields[i].GetValue(this));
break;
case FieldType.Long:
writer.Write((long)syncedFields[i].GetValue(this));
break;
case FieldType.SByte:
writer.Write((sbyte)syncedFields[i].GetValue(this));
break;
case FieldType.Short:
writer.Write((short)syncedFields[i].GetValue(this));
break;
case FieldType.UInt:
writer.Write((uint)syncedFields[i].GetValue(this));
break;
case FieldType.ULong:
writer.Write((ulong)syncedFields[i].GetValue(this));
break;
case FieldType.UShort:
writer.Write((ushort)syncedFields[i].GetValue(this));
break;
case FieldType.String:
writer.Write((string)syncedFields[i].GetValue(this));
break;
}
syncedFieldValues[i] = syncedFields[i].GetValue(this);
dirtyFields[i] = false;
}
}
NetworkingManager.singleton.Send("MLAPI_SYNC_VAR_UPDATE", "MLAPI_RELIABLE_FRAGMENTED_SEQUENCED", stream.ToArray(), ownerClientId);
}
}
lastSyncTime = Time.time;
}
}
private void SetDirtyness()
{
if (!isServer)
return;
for (int i = 0; i < syncedFields.Count; i++)
{
switch (syncedFieldTypes[i])
{
case FieldType.Bool:
if ((bool)syncedFields[i].GetValue(this) != (bool)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
case FieldType.Byte:
if ((byte)syncedFields[i].GetValue(this) != (byte)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
case FieldType.Char:
if ((char)syncedFields[i].GetValue(this) != (char)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
case FieldType.Double:
if ((double)syncedFields[i].GetValue(this) != (double)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
case FieldType.Single:
if ((float)syncedFields[i].GetValue(this) != (float)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
case FieldType.Int:
if ((int)syncedFields[i].GetValue(this) != (int)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
case FieldType.Long:
if ((long)syncedFields[i].GetValue(this) != (long)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
case FieldType.SByte:
if ((sbyte)syncedFields[i].GetValue(this) != (sbyte)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
case FieldType.Short:
if ((short)syncedFields[i].GetValue(this) != (short)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
case FieldType.UInt:
if ((uint)syncedFields[i].GetValue(this) != (uint)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
case FieldType.ULong:
if ((ulong)syncedFields[i].GetValue(this) != (ulong)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
case FieldType.UShort:
if ((ushort)syncedFields[i].GetValue(this) != (ushort)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
case FieldType.String:
if ((string)syncedFields[i].GetValue(this) != (string)syncedFieldValues[i])
dirtyFields[i] = true; //This fields value is out of sync!
else
dirtyFields[i] = false; //Up to date
break;
}
}
}
#endregion
protected void SendToServer(string messageType, string channelName, byte[] data)
{
if(MessageManager.messageTypes[messageType] < 32)

View File

@ -1,4 +1,5 @@
using MLAPI.NetworkingManagerComponents;
using System.Collections.Generic;
using UnityEngine;
namespace MLAPI
@ -96,8 +97,35 @@ namespace MLAPI
if(netBehaviours[i].networkedObject == this && !netBehaviours[i].networkedStartInvoked)
{
netBehaviours[i].NetworkStart();
netBehaviours[i].SyncVarInit();
}
}
}
internal static List<NetworkedBehaviour> networkedBehaviours = new List<NetworkedBehaviour>();
internal static void InvokeSyncvarUpdate()
{
for (int i = 0; i < networkedBehaviours.Count; i++)
{
networkedBehaviours[i].SyncvarUpdate();
}
}
internal ushort GetOrderIndex(NetworkedBehaviour instance)
{
NetworkedBehaviour[] behaviours = GetComponentsInChildren<NetworkedBehaviour>();
for (ushort i = 0; i < behaviours.Length; i++)
{
if (behaviours[i].networkedObject == this && behaviours[i] == instance)
return i;
}
return 0;
}
internal NetworkedBehaviour GetBehaviourAtOrderIndex(ushort index)
{
NetworkedBehaviour[] behaviours = GetComponentsInChildren<NetworkedBehaviour>();
return behaviours[index];
}
}
}

View File

@ -1,4 +1,5 @@
using MLAPI.NetworkingManagerComponents;
using MLAPI.Attributes;
using MLAPI.NetworkingManagerComponents;
using System;
using System.Collections;
using System.Collections.Generic;
@ -73,6 +74,7 @@ namespace MLAPI
{
NetworkConfig = netConfig;
SyncedVarManager.Init();
pendingClients = new HashSet<int>();
connectedClients = new Dictionary<int, NetworkedClient>();
messageBuffer = new byte[NetworkConfig.MessageBufferSize];
@ -117,6 +119,7 @@ namespace MLAPI
MessageManager.messageTypes.Add("MLAPI_SPAWN_POOL_OBJECT", 6);
MessageManager.messageTypes.Add("MLAPI_DESTROY_POOL_OBJECT", 7);
MessageManager.messageTypes.Add("MLAPI_CHANGE_OWNER", 8);
MessageManager.messageTypes.Add("MLAPI_SYNC_VAR_UPDATE", 9);
NetworkConfig.MessageTypes.Add("MLAPI_OnRecieveTransformFromClient");
NetworkConfig.MessageTypes.Add("MLAPI_OnRecieveTransformFromServer");
NetworkConfig.MessageTypes.Add("MLAPI_HandleAnimationMessage");
@ -361,6 +364,8 @@ namespace MLAPI
}
if (isServer)
LagCompensationManager.AddFrames();
NetworkedObject.InvokeSyncvarUpdate();
}
private IEnumerator ApprovalTimeout(int clientId)
@ -677,6 +682,71 @@ namespace MLAPI
}
}
break;
case 9:
if (isClient)
{
using (MemoryStream messageReadStream = new MemoryStream(incommingData))
{
using (BinaryReader messageReader = new BinaryReader(messageReadStream))
{
byte dirtyCount = messageReader.ReadByte();
if(dirtyCount > 0)
{
for (int i = 0; i < dirtyCount; i++)
{
uint netId = messageReader.ReadUInt32(); //NetId the syncvar is from
ushort orderIndex = messageReader.ReadUInt16();
ushort networkBehaviourId = messageReader.ReadUInt16();
byte fieldIndex = messageReader.ReadByte();
FieldType type = SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).syncedFieldTypes[fieldIndex];
switch (type)
{
case FieldType.Bool:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadBoolean(), fieldIndex);
break;
case FieldType.Byte:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadByte(), fieldIndex);
break;
case FieldType.Char:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadChar(), fieldIndex);
break;
case FieldType.Double:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadDouble(), fieldIndex);
break;
case FieldType.Single:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadSingle(), fieldIndex);
break;
case FieldType.Int:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadInt32(), fieldIndex);
break;
case FieldType.Long:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadInt64(), fieldIndex);
break;
case FieldType.SByte:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadSByte(), fieldIndex);
break;
case FieldType.Short:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadInt16(), fieldIndex);
break;
case FieldType.UInt:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadUInt32(), fieldIndex);
break;
case FieldType.ULong:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadUInt64(), fieldIndex);
break;
case FieldType.UShort:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadUInt16(), fieldIndex);
break;
case FieldType.String:
SpawnManager.spawnedObjects[netId].GetBehaviourAtOrderIndex(orderIndex).OnSyncVarUpdate(messageReader.ReadString(), fieldIndex);
break;
}
}
}
}
}
}
break;
}
}
}

View File

@ -0,0 +1,35 @@
using MLAPI.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace MLAPI.NetworkingManagerComponents
{
internal static class SyncedVarManager
{
internal static void Init()
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
Assembly assembly = Assembly.GetExecutingAssembly();
for (int i = 0; i < assemblies.Length; i++)
{
if (assemblies[i].FullName == "Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")
{
assembly = assemblies[i];
break;
}
}
IEnumerable<Type> types = from t in assembly.GetTypes()
where t.IsClass && t.IsSubclassOf(typeof(NetworkedBehaviour))
select t;
List<Type> behaviourTypes = types.OrderBy(x => x.FullName).ToList();
for (ushort i = 0; i < behaviourTypes.Count; i++)
{
FieldInfo networkedBehaviourId = behaviourTypes[i].GetField("networkedBehaviourId", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy);
networkedBehaviourId.SetValue(null, i);
}
}
}
}