Beispiel #1
0
        public override void OnInspectorGUI()
        {
            Type scriptClass = _script.GetClass();
            RealtimeModelAttribute realtimeModelAttribute = GetRealtimeModelAttribute(scriptClass);

            if (realtimeModelAttribute != null)
            {
                // If this is a model file. Do some cool UI for it.
                if (GUILayout.Button("Compile Model"))
                {
                    try {
                        string modelCode = GenerateModel();
                        SaveGeneratedModel(modelCode);
                    } catch (Exception exception) {
                        Debug.LogError("Failed to compile model.");
                        Debug.LogException(exception);
                    }
                }
                if (GUILayout.Button("Clear Autogenerated Model"))
                {
                    try {
                        SaveGeneratedModel(null);
                    } catch (Exception exception) {
                        Debug.LogException(exception);
                        Debug.LogError("Failed to clear model.");
                    }
                }
            }
            else
            {
                DrawDefaultInspectorGUI();
            }
        }
Beispiel #2
0
        private string GenerateModel()
        {
            // Model
            Type   modelClass     = _script.GetClass();
            string classNamespace = modelClass.Namespace;
            string className      = modelClass.Name;
            //string baseClassName  = modelClass.BaseType.Name;
            bool hasMetaModel = false;

            RealtimeModelAttribute realtimeModelAttribute = GetRealtimeModelAttribute(modelClass);

            if (realtimeModelAttribute != null)
            {
                hasMetaModel = realtimeModelAttribute.createMetaModel;
            }

            // Properties
            Dictionary <uint, FieldInfo> allProperties;
            Dictionary <uint, FieldInfo> reliableProperties;
            Dictionary <uint, FieldInfo> unreliableProperties;
            Dictionary <uint, FieldInfo> modelProperties;
            Dictionary <uint, FieldInfo> eventProperties;

            GetRealtimePropertyAttributesForModel(modelClass, out allProperties, out reliableProperties, out unreliableProperties, out modelProperties, out eventProperties);

            // Model configuration
            bool hasNamespace   = classNamespace != null;
            bool hasChangeCache = reliableProperties.Count > 0;

            // Model code
            List <string> code  = new List <string>();
            int           level = 0;

            // Namespace
            if (hasNamespace)
            {
                code.Add(Indent(level++) + "namespace " + classNamespace + " {");
            }

            // Class
            code.Add(Indent(level++) + "public partial class " + className + " : IModel {");

            // Public properties
            if (allProperties.Count > 0)
            {
                code.Add(Indent(level) + "// Properties");
                foreach (KeyValuePair <uint, FieldInfo> property in allProperties)
                {
                    uint      propertyID = property.Key;
                    FieldInfo field      = property.Value;
                    if (reliableProperties.ContainsKey(propertyID))
                    {
                        // Reliable
                        code.Add(Indent(level++) + "public " + GetTypeAsString(field.FieldType) + " " + GetCleanPropertyName(field.Name, false) + " {");
                        code.Add(Indent(level) + "get { return _cache.LookForValueInCache(" + field.Name + ", entry => entry." + GetCleanPropertyName(field.Name, false) + "Set, entry => entry." + GetCleanPropertyName(field.Name, false) + "); }");
                        code.Add(Indent(level) + "set { if (value == " + GetCleanPropertyName(field.Name, false) + ") return; _cache.UpdateLocalCache(entry => { entry." + GetCleanPropertyName(field.Name, false) + "Set = true; entry." + GetCleanPropertyName(field.Name, false) + " = value; return entry; });" + (eventProperties.ContainsKey(propertyID) ? " Fire" + GetCleanPropertyName(field.Name, true) + "DidChange(value);" : "") + " }");
                        code.Add(Indent(--level) + "}");
                    }
                    else if (unreliableProperties.ContainsKey(propertyID))
                    {
                        // Unreliable
                        code.Add(Indent(level++) + "public " + GetTypeAsString(field.FieldType) + " " + GetCleanPropertyName(field.Name, false) + " {");
                        code.Add(Indent(level) + "get { return " + field.Name + "; }");
                        code.Add(Indent(level) + "set { if (value == " + field.Name + ") return; _" + GetCleanPropertyName(field.Name, false) + "ShouldWrite = true; " + field.Name + " = value;" + (eventProperties.ContainsKey(propertyID) ? " Fire" + GetCleanPropertyName(field.Name, true) + "DidChange(value);" : "") + " }");
                        code.Add(Indent(--level) + "}");
                    }
                    else if (modelProperties.ContainsKey(propertyID))
                    {
                        // Model
                        code.Add(Indent(level++) + "public " + GetTypeAsString(field.FieldType) + " " + GetCleanPropertyName(field.Name, false) + " {");
                        code.Add(Indent(level) + "get { return " + field.Name + "; }");
                        code.Add(Indent(--level) + "}");
                    }
                }
                code.Add(Indent(level));
            }

            // Events
            if (eventProperties.Count > 0)
            {
                code.Add(Indent(level) + "// Events");
                foreach (KeyValuePair <uint, FieldInfo> property in eventProperties)
                {
                    FieldInfo field = property.Value;
                    code.Add(Indent(level) + "public delegate void " + GetCleanPropertyName(field.Name, true) + "DidChange(" + _script.GetClass().Name + " model, " + GetTypeAsString(field.FieldType) + " value);");
                    code.Add(Indent(level) + "public event         " + GetCleanPropertyName(field.Name, true) + "DidChange " + GetCleanPropertyName(field.Name, false) + "DidChange;");
                }
                code.Add(Indent(level));
            }

            // Ownership
            if (hasMetaModel)
            {
                code.Add(Indent(level) + "// Ownership");
                code.Add(Indent(level) + "public int ownerID { get { return _metaModel.ownerID; } }");
                code.Add(Indent(level));
                code.Add(Indent(level++) + "public void RequestOwnership(int clientIndex) {");
                code.Add(Indent(level) + "_metaModel.ownerID = clientIndex;");
                code.Add(Indent(--level) + "}");
                code.Add(Indent(level));
                code.Add(Indent(level++) + "public void ClearOwnership() {");
                code.Add(Indent(level) + "_metaModel.ownerID = -1;");
                code.Add(Indent(--level) + "}");
                code.Add(Indent(level));
            }

            // Meta model
            if (hasMetaModel)
            {
                code.Add(Indent(level) + "// Meta model");
                code.Add(Indent(level) + "private MetaModel _metaModel;");
                code.Add(Indent(level));
            }

            // Change cache
            if (hasChangeCache)
            {
                code.Add(Indent(level) + "// Delta updates");
                code.Add(Indent(level++) + "private struct LocalCacheEntry {");
                int longestTypeLength = 4; // bool
                foreach (KeyValuePair <uint, FieldInfo> property in reliableProperties)
                {
                    FieldInfo field      = property.Value;
                    int       typeLength = GetTypeAsString(field.FieldType).Length;
                    if (typeLength > longestTypeLength)
                    {
                        longestTypeLength = typeLength;
                    }
                }
                foreach (KeyValuePair <uint, FieldInfo> property in reliableProperties)
                {
                    FieldInfo field    = property.Value;
                    string    typeName = GetTypeAsString(field.FieldType);
                    code.Add(Indent(level) + "public bool " + Space(longestTypeLength - 4) + GetCleanPropertyName(field.Name, false) + "Set;");
                    code.Add(Indent(level) + "public " + typeName + Space(longestTypeLength - typeName.Length) + " " + GetCleanPropertyName(field.Name, false) + ";");
                }
                code.Add(Indent(--level) + "}");
                code.Add(Indent(level));
                code.Add(Indent(level) + "private LocalChangeCache<LocalCacheEntry> _cache;");
                code.Add(Indent(level));
            }

            // Unreliable property should write bools
            if (unreliableProperties.Count > 0)
            {
                foreach (KeyValuePair <uint, FieldInfo> property in unreliableProperties)
                {
                    FieldInfo field = property.Value;
                    code.Add(Indent(level) + "private bool _" + GetCleanPropertyName(field.Name, false) + "ShouldWrite;");
                }
                code.Add(Indent(level));
            }

            // Constructor
            code.Add(Indent(level++) + "public " + className + "() {");
            if (hasMetaModel)
            {
                code.Add(Indent(level) + "_metaModel = new MetaModel();");
            }
            if (hasChangeCache)
            {
                code.Add(Indent(level) + "_cache = new LocalChangeCache<LocalCacheEntry>();");
            }

            if (hasChangeCache && modelProperties.Count > 0)
            {
                code.Add(Indent(level));
            }

            if (modelProperties.Count > 0)
            {
                foreach (KeyValuePair <uint, FieldInfo> property in modelProperties)
                {
                    FieldInfo field = property.Value;
                    code.Add(Indent(level) + field.Name + " = new " + GetTypeAsString(field.FieldType) + "();");
                }
            }
            code.Add(Indent(--level) + "}");
            code.Add(Indent(level));

            // Events
            if (eventProperties.Count > 0)
            {
                code.Add(Indent(level) + "// Events");
                foreach (KeyValuePair <uint, FieldInfo> property in eventProperties)
                {
                    FieldInfo field = property.Value;
                    code.Add(Indent(level++) + "public void Fire" + GetCleanPropertyName(field.Name, true) + "DidChange(" + GetTypeAsString(field.FieldType) + " value) {");
                    code.Add(Indent(level++) + "try {");
                    code.Add(Indent(level++) + "if (" + GetCleanPropertyName(field.Name, false) + "DidChange != null)");
                    code.Add(Indent(level--) + GetCleanPropertyName(field.Name, false) + "DidChange(this, value);");
                    code.Add(Indent(level - 1) + "} catch (System.Exception exception) {");
                    code.Add(Indent(level) + "Debug.LogException(exception);");
                    code.Add(Indent(--level) + "}");
                    code.Add(Indent(--level) + "}");
                }
                code.Add(Indent(level));
            }

            //// Serialization
            // PropertyID enum
            code.Add(Indent(level) + "// Serialization");
            code.Add(Indent(level++) + "enum PropertyID {");
            foreach (KeyValuePair <uint, FieldInfo> property in allProperties)
            {
                uint      propertyID = property.Key;
                FieldInfo field      = property.Value;
                code.Add(Indent(level) + GetCleanPropertyName(field.Name, true) + " = " + propertyID + ",");
            }
            code.Add(Indent(--level) + "}");
            code.Add(Indent(level));

            // Write Length
            code.Add(Indent(level++) + "public int WriteLength(StreamContext context) {");
            code.Add(Indent(level) + "int length = 0;");
            code.Add(Indent(level));
            code.Add(Indent(level++) + "if (context.fullModel) {");
            if (hasChangeCache)
            {
                code.Add(Indent(level) + "// Mark unreliable properties as clean and flatten the in-flight cache.");
                code.Add(Indent(level) + "// TODO: Move this out of WriteLength() once we have a prepareToWrite method.");
                foreach (KeyValuePair <uint, FieldInfo> property in unreliableProperties)
                {
                    FieldInfo field = property.Value;
                    code.Add(Indent(level) + "_" + GetCleanPropertyName(field.Name, false) + "ShouldWrite = false;");
                }
                foreach (KeyValuePair <uint, FieldInfo> property in reliableProperties)
                {
                    FieldInfo field = property.Value;
                    code.Add(Indent(level) + field.Name + " = " + GetCleanPropertyName(field.Name, false) + ";");
                }
                code.Add(Indent(level) + "_cache.Clear();");
                code.Add(Indent(level));
            }
            if (hasMetaModel)
            {
                code.Add(Indent(level) + "// Meta model");
                code.Add(Indent(level) + "length += WriteStream.WriteModelLength(0, _metaModel, context);");
                code.Add(Indent(level));
            }
            code.Add(Indent(level) + "// Write all properties");
            foreach (KeyValuePair <uint, FieldInfo> property in allProperties)
            {
                uint      propertyID = property.Key;
                FieldInfo field      = property.Value;
                code.Add(Indent(level) + GetWriteLengthLineForProperty(field));
            }
            code.Add(Indent(level - 1) + "} else {");
            // Meta model
            if (hasMetaModel)
            {
                code.Add(Indent(level) + "// Meta model");
                code.Add(Indent(level) + "length += WriteStream.WriteModelLength(0, _metaModel, context);");
            }

            if (hasMetaModel && unreliableProperties.Count > 0)
            {
                code.Add(Indent(level));
            }

            // Unreliable properties
            if (unreliableProperties.Count > 0)
            {
                code.Add(Indent(level) + "// Unreliable properties");
                code.Add(Indent(level++) + "if (context.unreliableChannel) {");
                foreach (KeyValuePair <uint, FieldInfo> property in unreliableProperties)
                {
                    FieldInfo field = property.Value;
                    code.Add(Indent(level++) + "if (_" + GetCleanPropertyName(field.Name, false) + "ShouldWrite) {");
                    code.Add(Indent(level) + GetWriteLengthLineForProperty(field));
                    code.Add(Indent(--level) + "}");
                }
                code.Add(Indent(--level) + "}");
            }

            if (unreliableProperties.Count > 0 && reliableProperties.Count > 0)
            {
                code.Add(Indent(level));
            }

            // Reliable properties
            if (reliableProperties.Count > 0)
            {
                code.Add(Indent(level) + "// Reliable properties");
                code.Add(Indent(level++) + "if (context.reliableChannel) {");
                if (hasChangeCache)
                {
                    code.Add(Indent(level) + "LocalCacheEntry entry = _cache.localCache;");
                }
                foreach (KeyValuePair <uint, FieldInfo> property in reliableProperties)
                {
                    FieldInfo field = property.Value;
                    code.Add(Indent(level++) + "if (entry." + GetCleanPropertyName(field.Name, false) + "Set)");
                    code.Add(Indent(level--) + GetWriteLengthLineForProperty(field, true));
                }
                code.Add(Indent(--level) + "}");
            }

            if (reliableProperties.Count > 0 && modelProperties.Count > 0)
            {
                code.Add(Indent(level));
            }

            // Models
            if (modelProperties.Count > 0)
            {
                code.Add(Indent(level) + "// Models");
                foreach (KeyValuePair <uint, FieldInfo> property in modelProperties)
                {
                    FieldInfo field = property.Value;
                    code.Add(Indent(level) + GetWriteLengthLineForProperty(field, false));
                }
            }

            code.Add(Indent(--level) + "}");
            code.Add(Indent(level));
            code.Add(Indent(level) + "return length;");
            code.Add(Indent(--level) + "}");
            code.Add(Indent(level));

            // Write
            code.Add(Indent(level++) + "public void Write(WriteStream stream, StreamContext context) {");
            code.Add(Indent(level++) + "if (context.fullModel) {");
            if (hasMetaModel)
            {
                code.Add(Indent(level) + "// Meta model");
                code.Add(Indent(level) + "stream.WriteModel(0, _metaModel, context);");
                code.Add(Indent(level));
            }
            code.Add(Indent(level) + "// Write all properties");
            foreach (KeyValuePair <uint, FieldInfo> property in allProperties)
            {
                uint      propertyID = property.Key;
                FieldInfo field      = property.Value;
                code.Add(Indent(level) + GetWriteLineForProperty(field));
            }
            code.Add(Indent(level - 1) + "} else {");
            // Meta model
            if (hasMetaModel)
            {
                code.Add(Indent(level) + "// Meta model");
                code.Add(Indent(level) + "stream.WriteModel(0, _metaModel, context);");
            }

            if (hasMetaModel && unreliableProperties.Count > 0)
            {
                code.Add(Indent(level));
            }

            // Unreliable properties
            if (unreliableProperties.Count > 0)
            {
                code.Add(Indent(level) + "// Unreliable properties");
                code.Add(Indent(level++) + "if (context.unreliableChannel) {");
                foreach (KeyValuePair <uint, FieldInfo> property in unreliableProperties)
                {
                    FieldInfo field = property.Value;
                    code.Add(Indent(level++) + "if (_" + GetCleanPropertyName(field.Name, false) + "ShouldWrite) {");
                    code.Add(Indent(level) + GetWriteLineForProperty(field));
                    code.Add(Indent(level) + "_" + GetCleanPropertyName(field.Name, false) + "ShouldWrite = false;");
                    code.Add(Indent(--level) + "}");
                }
                code.Add(Indent(--level) + "}");
            }

            if (unreliableProperties.Count > 0 && reliableProperties.Count > 0)
            {
                code.Add(Indent(level));
            }

            // Reliable properties
            if (reliableProperties.Count > 0)
            {
                code.Add(Indent(level) + "// Reliable properties");
                code.Add(Indent(level++) + "if (context.reliableChannel) {");
                if (hasChangeCache)
                {
                    code.Add(Indent(level) + "LocalCacheEntry entry = _cache.localCache;");
                    code.Add(Indent(level++) + GetIfPushToChangeCacheLineForProperties(reliableProperties.Values));
                    code.Add(Indent(level--) + "_cache.PushLocalCacheToInflight(context.updateID);");
                    code.Add(Indent(level));
                }
                foreach (KeyValuePair <uint, FieldInfo> property in reliableProperties)
                {
                    FieldInfo field = property.Value;
                    code.Add(Indent(level++) + "if (entry." + GetCleanPropertyName(field.Name, false) + "Set)");
                    code.Add(Indent(level--) + GetWriteLineForProperty(field, true));
                }
                code.Add(Indent(--level) + "}");
            }

            if (reliableProperties.Count > 0 && modelProperties.Count > 0)
            {
                code.Add(Indent(level));
            }

            // Models
            if (modelProperties.Count > 0)
            {
                code.Add(Indent(level) + "// Models");
                foreach (KeyValuePair <uint, FieldInfo> property in modelProperties)
                {
                    FieldInfo field = property.Value;
                    code.Add(Indent(level) + GetWriteLineForProperty(field, false));
                }
            }

            code.Add(Indent(--level) + "}");
            code.Add(Indent(--level) + "}");
            code.Add(Indent(level));

            // Read
            code.Add(Indent(level++) + "public void Read(ReadStream stream, StreamContext context) {");
            if (hasChangeCache)
            {
                IEnumerable <KeyValuePair <uint, FieldInfo> > reliableEventProperties = reliableProperties.Intersect(eventProperties);
                if (reliableEventProperties.Count() > 0)
                {
                    foreach (KeyValuePair <uint, FieldInfo> property in reliableEventProperties)
                    {
                        uint      propertyID = property.Key;
                        FieldInfo field      = property.Value;

                        code.Add(Indent(level) + "bool " + GetCleanPropertyName(field.Name, false) + "ExistsInChangeCache = _cache.ValueExistsInCache(entry => entry." + GetCleanPropertyName(field.Name, false) + "Set);");
                    }
                    code.Add(Indent(level));
                }

                code.Add(Indent(level) + "// Remove from in-flight");
                code.Add(Indent(level++) + "if (context.deltaUpdatesOnly && context.reliableChannel)");
                code.Add(Indent(level--) + "_cache.RemoveUpdateFromInflight(context.updateID);");
                code.Add(Indent(level));
            }
            code.Add(Indent(level) + "// Loop through each property and deserialize");
            code.Add(Indent(level) + "uint propertyID;");
            code.Add(Indent(level++) + "while (stream.ReadNextPropertyID(out propertyID)) {");
            code.Add(Indent(level++) + "switch (propertyID) {");
            if (hasMetaModel)
            {
                code.Add(Indent(level++) + "case 0:");
                code.Add(Indent(level) + "// Meta model");
                code.Add(Indent(level) + "stream.ReadModel(_metaModel, context);");
                code.Add(Indent(level--) + "break;");
            }
            foreach (KeyValuePair <uint, FieldInfo> property in allProperties)
            {
                uint      propertyID = property.Key;
                FieldInfo field      = property.Value;

                bool reliableProperty   = reliableProperties.ContainsKey(propertyID);
                bool unreliableProperty = unreliableProperties.ContainsKey(propertyID);
                bool eventProperty      = eventProperties.ContainsKey(propertyID);

                code.Add(Indent(level++) + "case (uint)PropertyID." + GetCleanPropertyName(field.Name, true) + ": {");
                if (eventProperty)
                {
                    code.Add(Indent(level) + GetTypeAsString(field.FieldType) + " previousValue = " + field.Name + ";");
                    code.Add(Indent(level));
                }
                code.Add(Indent(level) + GetReadLineForProperty(field));
                if (unreliableProperty)
                {
                    code.Add(Indent(level) + "_" + GetCleanPropertyName(field.Name, false) + "ShouldWrite = false;");
                }
                if (eventProperty)
                {
                    code.Add(Indent(level));
                    code.Add(Indent(level++) + "if (" + (reliableProperty ? "!" + GetCleanPropertyName(field.Name, false) + "ExistsInChangeCache && " : "") + field.Name + " != previousValue)");
                    code.Add(Indent(level--) + "Fire" + GetCleanPropertyName(field.Name, true) + "DidChange(" + field.Name + ");");
                }
                code.Add(Indent(level) + "break;");
                code.Add(Indent(--level) + "}");
            }
            code.Add(Indent(level++) + "default:");
            code.Add(Indent(level) + "stream.SkipProperty();");
            code.Add(Indent(level--) + "break;");
            code.Add(Indent(--level) + "}");
            code.Add(Indent(--level) + "}");
            code.Add(Indent(--level) + "}");

            // Class
            code.Add(Indent(--level) + "}");

            // Namespace
            if (hasNamespace)
            {
                code.Add(Indent(--level) + "}");
            }

            // Convert to string and return
            string codeString = "";

            foreach (string line in code)
            {
                codeString += line + Environment.NewLine;
            }
            return(codeString);
        }