SyncObjectProcessor.cs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. using System.Collections.Generic;
  2. using Mono.CecilX;
  3. namespace Mirror.Weaver
  4. {
  5. public static class SyncObjectProcessor
  6. {
  7. // ulong = 64 bytes
  8. const int SyncObjectsLimit = 64;
  9. // Finds SyncObjects fields in a type
  10. // Type should be a NetworkBehaviour
  11. public static List<FieldDefinition> FindSyncObjectsFields(Writers writers, Readers readers, Logger Log, TypeDefinition td, ref bool WeavingFailed)
  12. {
  13. List<FieldDefinition> syncObjects = new List<FieldDefinition>();
  14. foreach (FieldDefinition fd in td.Fields)
  15. {
  16. if (fd.FieldType.IsGenericParameter)
  17. {
  18. // can't call .Resolve on generic ones
  19. continue;
  20. }
  21. if (fd.FieldType.Resolve().IsDerivedFrom<SyncObject>())
  22. {
  23. if (fd.IsStatic)
  24. {
  25. Log.Error($"{fd.Name} cannot be static", fd);
  26. WeavingFailed = true;
  27. continue;
  28. }
  29. // SyncObjects always needs to be readonly to guarantee.
  30. // Weaver calls InitSyncObject on them for dirty bits etc.
  31. // Reassigning at runtime would cause undefined behaviour.
  32. // (C# 'readonly' is called 'initonly' in IL code.)
  33. //
  34. // NOTE: instead of forcing readonly, we could also scan all
  35. // instructions for SyncObject assignments. this would
  36. // make unit tests very difficult though.
  37. if (!fd.IsInitOnly)
  38. {
  39. // just a warning for now.
  40. // many people might still use non-readonly SyncObjects.
  41. Log.Warning($"{fd.Name} should have a 'readonly' keyword in front of the variable because {typeof(SyncObject)}s always need to be initialized by the Weaver.", fd);
  42. // only log, but keep weaving. no need to break projects.
  43. //WeavingFailed = true;
  44. }
  45. GenerateReadersAndWriters(writers, readers, fd.FieldType, ref WeavingFailed);
  46. syncObjects.Add(fd);
  47. }
  48. }
  49. // SyncObjects dirty mask is 64 bit. can't sync more than 64.
  50. if (syncObjects.Count > 64)
  51. {
  52. Log.Error($"{td.Name} has > {SyncObjectsLimit} SyncObjects (SyncLists etc). Consider refactoring your class into multiple components", td);
  53. WeavingFailed = true;
  54. }
  55. return syncObjects;
  56. }
  57. // Generates serialization methods for synclists
  58. static void GenerateReadersAndWriters(Writers writers, Readers readers, TypeReference tr, ref bool WeavingFailed)
  59. {
  60. if (tr is GenericInstanceType genericInstance)
  61. {
  62. foreach (TypeReference argument in genericInstance.GenericArguments)
  63. {
  64. if (!argument.IsGenericParameter)
  65. {
  66. readers.GetReadFunc(argument, ref WeavingFailed);
  67. writers.GetWriteFunc(argument, ref WeavingFailed);
  68. }
  69. }
  70. }
  71. if (tr != null)
  72. {
  73. GenerateReadersAndWriters(writers, readers, tr.Resolve().BaseType, ref WeavingFailed);
  74. }
  75. }
  76. }
  77. }