Skip to content

Commit dc6afc1

Browse files
committed
Added Reflection based SyncVar system
1 parent 352c827 commit dc6afc1

File tree

7 files changed

+463
-2
lines changed

7 files changed

+463
-2
lines changed

MLAPI/Attributes/FieldType.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace MLAPI.Attributes
2+
{
3+
internal enum FieldType
4+
{
5+
Bool,
6+
Byte,
7+
Char,
8+
Double,
9+
Single,
10+
Int,
11+
Long,
12+
SByte,
13+
Short,
14+
UInt,
15+
ULong,
16+
UShort,
17+
String
18+
}
19+
}

MLAPI/Attributes/SyncedVar.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
3+
namespace MLAPI.Attributes
4+
{
5+
[AttributeUsage(AttributeTargets.Field)]
6+
public class SyncedVar : Attribute
7+
{
8+
9+
}
10+
}

MLAPI/MLAPI.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
<PlatformTarget>AnyCPU</PlatformTarget>
4242
<ErrorReport>prompt</ErrorReport>
4343
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
44-
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
44+
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
4545
</PropertyGroup>
4646
<ItemGroup>
4747
<Reference Include="System" />
@@ -56,6 +56,8 @@
5656
</Reference>
5757
</ItemGroup>
5858
<ItemGroup>
59+
<Compile Include="Attributes\FieldType.cs" />
60+
<Compile Include="Attributes\SyncedVar.cs" />
5961
<Compile Include="Data\NetworkPool.cs" />
6062
<Compile Include="Data\TrackedPointData.cs" />
6163
<Compile Include="MonoBehaviours\Prototyping\NetworkedAnimator.cs" />
@@ -71,6 +73,7 @@
7173
<Compile Include="NetworkingManagerComponents\NetworkPoolManager.cs" />
7274
<Compile Include="NetworkingManagerComponents\NetworkSceneManager.cs" />
7375
<Compile Include="NetworkingManagerComponents\SpawnManager.cs" />
76+
<Compile Include="NetworkingManagerComponents\SyncedVarManager.cs" />
7477
<Compile Include="Properties\AssemblyInfo.cs" />
7578
</ItemGroup>
7679
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

MLAPI/MonoBehaviours/Core/NetworkedBehaviour.cs

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@
22
using System.Collections.Generic;
33
using UnityEngine;
44
using MLAPI.NetworkingManagerComponents;
5+
using System.Reflection;
6+
using MLAPI.Attributes;
7+
using System.Linq;
8+
using System.IO;
59

610
namespace MLAPI
711
{
812
public abstract class NetworkedBehaviour : MonoBehaviour
913
{
14+
public float SyncVarSyncDelay = 0.1f;
1015
public bool isLocalPlayer
1116
{
1217
get
@@ -79,6 +84,7 @@ private void OnEnable()
7984
{
8085
_networkedObject = GetComponentInParent<NetworkedObject>();
8186
}
87+
NetworkedObject.networkedBehaviours.Add(this);
8288
}
8389

8490
internal bool networkedStartInvoked = false;
@@ -109,6 +115,11 @@ protected void DeregisterMessageHandler(string name, int counter)
109115
MessageManager.RemoveIncomingMessageHandler(name, counter, networkId);
110116
}
111117

118+
private void OnDisable()
119+
{
120+
NetworkedObject.networkedBehaviours.Remove(this);
121+
}
122+
112123
private void OnDestroy()
113124
{
114125
foreach (KeyValuePair<string, int> pair in registeredMessageHandlers)
@@ -117,6 +128,291 @@ private void OnDestroy()
117128
}
118129
}
119130

