Scripting: Basic copying of entities during runtime + Reference Components using UUID

This commit is contained in:
Simon Lübeß
2023-09-21 14:23:00 +02:00
parent ba54d48dfe
commit 446dc2ca2a
7 changed files with 175 additions and 43 deletions
+77 -15
View File
@@ -299,20 +299,26 @@ static class ScriptEngine
}
/// Creates an instance of the given component class referencing the specified entity instance.
private static MonoObject* CreateComponentReferenceInstance(ScriptClass componentClass, MonoObject* entityReferenceInstance)
private static MonoObject* CreateComponentReferenceInstance(ScriptClass componentClass, UUID id)//MonoObject* entityReferenceInstance)
{
MonoObject* componentInstance = componentClass.CreateInstance();
// TODO: We could cache the property, but this might be fine
MonoClassField* idField = Mono.mono_class_get_field_from_name(componentClass.[Friend]_monoClass, "_uuid");
s_ComponentRoot.SetFieldValue<UUID>(componentInstance, idField, id);
/*// TODO: We could cache the property, but this might be fine
MonoProperty* entityProperty = Mono.mono_class_get_property_from_name(componentClass.[Friend]_monoClass, "Entity");
MonoObject* exception = null;
//#unwarn
//Mono.mono_property_set_value(entityProperty, componentInstance, (void**)&entityReferenceInstance, &exception);
#unwarn
Mono.mono_property_set_value(entityProperty, componentInstance, (void**)&entityReferenceInstance, &exception);
Mono.mono_property_set_value(entityProperty, componentInstance, (void**)&id, &exception);
if (exception != null)
ScriptEngine.HandleMonoException((MonoException*)exception, null);
ScriptEngine.HandleMonoException((MonoException*)exception, null);*/
return componentInstance;
}
@@ -345,7 +351,7 @@ static class ScriptEngine
case .Component:
// Get or create entity reference
UUID referencedId = field.GetData<UUID>();
MonoObject* referencedEntity = GetOrCreateScriptReferenceInstance(referencedId);
//MonoObject* referencedEntity = GetOrCreateScriptReferenceInstance(referencedId);
MonoType* fieldMonoType = scriptField.GetMonoType();
@@ -353,7 +359,8 @@ static class ScriptEngine
var componentClass = ComponentClasses[componentType.FullName];
MonoObject* componentInstance = CreateComponentReferenceInstance(componentClass, referencedEntity);
//MonoObject* componentInstance = CreateComponentReferenceInstance(componentClass, referencedEntity);
MonoObject* componentInstance = CreateComponentReferenceInstance(componentClass, referencedId);
script.Instance.SetFieldValue(scriptField, componentInstance);
@@ -363,26 +370,81 @@ static class ScriptEngine
}
}
}
public static void CopyFieldsToInstance(ScriptComponent* targetScript, ScriptComponent* sourceScript, Dictionary<UUID, UUID> sourceIdToTargetId)
{
Debug.Profiler.ProfileFunction!();
Log.EngineLogger.AssertDebug(targetScript.Instance.ScriptClass == sourceScript.Instance.ScriptClass);
for (let (name, scriptField) in sourceScript.Instance.ScriptClass.Fields)
{
Debug.Profiler.ProfileScope!("Copy Field");
targetScript.Instance.CopyFieldValue(scriptField, sourceScript.Instance);
switch (scriptField.FieldType)
{
case .Entity:
let sourceEntityReference = sourceScript.Instance.GetFieldValue<MonoObject*>(scriptField);
MonoObject* referencedEntity = sourceEntityReference;
if (sourceEntityReference != null)
{
let idField = Mono.mono_class_get_field_from_name(s_EntityRoot._monoClass, "_uuid");
UUID sourceId = s_EntityRoot.GetFieldValue<UUID>(sourceEntityReference, idField);
// Check if we need to translate, copy otherwise
if (sourceIdToTargetId.TryGetValue(sourceId, let referencedId))
{
// On the C# side we actually differentiate between an Entity and the Script
// in the sense that getting an entity and a script yields two different results (one creates a new Entity-Class instance, the other returns the actual instance).
// But here its just easier to always use the script instance.
// Obviously breaks once we support multiple scripts per entity.
referencedEntity = GetOrCreateScriptReferenceInstance(referencedId);
}
}
targetScript.Instance.SetFieldValue(scriptField, referencedEntity);
case .Component:
// We create a new instance of a component class
MonoType* type = scriptField.GetMonoType();
SharpType sharpType = ScriptEngine.GetSharpType(type);
let sourceComponentReference = sourceScript.Instance.GetFieldValue<MonoObject*>(scriptField);
MonoObject* componentInstance = sourceComponentReference;
// Get or create entity reference
if (sourceComponentReference != null)
{
let idField = Mono.mono_class_get_field_from_name(s_EngineObject._monoClass, "_uuid");
UUID sourceId = s_EntityRoot.GetFieldValue<UUID>(sourceComponentReference, idField);
MonoType* fieldMonoType = scriptField.GetMonoType();
var componentClass = ComponentClasses[sharpType.FullName];
SharpType componentType = ScriptEngine.GetSharpType(fieldMonoType);
MonoObject* componentInstance = script.Instance.CreateComponentInstance(componentClass);
script.Instance.SetFieldValue(scriptField, componentInstance);
var componentClass = ComponentClasses[componentType.FullName];
if (sourceIdToTargetId.TryGetValue(sourceId, let targetId))
{
// Create reference for translated id
componentInstance = CreateComponentReferenceInstance(componentClass, targetId);
}
componentType.ReleaseRef();
}
targetScript.Instance.SetFieldValue(scriptField, componentInstance);
sharpType.ReleaseRef();
default:
script.Instance.SetFieldValue(scriptField, field._data);
targetScript.Instance.CopyFieldValue(scriptField, sourceScript.Instance);
}
}
}
private static MonoAssembly* LoadCSharpAssembly(StringView assemblyPath, bool loadPDB = false)
{
Debug.Profiler.ProfileFunction!();
List<uint8> data = new List<uint8>(1024);
File.ReadAll(assemblyPath, data);
@@ -114,6 +114,13 @@ class ScriptInstance : RefCounter
_scriptClass.SetFieldValue<T>(_instance, field.[Friend]_monoField, value);
}
public void CopyFieldValue(ScriptField field, ScriptInstance sourceInstance)
{
// TODO: I hate this!
var data = sourceInstance.GetFieldValue<uint8[sizeof(GlitchyEngine.Math.Matrix)]>(field);
SetFieldValue(field, data);
}
/// Creates a new instance of the given component class and initializes it for the current entity.
public MonoObject* CreateComponentInstance(ScriptClass componentClassType)
{
+69 -8
View File
@@ -29,6 +29,8 @@ namespace GlitchyEngine.World
// Maps ids to the entities they represent.
private Dictionary<UUID, EcsEntity> _idToEntity = new .() ~ delete _;
private HashSet<EcsEntity> _updateBlockList = new .() ~ delete _;
public Entity ActiveCamera => {
Entity cameraEntity = .();
@@ -99,7 +101,7 @@ namespace GlitchyEngine.World
if (initializeScripts)
{
CopyComponents<NativeScriptComponent>(this, target);
//CopyComponents<NativeScriptComponent>(this, target);
// Copy ScriptComponents... needs extra handling for the script instances
for (let (sourceHandle, sourceComponent) in _ecsWorld.Enumerate<ScriptComponent>())
@@ -583,6 +585,8 @@ namespace GlitchyEngine.World
if (mode.HasFlag(.Scripts))
{
Debug.Profiler.ProfileScope!("Update scripts");
// Run scripts
for (var (entity, script) in _ecsWorld.Enumerate<NativeScriptComponent>())
{
@@ -595,12 +599,9 @@ namespace GlitchyEngine.World
script.Instance.[Friend]OnUpdate(gameTime);
}
/*}
if (mode.HasFlag(.Runtime) || mode.HasFlag(.Editor))
{*/
// Run scripts
for (var (entity, script) in _ecsWorld.Enumerate<ScriptComponent>())
for (let (entity, script) in _ecsWorld.Enumerate<ScriptComponent>())
{
if (!script.IsCreated)
{
@@ -615,7 +616,12 @@ namespace GlitchyEngine.World
}
if (mode.HasFlag(.Runtime))
script.Instance.InvokeOnUpdate(gameTime.DeltaTime);
{
if (_updateBlockList.Contains(entity))
_updateBlockList.Remove(entity);
else
script.Instance.InvokeOnUpdate(gameTime.DeltaTime);
}
}
}
@@ -761,11 +767,50 @@ namespace GlitchyEngine.World
*/
public Entity CreateInstance(Entity entity)
{
Log.EngineLogger.Info("Ja moin");
List<Entity> newEntities = scope .();
Dictionary<EcsEntity, EcsEntity> sourceToTargetEntity = scope .();
Dictionary<UUID, UUID> sourceIdToTargetId = scope .();
Dictionary<UUID, Entity> targetIdToSourceEntity = scope .();
Entity CopyEntityAndChildren(Entity original)
{
Entity copy = CreateEntity(original.Name);
// TODO: Copy components
newEntities.Add(copy);
sourceToTargetEntity.Add(original.Handle, copy.Handle);
sourceIdToTargetId.Add(original.UUID, copy.UUID);
targetIdToSourceEntity.Add(copy.UUID, original);
_updateBlockList.Add(copy.Handle);
CopyComponent<MeshRendererComponent>(original, copy);
CopyComponent<MeshComponent>(original, copy);
CopyComponent<EditorComponent>(original, copy);
CopyComponent<SpriteRendererComponent>(original, copy);
CopyComponent<CircleRendererComponent>(original, copy);
CopyComponent<CameraComponent>(original, copy);
CopyComponent<LightComponent>(original, copy);
CopyComponent<Rigidbody2DComponent>(original, copy);
CopyComponent<BoxCollider2DComponent>(original, copy);
CopyComponent<CircleCollider2DComponent>(original, copy);
CopyComponent<PolygonCollider2DComponent>(original, copy);
// Copy ScriptComponent... needs extra handling for the script instances
if (original.TryGetComponent<ScriptComponent>(let sourceScript))
{
ScriptComponent* targetScript = copy.AddComponent<ScriptComponent>();
targetScript.ScriptClassName = sourceScript.ScriptClassName;
// Initializes the created instance
// TODO: this returns false, if no script with ScriptClassName exists, we have to handle this case correctly I think.
ScriptEngine.InitializeInstance(copy, targetScript);
// TODO: Copy Data from one instance to another
//ScriptEngine.CopyFieldsToInstance(targetScript, sourceScript);
}
// This is kinda slow because it's in O(n*m) where n is the tree depth and m is the total number of entities in the scene...
for (let child in original.EnumerateChildren)
@@ -778,6 +823,22 @@ namespace GlitchyEngine.World
}
Entity newEntity = CopyEntityAndChildren(entity);
for (let copy in newEntities)
{
if (copy.TryGetComponent<ScriptComponent>(let targetScript))
{
Entity originalEntity = targetIdToSourceEntity[copy.UUID];
if (!originalEntity.TryGetComponent<ScriptComponent>(let sourceScript))
continue;
ScriptEngine.CopyFieldsToInstance(targetScript, sourceScript, sourceIdToTargetId);
//targetScript.Instance.InvokeOnCreate();
}
}
return newEntity;
}