void GenerateSpawnSystem(GhostAuthoringComponent ghostInfo) { string ghostInterpolateComponents = ""; string ghostPredictComponents = ""; for (int comp = 0; comp < ghostInfo.Components.Length; ++comp) { if (ghostInfo.Components[comp].interpolatedClient) { ghostInterpolateComponents += k_GhostSpawnComponentTemplate.Replace("$(GHOSTCOMPONENTTYPE)", ghostInfo.Components[comp].name); } if (ghostInfo.Components[comp].predictedClient) { ghostPredictComponents += k_GhostSpawnComponentTemplate.Replace("$(GHOSTCOMPONENTTYPE)", ghostInfo.Components[comp].name); } } var spawnData = k_GhostSpawnSystemTemplate .Replace("$(GHOSTNAME)", target.name) .Replace("$(GHOSTINTERPOLATEDCOMPONENTS)", ghostInterpolateComponents) .Replace("$(GHOSTPREDICTEDCOMPONENTS)", ghostPredictComponents); File.WriteAllText(Path.Combine(assetPath, ghostInfo.SpawnSystemPath), spawnData); }
void GenerateGhost(GhostAuthoringComponent ghostInfo) { List <Type> allTypes = new List <Type>(); foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { IEnumerable <Type> asmTypes = null; try { asmTypes = assembly.GetTypes(); } catch (ReflectionTypeLoadException e) { asmTypes = e.Types.Where(t => t != null); Debug.LogWarning( $"GhostImporter failed loading assembly: {(assembly.IsDynamic ? assembly.ToString() : assembly.Location)}"); } allTypes.AddRange(asmTypes.Where(t => typeof(IComponentData).IsAssignableFrom(t) || typeof(IBufferElementData).IsAssignableFrom(t) || typeof(ISharedComponentData).IsAssignableFrom(t))); } // Update type of all fields for (int comp = 0; comp < ghostInfo.Components.Length; ++comp) { var componentTypes = allTypes.Where(t => t.Name == ghostInfo.Components[comp].name); if (componentTypes.Count() != 1) { Debug.LogError("Could not find the type or found several candidates: " + ghostInfo.Components[comp].name); return; } Type componentType = componentTypes.First(); for (int field = 0; field < ghostInfo.Components[comp].fields.Length; ++field) { var fieldInfo = componentType.GetField(ghostInfo.Components[comp].fields[field].name); if (fieldInfo == null) { Debug.LogError("Could not find field: " + ghostInfo.Components[comp].fields[field].name + " in componentType: " + ghostInfo.Components[comp].name); return; } ghostInfo.Components[comp].fields[field].DataType = fieldInfo.FieldType; } } assetPath = AssetDatabase.GetAssetPath(target); if (assetPath == "") { assetPath = "Assets"; } else { assetPath = Path.GetDirectoryName(assetPath); } GenerateSnapshotData(ghostInfo); GenerateSerializer(ghostInfo); GenerateSpawnSystem(ghostInfo); GenerateUpdateSystem(ghostInfo); }
void GenerateSerializer(GhostAuthoringComponent ghostInfo) { string ghostComponentType = ""; string assignGhostComponentType = ""; string ghostComponentTypeCheck = ""; string ghostApply = ""; int serverComponentCount = 0; for (int comp = 0; comp < ghostInfo.Components.Length; ++comp) { if (!ghostInfo.Components[comp].server) { continue; } ++serverComponentCount; ghostComponentTypeCheck += k_SerializerComponentTypeCheckTemplate.Replace("$(GHOSTCOMPONENTTYPE)", ghostInfo.Components[comp].name); ghostComponentType += k_SerializerComponentTypeTemplate.Replace("$(GHOSTCOMPONENTTYPE)", ghostInfo.Components[comp].name); assignGhostComponentType += k_SerializerAssignComponentTypeTemplate.Replace("$(GHOSTCOMPONENTTYPE)", ghostInfo.Components[comp].name); if (ghostInfo.Components[comp].fields.Length > 0) { ghostComponentType += k_SerializerComponentTypeDataTemplate.Replace("$(GHOSTCOMPONENTTYPE)", ghostInfo.Components[comp].name); assignGhostComponentType += k_SerializerAssignComponentTypeDataTemplate.Replace("$(GHOSTCOMPONENTTYPE)", ghostInfo.Components[comp].name); ghostApply += k_SerializerAssignChunkArrayTemplate.Replace("$(GHOSTCOMPONENTTYPE)", ghostInfo.Components[comp].name); for (int field = 0; field < ghostInfo.Components[comp].fields.Length; ++field) { ghostApply += k_SerializerAssignSnapshotTemplate.Replace("$(GHOSTCOMPONENTTYPE)", ghostInfo.Components[comp].name) .Replace("$(GHOSTFIELDNAME)", ghostInfo.Components[comp].fields[field].name); } } } var serializerData = k_SerializerTemplate .Replace("$(GHOSTNAME)", target.name) .Replace("$(GHOSTIMPORTANCE)", ghostInfo.Importance) .Replace("$(GHOSTCOMPONENTCHECK)", ghostComponentTypeCheck) .Replace("$(GHOSTAPPLY)", ghostApply) .Replace("$(GHOSTCOMPONENTCOUNT)", serverComponentCount.ToString()) .Replace("$(ASSIGNGHOSTCOMPONENTTYPES)", assignGhostComponentType) .Replace("$(GHOSTCOMPONENTTYPES)", ghostComponentType); File.WriteAllText(Path.Combine(assetPath, ghostInfo.SerializerPath), serializerData); }
public override void OnInspectorGUI() { base.OnInspectorGUI(); if (GUILayout.Button("Add Ghost Authoring Component")) { GameObject[] gameObjects = Selection.gameObjects; foreach (GameObject g in gameObjects) { GhostAuthoringComponent gac = g.AddComponent(typeof(GhostAuthoringComponent)) as GhostAuthoringComponent; } } }
public void OnEnable() { ghost = (GhostAuthoringComponent)this.target; defaultClientInstantiationType = serializedObject.FindProperty(nameof(GhostAuthoringComponent.Type)); ghostComponents = serializedObject.FindProperty(nameof(GhostAuthoringComponent.Components)); // _serializers = ghost.Components.Where(f => f.isSerializer).ToList(); // // _ghostList = new ReorderableList(_serializers, // typeof(ComponentType), // false, true, false, false); // _ghostList.drawHeaderCallback += DrawHeaderCallback; // _ghostList.drawElementCallback += DrawElementCallback; }
public static void SyncComponentList(GhostAuthoringComponent self) { using (var tempWorld = new World("TempGhostConversion")) using (var blobAssetStore = new BlobAssetStore()) { self.doNotStrip = true; var convertedEntity = GameObjectConversionUtility.ConvertGameObjectHierarchy(self.gameObject, GameObjectConversionSettings.FromWorld(tempWorld, blobAssetStore)); self.doNotStrip = false; // Build list of existing components var toDelete = new Dictionary <string, int>(); for (int i = 0; i < self.Components.Length; ++i) { try { toDelete.Add(self.Components[i].entityIndex + self.Components[i].name, i); } catch (Exception e) { Debug.LogAssertion(e); } } var newComponents = new List <GhostAuthoringComponent.GhostComponent>(); AddToComponentList(self, newComponents, toDelete, tempWorld, convertedEntity, 0); if (tempWorld.EntityManager.HasComponent <LinkedEntityGroup>(convertedEntity)) { var linkedEntityGroup = tempWorld.EntityManager.GetBuffer <LinkedEntityGroup>(convertedEntity); for (int i = 1; i < linkedEntityGroup.Length; ++i) { AddToComponentList(self, newComponents, toDelete, tempWorld, linkedEntityGroup[i].Value, i); } } self.Components = newComponents.ToArray(); } EditorUtility.SetDirty(self); }
static void GenerateUpdateSystem(GhostAuthoringComponent ghostInfo, string ownerField, string assetPath, GhostCodeGen.Batch batch) { var codeGen = new GhostCodeGen("Packages/com.unity.netcode/Editor/CodeGenTemplates/GhostUpdateSystem.cs"); var replacements = new Dictionary <string, string>(); var ghostInterpolatedComponentFromEntitySet = new HashSet <string>(); var ghostPredictedComponentFromEntitySet = new HashSet <string>(); bool interpolatedNeedsLinkedEntityGroup = false; bool predictedNeedsLinkedEntityGroup = false; HashSet <string> imports = new HashSet <string>(); imports.Add("Unity.Entities"); HashSet <string> interpolatedEntityGroupTypes = new HashSet <string>(); HashSet <string> predictedEntityGroupTypes = new HashSet <string>(); for (int comp = 0; comp < ghostInfo.Components.Length; ++comp) { if (ghostInfo.Components[comp].interpolatedClient && ghostInfo.Components[comp].entityIndex > 0 && ghostInfo.Components[comp].fields.Length > 0) { interpolatedEntityGroupTypes.Add(ghostInfo.Components[comp].name); } if (ghostInfo.Components[comp].predictedClient && ghostInfo.Components[comp].entityIndex > 0 && ghostInfo.Components[comp].fields.Length > 0) { predictedEntityGroupTypes.Add(ghostInfo.Components[comp].name); } } for (int comp = 0; comp < ghostInfo.Components.Length; ++comp) { if (!ghostInfo.Components[comp].server) { continue; } if (ghostInfo.Components[comp].fields.Length == 0) { continue; } var componentTypeName = GetShortName(ghostInfo.Components[comp]); var type = ghostInfo.Components[comp].ShortName.Replace("+", "."); var fromEntityName = type.Replace(".", ""); replacements.Clear(); replacements.Add("GHOST_COMPONENT_TYPE", type); replacements.Add("GHOST_COMPONENT_TYPE_NAME", componentTypeName); replacements.Add("GHOST_COMPONENT_FROM_ENTITY_NAME", fromEntityName); replacements.Add("GHOST_ENTITY_INDEX", ghostInfo.Components[comp].entityIndex.ToString()); if (ghostInfo.Components[comp].interpolatedClient && (ghostInfo.DefaultClientInstantiationType != GhostAuthoringComponent.ClientInstantionType.OwnerPredicted || ghostInfo.Components[comp].sendDataTo != GhostAuthoringComponent.ClientSendType.Predicted)) { imports.Add(ghostInfo.Components[comp].NamespaceName); // When there are nested entities (or linked group entities) all of the components of the type // we need from the group of entities (parent+children) should be accessed the same way, or we'll get native array aliasing if (ghostInfo.Components[comp].entityIndex == 0 && !interpolatedEntityGroupTypes.Contains(ghostInfo.Components[comp].name)) { codeGen.GenerateFragment("GHOST_INTERPOLATED_COMPONENT_TYPE", replacements); codeGen.GenerateFragment("GHOST_INTERPOLATED_COMPONENT_REF", replacements); codeGen.GenerateFragment("GHOST_INTERPOLATED_ASSIGN_COMPONENT_REF", replacements); codeGen.GenerateFragment("GHOST_INTERPOLATED_COMPONENT_ARRAY", replacements); codeGen.GenerateFragment("GHOST_INTERPOLATED_BEGIN_ASSIGN", replacements); codeGen.GenerateFragment("GHOST_INTERPOLATED_END_ASSIGN", replacements); } else { interpolatedNeedsLinkedEntityGroup = true; if (!ghostInterpolatedComponentFromEntitySet.Contains(type)) { codeGen.GenerateFragment("GHOST_INTERPOLATED_COMPONENT_CHILD_REF", replacements); codeGen.GenerateFragment("GHOST_INTERPOLATED_ASSIGN_COMPONENT_CHILD_REF", replacements); ghostInterpolatedComponentFromEntitySet.Add(type); } codeGen.GenerateFragment("GHOST_INTERPOLATED_BEGIN_ASSIGN_CHILD", replacements); codeGen.GenerateFragment("GHOST_INTERPOLATED_END_ASSIGN_CHILD", replacements); } for (int field = 0; field < ghostInfo.Components[comp].fields.Length; ++field) { replacements["GHOST_FIELD_NAME"] = ghostInfo.Components[comp].fields[field].name; codeGen.GenerateFragment("GHOST_INTERPOLATED_ASSIGN", replacements); } } if (ghostInfo.Components[comp].predictedClient && (ghostInfo.DefaultClientInstantiationType != GhostAuthoringComponent.ClientInstantionType.OwnerPredicted || ghostInfo.Components[comp].sendDataTo != GhostAuthoringComponent.ClientSendType.Interpolated)) { imports.Add(ghostInfo.Components[comp].NamespaceName); // When there are nested entities (or linked group entities) all of the components of the type // we need from the group of entities (parent+children) should be accessed the same way, or we'll get native array aliasing if (ghostInfo.Components[comp].entityIndex == 0 && !predictedEntityGroupTypes.Contains(ghostInfo.Components[comp].name)) { codeGen.GenerateFragment("GHOST_PREDICTED_COMPONENT_TYPE", replacements); codeGen.GenerateFragment("GHOST_PREDICTED_COMPONENT_REF", replacements); codeGen.GenerateFragment("GHOST_PREDICTED_ASSIGN_COMPONENT_REF", replacements); codeGen.GenerateFragment("GHOST_PREDICTED_COMPONENT_ARRAY", replacements); codeGen.GenerateFragment("GHOST_PREDICTED_BEGIN_ASSIGN", replacements); codeGen.GenerateFragment("GHOST_PREDICTED_END_ASSIGN", replacements); } else { predictedNeedsLinkedEntityGroup = true; if (!ghostPredictedComponentFromEntitySet.Contains(type)) { codeGen.GenerateFragment("GHOST_PREDICTED_COMPONENT_CHILD_REF", replacements); codeGen.GenerateFragment("GHOST_PREDICTED_ASSIGN_COMPONENT_CHILD_REF", replacements); ghostPredictedComponentFromEntitySet.Add(type); } codeGen.GenerateFragment("GHOST_PREDICTED_BEGIN_ASSIGN_CHILD", replacements); codeGen.GenerateFragment("GHOST_PREDICTED_END_ASSIGN_CHILD", replacements); } for (int field = 0; field < ghostInfo.Components[comp].fields.Length; ++field) { replacements["GHOST_FIELD_NAME"] = ghostInfo.Components[comp].fields[field].name; codeGen.GenerateFragment("GHOST_PREDICTED_ASSIGN", replacements); } } } if (interpolatedNeedsLinkedEntityGroup) { replacements.Clear(); replacements.Add("GHOST_COMPONENT_TYPE", "LinkedEntityGroup"); replacements.Add("GHOST_COMPONENT_TYPE_NAME", "LinkedEntityGroup"); codeGen.GenerateFragment("GHOST_INTERPOLATED_READ_ONLY_COMPONENT_TYPE", replacements); codeGen.GenerateFragment("GHOST_INTERPOLATED_BUFFER_REF", replacements); codeGen.GenerateFragment("GHOST_INTERPOLATED_ASSIGN_BUFFER_REF", replacements); codeGen.GenerateFragment("GHOST_INTERPOLATED_BUFFER_ARRAY", replacements); } if (predictedNeedsLinkedEntityGroup) { replacements.Clear(); replacements.Add("GHOST_COMPONENT_TYPE", "LinkedEntityGroup"); replacements.Add("GHOST_COMPONENT_TYPE_NAME", "LinkedEntityGroup"); codeGen.GenerateFragment("GHOST_PREDICTED_READ_ONLY_COMPONENT_TYPE", replacements); codeGen.GenerateFragment("GHOST_PREDICTED_BUFFER_REF", replacements); codeGen.GenerateFragment("GHOST_PREDICTED_ASSIGN_BUFFER_REF", replacements); codeGen.GenerateFragment("GHOST_PREDICTED_BUFFER_ARRAY", replacements); } replacements.Clear(); replacements.Add("GHOST_NAME", ghostInfo.name); replacements.Add("GHOST_OWNER_FIELD", ownerField); if (ghostInfo.DefaultClientInstantiationType == GhostAuthoringComponent.ClientInstantionType.OwnerPredicted) { codeGen.GenerateFragment("GHOST_OWNER_PREDICTED_DEFAULT", replacements); } else if (ghostInfo.DefaultClientInstantiationType == GhostAuthoringComponent.ClientInstantionType.Predicted) { codeGen.GenerateFragment("GHOST_PREDICTED_DEFAULT", replacements); } replacements.Clear(); foreach (var ns in imports) { if (ns != null && ns != "") { replacements["GHOST_USING"] = ns; codeGen.GenerateFragment("GHOST_USING_STATEMENT", replacements); } } replacements.Clear(); replacements.Add("GHOST_NAME", ghostInfo.name); codeGen.GenerateFile(assetPath, ghostInfo.RootPath, ghostInfo.UpdateSystemPath, replacements, batch); }
static void GenerateSerializer(GhostAuthoringComponent ghostInfo, string assetPath, GhostCodeGen.Batch batch) { var codeGen = new GhostCodeGen("Packages/com.unity.netcode/Editor/CodeGenTemplates/GhostSerializer.cs"); var replacements = new Dictionary <string, string>(); int serverComponentCount = 0; bool needsLinkedEntityGroup = false; HashSet <string> imports = new HashSet <string>(); imports.Add("Unity.Entities"); imports.Add("Unity.Collections"); imports.Add("Unity.NetCode"); for (int comp = 0; comp < ghostInfo.Components.Length; ++comp) { var entityIndex = ghostInfo.Components[comp].entityIndex; if (!ghostInfo.Components[comp].server) { continue; } imports.Add(ghostInfo.Components[comp].NamespaceName); var componentTypeName = GetShortName(ghostInfo.Components[comp]); replacements.Clear(); replacements.Add("GHOST_COMPONENT_TYPE_NAME", componentTypeName); replacements.Add("GHOST_COMPONENT_TYPE", ghostInfo.Components[comp].ShortName.Replace("+", ".")); replacements.Add("GHOST_ENTITY_INDEX", entityIndex.ToString()); if (entityIndex == 0) { ++serverComponentCount; codeGen.GenerateFragment("GHOST_COMPONENT_TYPE", replacements); codeGen.GenerateFragment("GHOST_ASSIGN_COMPONENT_TYPE", replacements); } if (ghostInfo.Components[comp].fields.Length > 0) { if (entityIndex == 0) { codeGen.GenerateFragment("GHOST_COMPONENT_TYPE_DATA", replacements); codeGen.GenerateFragment("GHOST_ASSIGN_COMPONENT_TYPE_DATA", replacements); codeGen.GenerateFragment("GHOST_ASSIGN_CHUNK_ARRAY", replacements); } else { needsLinkedEntityGroup = true; codeGen.GenerateFragment("GHOST_COMPONENT_TYPE_CHILD_DATA", replacements); codeGen.GenerateFragment("GHOST_ASSIGN_COMPONENT_TYPE_CHILD_DATA", replacements); } for (int field = 0; field < ghostInfo.Components[comp].fields.Length; ++field) { replacements["GHOST_FIELD_NAME"] = ghostInfo.Components[comp].fields[field].name; if (entityIndex == 0) { codeGen.GenerateFragment("GHOST_ASSIGN_SNAPSHOT", replacements); } else { codeGen.GenerateFragment("GHOST_ASSIGN_CHILD_SNAPSHOT", replacements); } } } } if (needsLinkedEntityGroup) { ++serverComponentCount; replacements.Clear(); replacements.Add("GHOST_COMPONENT_TYPE_NAME", "LinkedEntityGroup"); replacements.Add("GHOST_COMPONENT_TYPE", "LinkedEntityGroup"); codeGen.GenerateFragment("GHOST_COMPONENT_TYPE", replacements); codeGen.GenerateFragment("GHOST_ASSIGN_COMPONENT_TYPE", replacements); codeGen.GenerateFragment("GHOST_BUFFER_COMPONENT_TYPE_DATA", replacements); codeGen.GenerateFragment("GHOST_ASSIGN_BUFFER_COMPONENT_TYPE_DATA", replacements); codeGen.GenerateFragment("GHOST_ASSIGN_CHUNK_BUFFER_ARRAY", replacements); } replacements.Clear(); foreach (var ns in imports) { if (ns != null && ns != "") { replacements["GHOST_USING"] = ns; codeGen.GenerateFragment("GHOST_USING_STATEMENT", replacements); } } replacements.Clear(); replacements.Add("GHOST_NAME", ghostInfo.name); replacements.Add("GHOST_IMPORTANCE", ghostInfo.Importance); replacements.Add("GHOST_COMPONENT_COUNT", serverComponentCount.ToString()); codeGen.GenerateFile(assetPath, ghostInfo.RootPath, ghostInfo.SerializerPath, replacements, batch); }
static void GenerateSnapshotData(GhostAuthoringComponent ghostInfo, string ownerField, string assetPath, GhostCodeGen.Batch batch) { var codeGen = new GhostCodeGen("Packages/com.unity.netcode/Editor/CodeGenTemplates/GhostSnapshotData.cs"); var replacements = new Dictionary <string, string>(); var typeProviders = new List <GhostSnapshotValue>(); typeProviders.AddRange(GhostSnapshotValue.GameSpecificTypes); typeProviders.AddRange(GhostSnapshotValue.DefaultTypes); var typeCodeGenCache = new Dictionary <string, GhostCodeGen>(); HashSet <string> imports = new HashSet <string>(); imports.Add("Unity.Mathematics"); bool hasTypeSpecificFields = false; bool hasReadWritePredicted = false; bool hasReadWriteInterpolated = false; if (ghostInfo.DefaultClientInstantiationType == GhostAuthoringComponent.ClientInstantionType.OwnerPredicted) { for (int comp = 0; comp < ghostInfo.Components.Length; ++comp) { if (ghostInfo.Components[comp].server && ghostInfo.Components[comp].fields.Length > 0 && ghostInfo.Components[comp].sendDataTo != GhostAuthoringComponent.ClientSendType.All) { hasTypeSpecificFields = true; } } } replacements.Add("GHOST_OWNER_FIELD", ownerField); if (hasTypeSpecificFields) { codeGen.GenerateFragment("GHOST_READ_IS_PREDICTED", replacements); codeGen.GenerateFragment("GHOST_WRITE_IS_PREDICTED", replacements); } int changeMaskIndex = 0; for (int comp = 0; comp < ghostInfo.Components.Length; ++comp) { if (!ghostInfo.Components[comp].server) { continue; } for (int field = 0; field < ghostInfo.Components[comp].fields.Length; ++field) { bool processed = false; foreach (var value in typeProviders) { if (value.CanProcess(ghostInfo.Components[comp].fields[field].Field, ghostInfo.Components[comp].name, ghostInfo.Components[comp].fields[field].name)) { var currentChangeMask = changeMaskIndex; value.AddImports(imports); var quantization = ghostInfo.Components[comp].fields[field].quantization; var shortName = GetShortName(ghostInfo.Components[comp]); var fullFieldName = shortName + ghostInfo.Components[comp].fields[field].name; var typeCodeGenPath = value.GetTemplatePath(quantization); if (!typeCodeGenCache.TryGetValue(typeCodeGenPath, out var typeCodeGen)) { typeCodeGen = new GhostCodeGen(typeCodeGenPath); typeCodeGenCache.Add(typeCodeGenPath, typeCodeGen); } replacements.Clear(); replacements.Add("GHOST_FIELD_NAME", fullFieldName); replacements.Add("GHOST_FIELD_TYPE_NAME", GetFieldTypeName(ghostInfo.Components[comp].fields[field].Field.FieldType)); if (quantization > 0) { replacements.Add("GHOST_QUANTIZE_SCALE", quantization.ToString()); replacements.Add("GHOST_DEQUANTIZE_SCALE", $"{(1.0f / quantization).ToString(CultureInfo.InvariantCulture)}f"); } replacements.Add("GHOST_MASK_BATCH", (currentChangeMask / 32).ToString()); replacements.Add("GHOST_MASK_INDEX", (currentChangeMask % 32).ToString()); typeCodeGen.GenerateFragment("GHOST_FIELD", replacements, codeGen); typeCodeGen.GenerateFragment("GHOST_FIELD_GET_SET", replacements, codeGen); typeCodeGen.GenerateFragment("GHOST_PREDICT", replacements, codeGen); if (currentChangeMask % 32 == 0) { typeCodeGen.GenerateFragment("GHOST_CALCULATE_CHANGE_MASK_ZERO", replacements, codeGen, "GHOST_CALCULATE_CHANGE_MASK"); } else { typeCodeGen.GenerateFragment("GHOST_CALCULATE_CHANGE_MASK", replacements, codeGen); } if (ghostInfo.Components[comp].fields[field].interpolate) { typeCodeGen.GenerateFragment("GHOST_INTERPOLATE", replacements, codeGen); } if (ghostInfo.Components[comp].server && ghostInfo.Components[comp].fields.Length > 0 && ghostInfo.Components[comp].sendDataTo == GhostAuthoringComponent.ClientSendType.Predicted) { if (!hasReadWritePredicted) { codeGen.GenerateFragment("GHOST_BEGIN_READ_PREDICTED", replacements); codeGen.GenerateFragment("GHOST_END_READ_PREDICTED", replacements); codeGen.GenerateFragment("GHOST_BEGIN_WRITE_PREDICTED", replacements); codeGen.GenerateFragment("GHOST_END_WRITE_PREDICTED", replacements); hasReadWritePredicted = true; } typeCodeGen.GenerateFragment("GHOST_READ", replacements, codeGen, "GHOST_READ_PREDICTED", " "); typeCodeGen.GenerateFragment("GHOST_WRITE", replacements, codeGen, "GHOST_WRITE_PREDICTED", " "); } else if (ghostInfo.Components[comp].server && ghostInfo.Components[comp].fields.Length > 0 && ghostInfo.Components[comp].sendDataTo == GhostAuthoringComponent.ClientSendType.Interpolated) { if (!hasReadWriteInterpolated) { codeGen.GenerateFragment("GHOST_BEGIN_READ_INTERPOLATED", replacements); codeGen.GenerateFragment("GHOST_END_READ_INTERPOLATED", replacements); codeGen.GenerateFragment("GHOST_BEGIN_WRITE_INTERPOLATED", replacements); codeGen.GenerateFragment("GHOST_END_WRITE_INTERPOLATED", replacements); hasReadWriteInterpolated = true; } typeCodeGen.GenerateFragment("GHOST_READ", replacements, codeGen, "GHOST_READ_INTERPOLATED", " "); typeCodeGen.GenerateFragment("GHOST_WRITE", replacements, codeGen, "GHOST_WRITE_INTERPOLATED", " "); } else { typeCodeGen.GenerateFragment("GHOST_READ", replacements, codeGen); typeCodeGen.GenerateFragment("GHOST_WRITE", replacements, codeGen); } ++changeMaskIndex; processed = true; break; } } if (!processed) { Debug.LogError("Unhandled type " + ghostInfo.Components[comp].fields[field].Field.FieldType); } } } var numMasks = (changeMaskIndex + 31) / 32; for (int i = 0; i < numMasks; ++i) { replacements["GHOST_MASK_BATCH"] = i.ToString(); codeGen.GenerateFragment("GHOST_CHANGE_MASK", replacements); codeGen.GenerateFragment("GHOST_WRITE_CHANGE_MASK", replacements); codeGen.GenerateFragment("GHOST_READ_CHANGE_MASK", replacements); } replacements.Clear(); foreach (var ns in imports) { if (ns != null && ns != "") { replacements["GHOST_USING"] = ns; codeGen.GenerateFragment("GHOST_USING_STATEMENT", replacements); } } replacements.Clear(); replacements.Add("GHOST_NAME", ghostInfo.name); codeGen.GenerateFile(assetPath, ghostInfo.RootPath, ghostInfo.SnapshotDataPath, replacements, batch); }
public static GhostCodeGen.Status GenerateGhost(GhostAuthoringComponent ghostInfo, bool testOnly = false) { var tempWorld = new World("GhostEnsureECSLoaded"); tempWorld.Dispose(); var allTypes = TypeManager.GetAllTypes(); var typeLookup = new Dictionary <string, Type>(); foreach (var compType in allTypes) { if (compType.Type != null) { typeLookup.Add(compType.Type.FullName, compType.Type); } } string ownerField = ""; // Update type of all fields for (int comp = 0; comp < ghostInfo.Components.Length; ++comp) { if (!typeLookup.TryGetValue(ghostInfo.Components[comp].name, out var componentType)) { Debug.LogError($"Could not find the type {ghostInfo.Components[comp].name}"); return(GhostCodeGen.Status.Failed); } ghostInfo.Components[comp].NamespaceName = componentType.Namespace; ghostInfo.Components[comp].ShortName = (String.IsNullOrEmpty(componentType.Namespace)) ? componentType.FullName : componentType.FullName.Substring(componentType.Namespace.Length + 1); for (int field = 0; field < ghostInfo.Components[comp].fields.Length; ++field) { var fieldInfo = componentType.GetField(ghostInfo.Components[comp].fields[field].name); if (fieldInfo == null) { Debug.LogError("Could not find field: " + ghostInfo.Components[comp].fields[field].name + " in componentType: " + ghostInfo.Components[comp].name); return(GhostCodeGen.Status.Failed); } ghostInfo.Components[comp].fields[field].Field = fieldInfo; if (ghostInfo.DefaultClientInstantiationType == GhostAuthoringComponent.ClientInstantionType.OwnerPredicted && ghostInfo.PredictingPlayerNetworkId == ghostInfo.Components[comp].name + "." + ghostInfo.Components[comp].fields[field].name) { ownerField = GetShortName(ghostInfo.Components[comp]) + ghostInfo.Components[comp].fields[field].name; } } } var assetPath = GhostCodeGen.GetPrefabAssetPath(ghostInfo.gameObject); var batch = new GhostCodeGen.Batch(); GenerateSnapshotData(ghostInfo, ownerField, assetPath, batch); GenerateSerializer(ghostInfo, assetPath, batch); GenerateUpdateSystem(ghostInfo, ownerField, assetPath, batch); var didWrite = batch.Flush(testOnly); AssetDatabase.Refresh(); return(didWrite ? GhostCodeGen.Status.Ok : GhostCodeGen.Status.NotModified); }
static void AddToComponentList(GhostAuthoringComponent self, List <GhostAuthoringComponent.GhostComponent> newComponents, Dictionary <string, int> toDelete, World tempWorld, Entity convertedEntity, int entityIndex) { var typeProviders = new List <GhostSnapshotValue>(); typeProviders.AddRange(GhostSnapshotValue.GameSpecificTypes); typeProviders.AddRange(GhostSnapshotValue.DefaultTypes); var compTypes = tempWorld.EntityManager.GetComponentTypes(convertedEntity); compTypes.Sort(default(ComponentNameComparer)); for (int i = 0; i < compTypes.Length; ++i) { var managedType = compTypes[i].GetManagedType(); if (managedType == typeof(Prefab) || managedType == typeof(LinkedEntityGroup)) { continue; } GhostAuthoringComponent.GhostComponent newComponent; if (GhostDefaultOverrides.TryGetValue(managedType.FullName, out newComponent)) { newComponent.fields = (GhostAuthoringComponent.GhostComponentField[])newComponent.fields.Clone(); } else { var fields = new List <GhostAuthoringComponent.GhostComponentField>(); foreach (var componentField in managedType.GetFields()) { var attr = componentField.GetCustomAttributes <GhostDefaultFieldAttribute>().ToArray(); if (attr.Length > 0) { bool valid = true; foreach (var valueType in typeProviders) { if (valueType.CanProcess(componentField, managedType.FullName, componentField.Name)) { if (attr[0].Quantization < 0 && valueType.SupportsQuantization) { Debug.LogError(String.Format( "{0}.{1} is of type {2} which requires quantization factor to be specified - ignoring field", managedType.FullName, componentField.Name, componentField.FieldType)); valid = false; } if (attr[0].Quantization > 1 && !valueType.SupportsQuantization) { Debug.LogError(String.Format( "{0}.{1} is of type {2} which does not support quantization - ignoring field", managedType.FullName, componentField.Name, componentField.FieldType)); valid = false; } break; } } if (valid) { // If type requires quantization not specifying quantization is an error (log + ignore field) fields.Add(new GhostAuthoringComponent.GhostComponentField { name = componentField.Name, quantization = attr[0].Quantization, interpolate = attr[0].Interpolate }); } } } newComponent = new GhostAuthoringComponent.GhostComponent { name = managedType.FullName, interpolatedClient = true, predictedClient = true, server = true, fields = fields.ToArray(), manualFieldList = false }; var compAttr = managedType.GetCustomAttributes <GhostDefaultComponentAttribute>().ToArray(); if (compAttr.Length > 0) { newComponent.server = (compAttr[0].TargetType & GhostDefaultComponentAttribute.Type.Server) != 0; newComponent.interpolatedClient = (compAttr[0].TargetType & GhostDefaultComponentAttribute.Type.InterpolatedClient) != 0; newComponent.predictedClient = (compAttr[0].TargetType & GhostDefaultComponentAttribute.Type.PredictedClient) != 0; } } if (toDelete.TryGetValue(entityIndex + managedType.FullName, out var compIdx)) { var fields = newComponent.fields; newComponent = self.Components[compIdx]; if (!self.Components[compIdx].manualFieldList) { newComponent.fields = fields; } toDelete.Remove(entityIndex + managedType.FullName); } if (entityIndex > 0) { // Stripping components on child entities is not supported newComponent.server = true; newComponent.interpolatedClient = true; newComponent.predictedClient = true; } newComponent.entityIndex = entityIndex; newComponents.Add(newComponent); } }
void GenerateUpdateSystem(GhostAuthoringComponent ghostInfo) { var ghostInterpolatedComponentRefs = ""; var ghostPredictedComponentRefs = ""; var ghostInterpolatedComponentTypes = ""; var ghostPredictedComponentTypes = ""; var ghostInterpolatedAssignments = ""; var ghostPredictedAssignments = ""; for (int comp = 0; comp < ghostInfo.Components.Length; ++comp) { if (ghostInfo.Components[comp].fields.Length > 0) { if (ghostInfo.Components[comp].interpolatedClient) { if (ghostInterpolatedComponentTypes != "") { ghostInterpolatedComponentTypes += ", "; } ghostInterpolatedComponentTypes += ghostInfo.Components[comp].name; ghostInterpolatedComponentRefs += k_GhostUpdateComponentRefTemplate.Replace("$(GHOSTCOMPONENT)", ghostInfo.Components[comp].name); for (int field = 0; field < ghostInfo.Components[comp].fields.Length; ++field) { ghostInterpolatedAssignments += k_GhostUpdateAssignTemplate.Replace("$(GHOSTCOMPONENT)", ghostInfo.Components[comp].name) .Replace("$(GHOSTFIELD)", ghostInfo.Components[comp].fields[field].name); } } if (ghostInfo.Components[comp].predictedClient) { if (ghostPredictedComponentTypes != "") { ghostPredictedComponentTypes += ", "; } ghostPredictedComponentTypes += ghostInfo.Components[comp].name; ghostPredictedComponentRefs += k_GhostUpdateComponentRefTemplate.Replace("$(GHOSTCOMPONENT)", ghostInfo.Components[comp].name); for (int field = 0; field < ghostInfo.Components[comp].fields.Length; ++field) { ghostPredictedAssignments += k_GhostUpdateAssignTemplate.Replace("$(GHOSTCOMPONENT)", ghostInfo.Components[comp].name) .Replace("$(GHOSTFIELD)", ghostInfo.Components[comp].fields[field].name); } } } } var updateData = k_GhostUpdateSystemTemplate .Replace("$(GHOSTNAME)", target.name) .Replace("$(GHOSTINTERPOLATEDCOMPONENTREFS)", ghostInterpolatedComponentRefs) .Replace("$(GHOSTPREDICTEDCOMPONENTREFS)", ghostPredictedComponentRefs) .Replace("$(GHOSTINTERPOLATEDCOMPONENTTYPES)", ghostInterpolatedComponentTypes) .Replace("$(GHOSTPREDICTEDCOMPONENTTYPES)", ghostPredictedComponentTypes) .Replace("$(GHOSTINTERPOLATEDASSIGNMENTS)", ghostInterpolatedAssignments) .Replace("$(GHOSTPREDICTEDASSIGNMENTS)", ghostPredictedAssignments); File.WriteAllText(Path.Combine(assetPath, ghostInfo.UpdateSystemPath), updateData); }
void GenerateSnapshotData(GhostAuthoringComponent ghostInfo) { var fields = ""; var fieldsGetSet = ""; var predict = ""; var read = ""; var write = ""; var interpolate = ""; var typeProviders = new List <GhostSnapshotValue>(); typeProviders.AddRange(GhostSnapshotValue.GameSpecificTypes); typeProviders.Add(new GhostSnapshotValueQuaternion()); typeProviders.Add(new GhostSnapshotValueFloat()); typeProviders.Add(new GhostSnapshotValueFloat2()); typeProviders.Add(new GhostSnapshotValueFloat3()); typeProviders.Add(new GhostSnapshotValueFloat4()); typeProviders.Add(new GhostSnapshotValueInt()); typeProviders.Add(new GhostSnapshotValueUInt()); for (int comp = 0; comp < ghostInfo.Components.Length; ++comp) { for (int field = 0; field < ghostInfo.Components[comp].fields.Length; ++field) { bool processed = false; foreach (var value in typeProviders) { if (value.CanProcess(ghostInfo.Components[comp].fields[field].DataType, ghostInfo.Components[comp].name, ghostInfo.Components[comp].fields[field].name)) { fields += value.GenerateMembers(ghostInfo.Components[comp].name, ghostInfo.Components[comp].fields[field].name); fieldsGetSet += value.GenerateGetSet(ghostInfo.Components[comp].name, ghostInfo.Components[comp].fields[field].name, ghostInfo.Components[comp].fields[field].quantization); predict += value.GeneratePredict(ghostInfo.Components[comp].name, ghostInfo.Components[comp].fields[field].name); read += value.GenerateRead(ghostInfo.Components[comp].name, ghostInfo.Components[comp].fields[field].name); write += value.GenerateWrite(ghostInfo.Components[comp].name, ghostInfo.Components[comp].fields[field].name); if (ghostInfo.Components[comp].fields[field].interpolate) { interpolate += value.GenerateInterpolate(ghostInfo.Components[comp].name, ghostInfo.Components[comp].fields[field].name); } processed = true; break; } } if (!processed) { Debug.LogError("Unhandled type " + ghostInfo.Components[comp].fields[field].DataType); } } } var snapshotData = k_SnapshotDataTemplate .Replace("$(GHOSTNAME)", target.name) .Replace("$(GHOSTFIELDS)", fields) .Replace("$(GHOSTFIELDSGETSET)", fieldsGetSet) .Replace("$(GHOSTPREDICT)", predict) .Replace("$(GHOSTREAD)", read) .Replace("$(GHOSTWRITE)", write) .Replace("$(GHOSTINTERPOLATE)", interpolate); File.WriteAllText(Path.Combine(assetPath, ghostInfo.SnapshotDataPath), snapshotData); }