131+
#region SYNC_VAR
132+
private List<FieldInfo> syncedFields = new List<FieldInfo>();
133+
internal List<FieldType> syncedFieldTypes = new List<FieldType>();
134+
private List<object> syncedFieldValues = new List<object>();
135+
//A dirty field is a field that's not synced.
136+
public bool[] dirtyFields;
137+
protected static ushort networkedBehaviourId;
138+
//This is just for the unity editor. if you turn the editor to DEBUG mode you can see what the networkedBehaviourId is.
139+
private ushort _networkedBehaviourId = networkedBehaviourId;
140+
internal void SyncVarInit()
141+
{
142+
FieldInfo[] sortedFields = GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance).OrderBy(x => x.Name).ToArray();
143+
for (byte i = 0; i < sortedFields.Length; i++)
144+
{
145+
if(sortedFields[i].IsDefined(typeof(SyncedVar), true))
146+
{
147+
if (sortedFields[i].FieldType == typeof(bool))
148+
{
149+
syncedFields.Add(sortedFields[i]);
150+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
151+
syncedFieldTypes.Add(FieldType.Bool);
152+
}
153+
else if(sortedFields[i].FieldType == typeof(byte))
154+
{
155+
syncedFields.Add(sortedFields[i]);
156+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
157+
syncedFieldTypes.Add(FieldType.Byte);
158+
}
159+
else if (sortedFields[i].FieldType == typeof(char))
160+
{
161+
syncedFields.Add(sortedFields[i]);
162+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
163+
syncedFieldTypes.Add(FieldType.Char);
164+
}
165+
else if (sortedFields[i].FieldType == typeof(double))
166+
{
167+
syncedFields.Add(sortedFields[i]);
168+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
169+
syncedFieldTypes.Add(FieldType.Double);
170+
}
171+
else if (sortedFields[i].FieldType == typeof(float))
172+
{
173+
syncedFields.Add(sortedFields[i]);
174+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
175+
syncedFieldTypes.Add(FieldType.Single);
176+
}
177+
else if (sortedFields[i].FieldType == typeof(int))
178+
{
179+
syncedFields.Add(sortedFields[i]);
180+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
181+
syncedFieldTypes.Add(FieldType.Int);
182+
}
183+
else if (sortedFields[i].FieldType == typeof(long))
184+
{
185+
syncedFields.Add(sortedFields[i]);
186+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
187+
syncedFieldTypes.Add(FieldType.Long);
188+
}
189+
else if (sortedFields[i].FieldType == typeof(sbyte))
190+
{
191+
syncedFields.Add(sortedFields[i]);
192+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
193+
syncedFieldTypes.Add(FieldType.SByte);
194+
}
195+
else if (sortedFields[i].FieldType == typeof(short))
196+
{
197+
syncedFields.Add(sortedFields[i]);
198+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
199+
syncedFieldTypes.Add(FieldType.Short);
200+
}
201+
else if (sortedFields[i].FieldType == typeof(uint))
202+
{
203+
syncedFields.Add(sortedFields[i]);
204+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
205+
syncedFieldTypes.Add(FieldType.UInt);
206+
}
207+
else if (sortedFields[i].FieldType == typeof(ulong))
208+
{
209+
syncedFields.Add(sortedFields[i]);
210+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
211+
syncedFieldTypes.Add(FieldType.ULong);
212+
}
213+
else if (sortedFields[i].FieldType == typeof(ushort))
214+
{
215+
syncedFields.Add(sortedFields[i]);
216+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
217+
syncedFieldTypes.Add(FieldType.UShort);
218+
}
219+
else if(sortedFields[i].FieldType == typeof(string))
220+
{
221+
syncedFields.Add(sortedFields[i]);
222+
syncedFieldValues.Add(sortedFields[i].GetValue(this));
223+
syncedFieldTypes.Add(FieldType.String);
224+
}
225+
else
226+
{
227+
Debug.LogError("MLAPI: The type " + sortedFields[i].FieldType.ToString() + " can not be used as a syncvar");
228+
}
229+
}
230+
}
231+
if(dirtyFields.Length > 255)
232+
{
233+
Debug.LogError("MLAPI: You can not have more than 255 SyncVar's per NetworkedBehaviour!");
234+
}
235+
dirtyFields = new bool[syncedFields.Count];
236+
}
237+
238+
internal void OnSyncVarUpdate(object value, byte fieldIndex)
239+
{
240+
if (isServer)
241+
return;
242+
syncedFields[fieldIndex].SetValue(this, value);
243+
}
244+
245+
private float lastSyncTime = 0f;
246+
internal void SyncvarUpdate()
247+
{
248+
if (!isServer)
249+
return;
250+
SetDirtyness();
251+
if(Time.time - lastSyncTime >= SyncVarSyncDelay)
252+
{
253+
byte dirtyCount = (byte)dirtyFields.Count(x => x == true);
254+
if (dirtyCount == 0)
255+
return; //All up to date!
256+
//It's sync time!
257+
using (MemoryStream stream = new MemoryStream())
258+
{
259+
using(BinaryWriter writer = new BinaryWriter(stream))
260+
{
261+
//Write all indexes
262+
writer.Write(dirtyCount);
263+
for (byte i = 0; i < dirtyFields.Length; i++)
264+
{
265+
//Writes all the indexes of the dirty syncvars.
266+
if (dirtyFields[i] == true)
267+
{
268+
writer.Write(networkId);
269+
writer.Write(networkedObject.GetOrderIndex(this));
270+
writer.Write(networkedBehaviourId);
271+
writer.Write(i); //Index
272+
switch (syncedFieldTypes[i])
273+
{
274+
case FieldType.Bool:
275+
writer.Write((bool)syncedFields[i].GetValue(this));
276+
break;
277+
case FieldType.Byte:
278+
writer.Write((byte)syncedFields[i].GetValue(this));
279+
break;
280+
case FieldType.Char:
281+
writer.Write((char)syncedFields[i].GetValue(this));
282+
break;
283+
case FieldType.Double:
284+
writer.Write((double)syncedFields[i].GetValue(this));
285+
break;
286+
case FieldType.Single:
287+
writer.Write((float)syncedFields[i].GetValue(this));
288+
break;
289+
case FieldType.Int:
290+
writer.Write((int)syncedFields[i].GetValue(this));
291+
break;
292+
case FieldType.Long:
293+
writer.Write((long)syncedFields[i].GetValue(this));
294+
break;
295+
case FieldType.SByte:
296+
writer.Write((sbyte)syncedFields[i].GetValue(this));
297+
break;
298+
case FieldType.Short:
299+
writer.Write((short)syncedFields[i].GetValue(this));
300+
break;
301+
case FieldType.UInt:
302+
writer.Write((uint)syncedFields[i].GetValue(this));
303+
break;
304+
case FieldType.ULong:
305+
writer.Write((ulong)syncedFields[i].GetValue(this));
306+
break;
307+
case FieldType.UShort:
308+
writer.Write((ushort)syncedFields[i].GetValue(this));
309+
break;
310+
case FieldType.String:
311+
writer.Write((string)syncedFields[i].GetValue(this));
312+
break;
313+
}
314+
syncedFieldValues[i] = syncedFields[i].GetValue(this);
315+
dirtyFields[i] = false;
316+
}
317+
}
318+
NetworkingManager.singleton.Send("MLAPI_SYNC_VAR_UPDATE", "MLAPI_RELIABLE_FRAGMENTED_SEQUENCED", stream.ToArray(), ownerClientId);
319+
}
320+
}
321+
lastSyncTime = Time.time;
322+
}
323+
}
324+
325+
private void SetDirtyness()
326+
{
327+
if (!isServer)
328+
return;
329+
for (int i = 0; i < syncedFields.Count; i++)
330+
{
331+
switch (syncedFieldTypes[i])
332+
{
333+
case FieldType.Bool:
334+
if ((bool)syncedFields[i].GetValue(this) != (bool)syncedFieldValues[i])
335+
dirtyFields[i] = true; //This fields value is out of sync!
336+
else
337+
dirtyFields[i] = false; //Up to date
338+
break;
339+
case FieldType.Byte:
340+
if ((byte)syncedFields[i].GetValue(this) != (byte)syncedFieldValues[i])
341+
dirtyFields[i] = true; //This fields value is out of sync!
342+
else
343+
dirtyFields[i] = false; //Up to date
344+
break;
345+
case FieldType.Char:
346+
if ((char)syncedFields[i].GetValue(this) != (char)syncedFieldValues[i])
347+
dirtyFields[i] = true; //This fields value is out of sync!
348+
else
349+
dirtyFields[i] = false; //Up to date
350+
break;
351+
case FieldType.Double:
352+
if ((double)syncedFields[i].GetValue(this) != (double)syncedFieldValues[i])
353+
dirtyFields[i] = true; //This fields value is out of sync!
354+
else
355+
dirtyFields[i] = false; //Up to date
356+
break;
357+
case FieldType.Single:
358+
if ((float)syncedFields[i].GetValue(this) != (float)syncedFieldValues[i])
359+
dirtyFields[i] = true; //This fields value is out of sync!
360+
else
361+
dirtyFields[i] = false; //Up to date
362+
break;
363+
case FieldType.Int:
364+
if ((int)syncedFields[i].GetValue(this) != (int)syncedFieldValues[i])
365+
dirtyFields[i] = true; //This fields value is out of sync!
366+
else
367+
dirtyFields[i] = false; //Up to date
368+
break;
369+
case FieldType.Long:
370+
if ((long)syncedFields[i].GetValue(this) != (long)syncedFieldValues[i])
371+
dirtyFields[i] = true; //This fields value is out of sync!
372+
else
373+
dirtyFields[i] = false; //Up to date
374+
break;
375+
case FieldType.SByte:
376+
if ((sbyte)syncedFields[i].GetValue(this) != (sbyte)syncedFieldValues[i])
377+
dirtyFields[i] = true; //This fields value is out of sync!
378+
else
379+
dirtyFields[i] = false; //Up to date
380+
break;
381+
case FieldType.Short:
382+
if ((short)syncedFields[i].GetValue(this) != (short)syncedFieldValues[i])
383+
dirtyFields[i] = true; //This fields value is out of sync!
384+
else
385+
dirtyFields[i] = false; //Up to date
386+
break;
387+
case FieldType.UInt:
388+
if ((uint)syncedFields[i].GetValue(this) != (uint)syncedFieldValues[i])
389+
dirtyFields[i] = true; //This fields value is out of sync!
390+
else
391+
dirtyFields[i] = false; //Up to date
392+
break;
393+
case FieldType.ULong:
394+
if ((ulong)syncedFields[i].GetValue(this) != (ulong)syncedFieldValues[i])
395+
dirtyFields[i] = true; //This fields value is out of sync!
396+
else
397+
dirtyFields[i] = false; //Up to date
398+
break;
399+
case FieldType.UShort:
400+
if ((ushort)syncedFields[i].GetValue(this) != (ushort)syncedFieldValues[i])
401+
dirtyFields[i] = true; //This fields value is out of sync!
402+
else
403+
dirtyFields[i] = false; //Up to date
404+
break;
405+
case FieldType.String:
406+
if ((string)syncedFields[i].GetValue(this) != (string)syncedFieldValues[i])
407+
dirtyFields[i] = true; //This fields value is out of sync!
408+
else
409+
dirtyFields[i] = false; //Up to date
410+
break;
411+
}
412+
}
413+
}
414+
#endregion
415+
120416
protected void SendToServer(string messageType, string channelName, byte[] data)
121417
{
122418
if(MessageManager.messageTypes[messageType] < 32)

0 commit comments

Comments
 (0)