mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Reload scripts in Play Mode!!!
- Dont serialize static fields - Added some profiling to ScriptEngine - Removed superseded ScriptFieldMaps
This commit is contained in:
@@ -560,8 +560,6 @@ namespace GlitchyEditor.EditWindows
|
||||
className == scriptComponent.ScriptClassName))
|
||||
{
|
||||
scriptComponent.ScriptClassName = scriptClass.FullName;
|
||||
|
||||
ScriptEngine.CreateScriptFieldMap(entity);
|
||||
}
|
||||
}
|
||||
ImGui.EndPopup();
|
||||
|
||||
@@ -1003,8 +1003,6 @@ namespace GlitchyEditor
|
||||
// Clear serialized data, so that we don't waste time deserializing it.
|
||||
ClearPrePlaySerializedData();
|
||||
OnSceneStop();
|
||||
|
||||
ScriptEngine.ClearEntityScriptFields();
|
||||
}
|
||||
|
||||
/// Creates a new scene and openes it.
|
||||
@@ -1130,8 +1128,6 @@ namespace GlitchyEditor
|
||||
{
|
||||
CloseCurrentScene();
|
||||
|
||||
ScriptEngine.ClearEntityScriptFields();
|
||||
|
||||
SceneFilePath = scope String(filename);
|
||||
|
||||
using (Scene newScene = new Scene())
|
||||
|
||||
@@ -79,8 +79,6 @@ public struct ScriptFieldInstance
|
||||
}
|
||||
}
|
||||
|
||||
public typealias ScriptFieldMap = Dictionary<String, ScriptFieldInstance>;
|
||||
|
||||
abstract class SharpType : RefCounter
|
||||
{
|
||||
protected String _namespace ~ delete _;
|
||||
|
||||
@@ -136,16 +136,6 @@ static class ScriptEngine
|
||||
private set => SetReference!(s_Context, value);
|
||||
}
|
||||
|
||||
private static Dictionary<UUID, ScriptFieldMap> _entityFields = new .() ~
|
||||
{
|
||||
for (var value in _entityFields.Values)
|
||||
{
|
||||
DeleteDictionaryAndKeys!(value);
|
||||
}
|
||||
|
||||
delete _;
|
||||
};
|
||||
|
||||
private static FileSystemWatcher _userAssemblyWatcher ~ delete _;
|
||||
|
||||
// TODO: This should be a global setting somewhere
|
||||
@@ -294,20 +284,11 @@ static class ScriptEngine
|
||||
|
||||
_requestingReload = true;
|
||||
|
||||
// TODO: Temporary, we want to be able to reload while in play-mode. (+ Editor Scripts will be a thing some day)
|
||||
if (_entityScriptInstances.Count > 0)
|
||||
{
|
||||
Log.EngineLogger.Warning("There are script instances. Skipping assembly reload.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.EngineLogger.Info("Script reload requested.");
|
||||
|
||||
Application.Instance.InvokeOnMainThread(new () =>
|
||||
{
|
||||
Log.EngineLogger.Info("Reloading scripts...");
|
||||
ReloadAssemblies();
|
||||
Log.EngineLogger.Info("Scripts reloaded!");
|
||||
|
||||
_requestingReload = false;
|
||||
_userAssemblyWatcher.StartRaisingEvents();
|
||||
@@ -319,19 +300,10 @@ static class ScriptEngine
|
||||
_userAssemblyWatcher.StartRaisingEvents();
|
||||
}
|
||||
|
||||
/// Gets rid of all entity fields.
|
||||
public static void ClearEntityScriptFields()
|
||||
{
|
||||
for (var value in _entityFields.Values)
|
||||
{
|
||||
DeleteDictionaryAndKeys!(value);
|
||||
}
|
||||
|
||||
_entityFields.Clear();
|
||||
}
|
||||
|
||||
static void LoadScriptAssemblies()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
CreateAppDomain("GlitchyEngineScriptRuntime");
|
||||
(s_CoreAssembly, s_CoreAssemblyImage) = LoadAssembly("resources/scripts/ScriptCore.dll", _debuggingEnabled);
|
||||
|
||||
@@ -393,6 +365,8 @@ static class ScriptEngine
|
||||
Context = null;
|
||||
}
|
||||
|
||||
/// Instantiates the entities script components script and runs the constructor.
|
||||
/// Disposes of and replaces the old instance, if one exists.
|
||||
public static bool InitializeInstance(Entity entity, ScriptComponent* script)
|
||||
{
|
||||
ScriptClass scriptClass = GetScriptClass(script.ScriptClassName);
|
||||
@@ -471,60 +445,6 @@ static class ScriptEngine
|
||||
return componentInstance;
|
||||
}
|
||||
|
||||
public static void CopyEditorFieldsToInstance(Entity entity, ScriptComponent* script)
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(script.Instance != null);
|
||||
|
||||
// Technically the map is for a different entity (namely the editor-entity),
|
||||
// however the UUID is the same, so we get the correct field map
|
||||
let fields = GetScriptFieldMap(entity);
|
||||
|
||||
for (var (fieldName, field) in fields)
|
||||
{
|
||||
// TODO: a litte assertion maybe?
|
||||
|
||||
ScriptField scriptField = script.Instance.ScriptClass.Fields[fieldName];
|
||||
|
||||
switch (scriptField.FieldType)
|
||||
{
|
||||
case .Entity:
|
||||
// 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.
|
||||
UUID referencedId = field.GetData<UUID>();
|
||||
MonoObject* referencedEntity = GetOrCreateScriptReferenceInstance(referencedId);
|
||||
|
||||
script.Instance.SetFieldValue(scriptField, referencedEntity);
|
||||
case .Component:
|
||||
// Get or create entity reference
|
||||
UUID referencedId = field.GetData<UUID>();
|
||||
//MonoObject* referencedEntity = GetOrCreateScriptReferenceInstance(referencedId);
|
||||
|
||||
MonoType* fieldMonoType = scriptField.GetMonoType();
|
||||
|
||||
SharpType componentType = ScriptEngine.GetSharpType(fieldMonoType);
|
||||
|
||||
var componentClass = ComponentClasses[componentType.FullName];
|
||||
|
||||
//MonoObject* componentInstance = CreateComponentReferenceInstance(componentClass, referencedEntity);
|
||||
MonoObject* componentInstance = CreateComponentReferenceInstance(componentClass, referencedId);
|
||||
|
||||
script.Instance.SetFieldValue(scriptField, componentInstance);
|
||||
|
||||
componentType.ReleaseRef();
|
||||
case .Struct:
|
||||
// TODO: Handle Structs when copying fields to instance.
|
||||
case .Class:
|
||||
// TODO: Handle Class when copying fields to instance.
|
||||
case .String:
|
||||
// TODO: Handle String when copying fields to instance.
|
||||
default:
|
||||
script.Instance.SetFieldValue(scriptField, field._data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void CopyFieldsToInstance(ScriptComponent* targetScript, ScriptComponent* sourceScript, Dictionary<UUID, UUID> sourceIdToTargetId)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
@@ -661,6 +581,8 @@ static class ScriptEngine
|
||||
|
||||
static (MonoAssembly* assembly, MonoImage* image) LoadAssembly(StringView filepath, bool loadPDB = false)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
MonoAssembly* assembly = LoadCSharpAssembly(filepath, loadPDB);
|
||||
MonoImage* image = Mono.mono_assembly_get_image(assembly);
|
||||
|
||||
@@ -680,6 +602,8 @@ static class ScriptEngine
|
||||
|
||||
private static void GetEntitiesFromAssemblies()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
if (s_AppAssemblyImage == null)
|
||||
return;
|
||||
|
||||
@@ -706,8 +630,11 @@ static class ScriptEngine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetComponentsFromAssemblies()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
MonoTableInfo* typeDefinitionsTable = Mono.mono_image_get_table_info(s_CoreAssemblyImage, .MONO_TABLE_TYPEDEF);
|
||||
int32 numTypes = Mono.mono_table_info_get_rows(typeDefinitionsTable);
|
||||
|
||||
@@ -736,18 +663,41 @@ static class ScriptEngine
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
Mono.mono_domain_set(s_RootDomain, false);
|
||||
Log.EngineLogger.Info("Reloading script assemblies.");
|
||||
|
||||
Dictionary<UUID, SerializedObject> serializedData = scope .();
|
||||
|
||||
SerializeScriptInstances(serializedData);
|
||||
|
||||
Mono.mono_domain_set(s_RootDomain, true);
|
||||
|
||||
Mono.mono_domain_unload(s_AppDomain);
|
||||
|
||||
LoadScriptAssemblies();
|
||||
|
||||
// TODO: ScriptFields might get added
|
||||
// TODO: ScriptField Types may change after reload!
|
||||
// TODO: Scripts may be renamed (probably not detectable (trivially))
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("Initialize Instances");
|
||||
|
||||
// TODO: Reload in play mode
|
||||
// Only scripts that were changed should actually be reinstatiated
|
||||
// We need to create a new instance for every entity
|
||||
for (let (id, scriptInstance) in _entityScriptInstances)
|
||||
{
|
||||
if (Context.GetEntityByID(id) case .Ok(let entity))
|
||||
{
|
||||
ScriptComponent* script = entity.GetComponent<ScriptComponent>();
|
||||
InitializeInstance(entity, script);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(false, "Entities script was just serialized but the entity doesn't exist anymore.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeserializeScriptInstances(serializedData);
|
||||
|
||||
ClearDictionaryAndDeleteValues!(serializedData);
|
||||
|
||||
Log.EngineLogger.Info("Script assemblies reloaded!");
|
||||
}
|
||||
|
||||
public static void Shutdown()
|
||||
@@ -838,58 +788,6 @@ static class ScriptEngine
|
||||
return new SharpClass(classNamespace, className, Mono.mono_class_get_image(monoClass), scriptType);
|
||||
}
|
||||
|
||||
public static void CreateScriptFieldMap(Entity entity)
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(entity.IsValid);
|
||||
|
||||
if (_entityFields.TryGetValue(entity.UUID, var entityFields))
|
||||
{
|
||||
ClearDictionaryAndDeleteKeys!(entityFields);
|
||||
}
|
||||
else
|
||||
{
|
||||
entityFields = new ScriptFieldMap();
|
||||
_entityFields.Add(entity.UUID, entityFields);
|
||||
}
|
||||
|
||||
let scriptComponent = entity.GetComponent<ScriptComponent>();
|
||||
|
||||
ScriptClass scriptClass = GetScriptClass(scriptComponent.ScriptClassName);
|
||||
|
||||
Log.EngineLogger.AssertDebug(scriptClass != null);
|
||||
|
||||
void AddFieldsToMap(Dictionary<StringView, ScriptField> classFields, StringView baseName)
|
||||
{
|
||||
for (let (fieldName, field) in classFields)
|
||||
{
|
||||
/*if (field.FieldType == .Struct && field.SharpType != null)
|
||||
{
|
||||
AddFieldsToMap(field.SharpType.Fields, scope $"{baseName}{fieldName}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
entityFields.Add(new $"{baseName}{fieldName}", ScriptFieldInstance(field.FieldType));
|
||||
}*/
|
||||
|
||||
entityFields.Add(new String(fieldName), ScriptFieldInstance(field.FieldType));
|
||||
}
|
||||
}
|
||||
|
||||
AddFieldsToMap(scriptClass.Fields, "");
|
||||
}
|
||||
|
||||
public static ScriptFieldMap GetScriptFieldMap(Entity entity)
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(entity.IsValid);
|
||||
|
||||
let uuid = entity.UUID;
|
||||
|
||||
// TODO: Entites bekommen noch kein Eintrag hier!!
|
||||
Log.EngineLogger.AssertDebug(_entityFields.ContainsKey(uuid));
|
||||
|
||||
return _entityFields[uuid];
|
||||
}
|
||||
|
||||
/// Returns the script instance or null.
|
||||
public static MonoObject* GetManagedInstance(UUID entityId)
|
||||
{
|
||||
@@ -955,11 +853,10 @@ static class ScriptEngine
|
||||
/// Serializes all script instances
|
||||
public static void SerializeScriptInstances(Dictionary<UUID, SerializedObject> allObjects)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
for (let (id, script) in _entityScriptInstances)
|
||||
{
|
||||
if (script.ScriptClass.ClassName != "SerializationTest")
|
||||
continue;
|
||||
|
||||
SerializedObject object = new SerializedObject(allObjects, script.EntityId);
|
||||
object.Serialize(script);
|
||||
}
|
||||
@@ -968,11 +865,10 @@ static class ScriptEngine
|
||||
/// Deserializes the given data into the script instances
|
||||
public static void DeserializeScriptInstances(Dictionary<UUID, SerializedObject> allObjects)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
for (let (id, script) in _entityScriptInstances)
|
||||
{
|
||||
if (script.ScriptClass.ClassName != "SerializationTest")
|
||||
continue;
|
||||
|
||||
if (allObjects.TryGetValue(id, let object))
|
||||
{
|
||||
object.Deserialize(script);
|
||||
|
||||
@@ -60,6 +60,8 @@ static class ScriptGlue
|
||||
|
||||
public static void RegisterManagedComponents()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
s_AddComponentMethods.Clear();
|
||||
s_HasComponentMethods.Clear();
|
||||
s_RemoveComponentMethods.Clear();
|
||||
|
||||
@@ -209,7 +209,8 @@ class SceneSerializer
|
||||
// TODO: Thats not a good check, I think. At least we know the script class is valid
|
||||
if (ScriptEngine.GetScriptClass(component.ScriptClassName) != null)
|
||||
{
|
||||
let fields = ScriptEngine.GetScriptFieldMap(entity);
|
||||
// TODO: Serialize Script Instance!
|
||||
/*let fields = ScriptEngine.GetScriptFieldMap(entity);
|
||||
|
||||
writer.Identifier("Fields");
|
||||
|
||||
@@ -234,7 +235,7 @@ class SceneSerializer
|
||||
Serialize.Value(writer, ValueView(fieldInstance.Type.GetBeefType(), &fieldInstance.[Friend]_data), gBonEnv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -624,7 +625,8 @@ class SceneSerializer
|
||||
// But that is a bug for me to rediscover in the distant future, so in case this bug occurred and it took ages for you to
|
||||
// figure out what happened: You are welcome :)
|
||||
|
||||
ScriptEngine.CreateScriptFieldMap(entity);
|
||||
// TODO: We need a new way to Serialize/Deserialize these fields!
|
||||
/*ScriptEngine.CreateScriptFieldMap(entity);
|
||||
|
||||
var fields = ScriptEngine.GetScriptFieldMap(entity);
|
||||
|
||||
@@ -708,19 +710,11 @@ class SceneSerializer
|
||||
}
|
||||
|
||||
Try!(reader.ArrayBlockEnd());
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}));
|
||||
/*
|
||||
|
||||
|
||||
SerializeComponent<ScriptComponent>(writer, entity, "ScriptComponent", scope (component) =>
|
||||
{
|
||||
Serialize.Value(writer, "ScriptClass", component.ScriptClass?.FullName);
|
||||
});
|
||||
*/
|
||||
default:
|
||||
Log.EngineLogger.AssertDebug(false, "Unknown component type");
|
||||
//return .Err;
|
||||
|
||||
@@ -453,7 +453,8 @@ public static class EntitySerializer
|
||||
}
|
||||
else if (fieldType.IsClass)
|
||||
{
|
||||
// SerializeClass(fieldName, fieldValue, fieldType);
|
||||
//DeserializeClass(fieldName, fieldValue, fieldType);
|
||||
Log.Error("Class is not yet implemented.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -565,6 +566,10 @@ public static class EntitySerializer
|
||||
|
||||
public static bool SerializeField(FieldInfo fieldInfo)
|
||||
{
|
||||
// TODO: We want to be able to serialize static fields in the future!
|
||||
if (fieldInfo.IsStatic)
|
||||
return false;
|
||||
|
||||
var serializeField = fieldInfo.HasCustomAttribute<SerializeFieldAttribute>();
|
||||
var dontSerializeField = fieldInfo.HasCustomAttribute<DontSerializeFieldAttribute>();
|
||||
//var hideField = fieldInfo.HasCustomAttribute<HideInEditorAttribute>();
|
||||
|
||||
Reference in New Issue
Block a user