mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-11-04 12:51:48 +00:00
Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7fb9af0c7 | ||
|
|
fff1abf012 | ||
|
|
8e9efbb367 | ||
|
|
8244033ac5 | ||
|
|
e3619ae93c | ||
|
|
0087bca27b | ||
|
|
9119613e62 | ||
|
|
1636f3a5d0 | ||
|
|
886d2760bc | ||
|
|
6da69aad03 | ||
|
|
6c184aafe0 | ||
|
|
5c18f61da4 | ||
|
|
1435dac4d6 | ||
|
|
abf7a01917 | ||
|
|
c8d63a9c36 | ||
|
|
9ccaf8977a | ||
|
|
e758d6fd09 | ||
|
|
1cc604cd51 |
@@ -21,7 +21,7 @@ IconFile = "$(ProjectDir)/Resources/Textures/GlitchyEngineIcon.ico"
|
|||||||
|
|
||||||
[Configs.Debug.Win64]
|
[Configs.Debug.Win64]
|
||||||
PostBuildCmds = ["CopyFilesIfNewer(\"$(WorkspaceDir)/bin/vswhere.exe\", \"$(TargetDir)\")"]
|
PostBuildCmds = ["CopyFilesIfNewer(\"$(WorkspaceDir)/bin/vswhere.exe\", \"$(TargetDir)\")"]
|
||||||
DebugCommandArguments = "\"D:\\Development\\Git\\SingleStateToOrbit\""
|
DebugCommandArguments = "\"$(ProjectDir)/../GlitchyEngine.Test/content/TestProject\""
|
||||||
PreprocessorMacros = ["DEBUG", "IMGUI"]
|
PreprocessorMacros = ["DEBUG", "IMGUI"]
|
||||||
|
|
||||||
[Configs.Release.Win64]
|
[Configs.Release.Win64]
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ namespace GlitchyEditor.CodeEditors;
|
|||||||
/// Runs on its own worker thread so the editor never blocks on a busy or starting Visual Studio.
|
/// Runs on its own worker thread so the editor never blocks on a busy or starting Visual Studio.
|
||||||
class DteOpenRequest
|
class DteOpenRequest
|
||||||
{
|
{
|
||||||
private const int ColdStartMaxPolls = 120;
|
|
||||||
private const int ColdStartPollIntervalMs = 500;
|
|
||||||
|
|
||||||
private append String _solutionPath = .();
|
private append String _solutionPath = .();
|
||||||
private append String _devenvPath = .();
|
private append String _devenvPath = .();
|
||||||
private append String _fileName = .();
|
private append String _fileName = .();
|
||||||
@@ -40,6 +37,7 @@ class DteOpenRequest
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int LineNumber;
|
public int LineNumber;
|
||||||
|
public int ColumnNumber;
|
||||||
|
|
||||||
private static int32 sWorkerActive = 0;
|
private static int32 sWorkerActive = 0;
|
||||||
|
|
||||||
@@ -83,7 +81,7 @@ class DteOpenRequest
|
|||||||
if (FileName.IsEmpty)
|
if (FileName.IsEmpty)
|
||||||
VisualStudioDte.ActivateMainWindow(dte);
|
VisualStudioDte.ActivateMainWindow(dte);
|
||||||
else
|
else
|
||||||
VisualStudioDte.OpenFileAtLine(dte, FileName, LineNumber).IgnoreError();
|
VisualStudioDte.OpenFileAtLine(dte, FileName, LineNumber, ColumnNumber).IgnoreError();
|
||||||
|
|
||||||
delete dte;
|
delete dte;
|
||||||
|
|
||||||
@@ -107,13 +105,13 @@ class DteOpenRequest
|
|||||||
|
|
||||||
// Wait for the new instance to finish loading the solution (Solution.FullName stays empty
|
// Wait for the new instance to finish loading the solution (Solution.FullName stays empty
|
||||||
// until then), then open the file in it.
|
// until then), then open the file in it.
|
||||||
for (int i < ColdStartMaxPolls)
|
for (int i < VisualStudioDte.MaxPolls)
|
||||||
{
|
{
|
||||||
Thread.Sleep(ColdStartPollIntervalMs);
|
Thread.Sleep(VisualStudioDte.PollIntervalMs);
|
||||||
|
|
||||||
if (VisualStudioDte.FindRunningInstance(SolutionPath) case .Ok(let startedDte))
|
if (VisualStudioDte.FindRunningInstance(SolutionPath) case .Ok(let startedDte))
|
||||||
{
|
{
|
||||||
VisualStudioDte.OpenFileAtLine(startedDte, FileName, LineNumber).IgnoreError();
|
VisualStudioDte.OpenFileAtLine(startedDte, FileName, LineNumber, ColumnNumber).IgnoreError();
|
||||||
|
|
||||||
delete startedDte;
|
delete startedDte;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ namespace GlitchyEditor.CodeEditors;
|
|||||||
/// All methods must be called from a thread that is initialized for COM (CoInitializeEx).
|
/// All methods must be called from a thread that is initialized for COM (CoInitializeEx).
|
||||||
static class VisualStudioDte
|
static class VisualStudioDte
|
||||||
{
|
{
|
||||||
|
public const int MaxPolls = 120;
|
||||||
|
public const int PollIntervalMs = 500;
|
||||||
|
|
||||||
private const String MonikerPrefix = "!VisualStudio.DTE";
|
private const String MonikerPrefix = "!VisualStudio.DTE";
|
||||||
/// EnvDTE.Constants.vsViewKindCode
|
/// EnvDTE.Constants.vsViewKindCode
|
||||||
private const String ViewKindCode = "{7651A701-06E5-11D1-8EBD-00A0C90F26EA}";
|
private const String ViewKindCode = "{7651A701-06E5-11D1-8EBD-00A0C90F26EA}";
|
||||||
@@ -107,7 +110,7 @@ static class VisualStudioDte
|
|||||||
|
|
||||||
/// Opens the given file in the code editor of the given Visual Studio instance and
|
/// Opens the given file in the code editor of the given Visual Studio instance and
|
||||||
/// jumps to lineNumber (1-based, ignored if <= 0).
|
/// jumps to lineNumber (1-based, ignored if <= 0).
|
||||||
public static Result<void> OpenFileAtLine(ComDispatch dte, StringView fileName, int lineNumber)
|
public static Result<void> OpenFileAtLine(ComDispatch dte, StringView fileName, int lineNumber, int columnNumber)
|
||||||
{
|
{
|
||||||
ComDispatch itemOperations = Try!(dte.GetObjectProperty("ItemOperations"));
|
ComDispatch itemOperations = Try!(dte.GetObjectProperty("ItemOperations"));
|
||||||
defer delete itemOperations;
|
defer delete itemOperations;
|
||||||
@@ -125,7 +128,7 @@ static class VisualStudioDte
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lineNumber > 0)
|
if (lineNumber > 0)
|
||||||
GoToLine(dte, lineNumber);
|
GoToLine(dte, lineNumber, columnNumber);
|
||||||
|
|
||||||
ActivateMainWindow(dte);
|
ActivateMainWindow(dte);
|
||||||
|
|
||||||
@@ -198,18 +201,18 @@ static class VisualStudioDte
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GoToLine(ComDispatch dte, int lineNumber)
|
private static void GoToLine(ComDispatch dte, int lineNumber, int columnNumber)
|
||||||
{
|
{
|
||||||
ComDispatch document = null;
|
ComDispatch document = null;
|
||||||
defer { delete document; }
|
defer { delete document; }
|
||||||
|
|
||||||
// Directly after OpenFile the ActiveDocument might not be set yet, so retry for a bit.
|
// Directly after OpenFile the ActiveDocument might not be set yet, so retry for a bit.
|
||||||
for (int i < 10)
|
for (int i < MaxPolls)
|
||||||
{
|
{
|
||||||
if (dte.GetObjectProperty("ActiveDocument") case .Ok(out document))
|
if (dte.GetObjectProperty("ActiveDocument") case .Ok(out document))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
Thread.Sleep(100);
|
Thread.Sleep(PollIntervalMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document == null)
|
if (document == null)
|
||||||
@@ -224,9 +227,14 @@ static class VisualStudioDte
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VARIANT[2] gotoLineArgs = .(VARIANT.FromInt32((int32)lineNumber), VARIANT.FromBool(false));
|
/*VARIANT[2] gotoLineArgs = .(VARIANT.FromInt32((int32)lineNumber), VARIANT.FromBool(false));
|
||||||
|
|
||||||
if (selection.InvokeMethod("GotoLine", gotoLineArgs) case .Ok(var gotoResult))
|
if (selection.InvokeMethod("GotoLine", gotoLineArgs) case .Ok(var gotoResult))
|
||||||
|
VariantClear(&gotoResult);*/
|
||||||
|
|
||||||
|
VARIANT[3] gotoLineArgs = .(VARIANT.FromInt32((int32)lineNumber), VARIANT.FromInt32((int32)columnNumber), VARIANT.FromBool(false));
|
||||||
|
|
||||||
|
if (selection.InvokeMethod("MoveTo", gotoLineArgs) case .Ok(var gotoResult))
|
||||||
VariantClear(&gotoResult);
|
VariantClear(&gotoResult);
|
||||||
|
|
||||||
delete selection;
|
delete selection;
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace GlitchyEditor.CodeEditors;
|
namespace GlitchyEditor.CodeEditors;
|
||||||
|
|
||||||
interface IIdeAdapter
|
interface IIdeAdapter
|
||||||
{
|
{
|
||||||
void OpenScript(StringView fileName, int lineNumber = 0);
|
/// Opens the current script solution and the specified script file in the IDE. Allows to specify a line number and a column that the cursor will be moved to.
|
||||||
|
/// @param fileName The absolute path to the script file. Can be null to just open the script project (Note: the same thing can be achieved by using @see OpenScriptProject).
|
||||||
|
/// @param lineNumber The line number that the cursor will be placed at (1-based, so 1 is first line).
|
||||||
|
/// @param lineNumber The column number that the cursor will be placed at (1-based, so 1 is the start of the line).
|
||||||
|
void OpenScript(StringView fileName, int lineNumber = 0, int columnNumber = 0);
|
||||||
|
|
||||||
|
/// Opens the current script solution in the IDE.
|
||||||
void OpenScriptProject();
|
void OpenScriptProject();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,15 @@ class RiderIdeAdapter : IIdeAdapter
|
|||||||
_ideInstallation = ideInstallation;
|
_ideInstallation = ideInstallation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenScript(StringView fileName, int lineNumber)
|
public void OpenScript(StringView fileName, int lineNumber, int columnNumber)
|
||||||
{
|
{
|
||||||
String solutionPath = scope .();
|
String solutionPath = scope .();
|
||||||
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
||||||
|
|
||||||
ProcessStartInfo startInfo = scope .();
|
ProcessStartInfo startInfo = scope .();
|
||||||
startInfo.SetFileName(_ideInstallation.Path);
|
startInfo.SetFileName(_ideInstallation.Path);
|
||||||
startInfo.SetArguments(scope $"{solutionPath} --line {lineNumber} {fileName}");
|
// For rider columns appear to be 0-indexed
|
||||||
|
startInfo.SetArguments(scope $"\"{solutionPath}\" --line {lineNumber} --column {columnNumber - 1} \"{fileName}\"");
|
||||||
|
|
||||||
scope SpawnedProcess().Start(startInfo);
|
scope SpawnedProcess().Start(startInfo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,17 +18,17 @@ class VisualStudioIdeAdapter : IIdeAdapter
|
|||||||
_ideInstallation = ideInstallation;
|
_ideInstallation = ideInstallation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenScript(StringView fileName, int lineNumber)
|
public void OpenScript(StringView fileName, int lineNumber, int columnNumber)
|
||||||
{
|
{
|
||||||
Open(fileName, lineNumber);
|
Open(fileName, lineNumber, columnNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenScriptProject()
|
public void OpenScriptProject()
|
||||||
{
|
{
|
||||||
Open(null, 0);
|
Open(null, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Open(StringView fileName, int lineNumber)
|
private void Open(StringView fileName, int lineNumber, int columnNumber)
|
||||||
{
|
{
|
||||||
String solutionPath = scope String();
|
String solutionPath = scope String();
|
||||||
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
||||||
@@ -57,6 +57,7 @@ class VisualStudioIdeAdapter : IIdeAdapter
|
|||||||
request.DevenvPath = _ideInstallation.Path;
|
request.DevenvPath = _ideInstallation.Path;
|
||||||
request.FileName = fileName;
|
request.FileName = fileName;
|
||||||
request.LineNumber = lineNumber;
|
request.LineNumber = lineNumber;
|
||||||
|
request.ColumnNumber = columnNumber;
|
||||||
|
|
||||||
DteOpenRequest.Start(request);
|
DteOpenRequest.Start(request);
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ namespace GlitchyEditor.EditWindows
|
|||||||
{
|
{
|
||||||
_entityToHighlight = e;
|
_entityToHighlight = e;
|
||||||
|
|
||||||
|
SelectEntity(e, true);
|
||||||
|
|
||||||
Entity walker = _entityToHighlight;
|
Entity walker = _entityToHighlight;
|
||||||
|
|
||||||
while (walker.Parent != null)
|
while (walker.Parent != null)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using GlitchyEngine.Scripting;
|
|||||||
using GlitchyEngine.Renderer;
|
using GlitchyEngine.Renderer;
|
||||||
using GlitchyEngine;
|
using GlitchyEngine;
|
||||||
using GlitchyEditor.CodeEditors;
|
using GlitchyEditor.CodeEditors;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace GlitchyEditor.EditWindows;
|
namespace GlitchyEditor.EditWindows;
|
||||||
|
|
||||||
@@ -295,12 +296,30 @@ class LogWindow : EditorWindow
|
|||||||
{
|
{
|
||||||
if (ImGui.CollapsingHeader(message.Message.Ptr))
|
if (ImGui.CollapsingHeader(message.Message.Ptr))
|
||||||
{
|
{
|
||||||
// Show the native to managed entry point only if we show engine messages
|
String lineBuffer = scope .();
|
||||||
|
for (StackFrameInfo frameInfo in message.Source.Exception.StackTrace.Frames)
|
||||||
|
{
|
||||||
|
String relativeSourcePath = scope .(frameInfo.FileName);
|
||||||
|
|
||||||
if (_showEngineMessages)
|
relativeSourcePath.Replace(Editor.Instance.ContentManager.ResourcesDirectory, "Resources");
|
||||||
ImGui.TextUnformatted(message.Source.Exception.StackTrace);
|
relativeSourcePath.Replace(Editor.Instance.ContentManager.AssetDirectory, "Assets");
|
||||||
else
|
|
||||||
ImGui.TextUnformatted(message.Source.Exception.CleanStackTrace);
|
//Path.GetRelativePath(frameInfo.FileName, Editor.Instance.CurrentProject.WorkspacePath, relativeSourcePath);
|
||||||
|
|
||||||
|
StackFrameInfo sfi = scope .(relativeSourcePath, frameInfo.MethodSignature, frameInfo.Line, frameInfo.Column);
|
||||||
|
|
||||||
|
//_editor.CurrentProject.PathInProject();
|
||||||
|
|
||||||
|
lineBuffer.Clear();
|
||||||
|
lineBuffer.SetF($" {sfi}");
|
||||||
|
|
||||||
|
float width = ImGui.CalcTextSize(lineBuffer.CStr()).x;
|
||||||
|
if (ImGui.Selectable(lineBuffer, false, .None, .(width, 0)))
|
||||||
|
{
|
||||||
|
// TODO: Columns?
|
||||||
|
Editor.Instance.IdeAdapter.OpenScript(frameInfo.FileName, frameInfo.Line, frameInfo.Column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
}
|
}
|
||||||
@@ -319,6 +338,13 @@ class LogWindow : EditorWindow
|
|||||||
{
|
{
|
||||||
Editor.Instance.IdeAdapter.OpenScript(message.Source.MessageOrigin.FileName, message.Source.MessageOrigin.LineNumber);
|
Editor.Instance.IdeAdapter.OpenScript(message.Source.MessageOrigin.FileName, message.Source.MessageOrigin.LineNumber);
|
||||||
}
|
}
|
||||||
|
else if (message.Source.Exception != null)
|
||||||
|
{
|
||||||
|
// TODO: Column?
|
||||||
|
let firstFrame = message.Source.Exception.StackTrace.Frames[0];
|
||||||
|
Editor.Instance.IdeAdapter.OpenScript(firstFrame.FileName, firstFrame.Line);
|
||||||
|
//message.Source.Exception.StackTrace
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dont show the counter if we only have one message.
|
// Dont show the counter if we only have one message.
|
||||||
@@ -373,15 +399,8 @@ class LogWindow : EditorWindow
|
|||||||
|
|
||||||
public void LogException(DateTime timestamp, ScriptException exception)
|
public void LogException(DateTime timestamp, ScriptException exception)
|
||||||
{
|
{
|
||||||
StringView firstLine = exception.StackTrace;
|
|
||||||
|
|
||||||
int firstInIndex = exception.StackTrace.IndexOf("\n");
|
|
||||||
|
|
||||||
if (firstInIndex != -1)
|
|
||||||
firstLine = firstLine.Substring(0, firstInIndex);
|
|
||||||
|
|
||||||
String message = scope .(128);
|
String message = scope .(128);
|
||||||
message.AppendF($"Exception: \"{exception.FullName}\" | Message: \"{exception.Message}\" {firstLine}\0");
|
message.AppendF($"Exception: \"{exception.FullName}\" | Message: \"{exception.Message}\" {exception.StackTrace.Frames[0]}\0");
|
||||||
|
|
||||||
// TODO: are mono exceptions never engine only?
|
// TODO: are mono exceptions never engine only?
|
||||||
LogMessage logMessage = new LogMessage(timestamp, message, .Error, new MessageSource(){Entity = exception.EntityId, Exception = exception, IsEngineMessage = false});
|
LogMessage logMessage = new LogMessage(timestamp, message, .Error, new MessageSource(){Entity = exception.EntityId, Exception = exception, IsEngineMessage = false});
|
||||||
|
|||||||
@@ -75,14 +75,6 @@ namespace GlitchyEditor
|
|||||||
|
|
||||||
private float _fixedTimestep = 1.0f / 60.0f;
|
private float _fixedTimestep = 1.0f / 60.0f;
|
||||||
|
|
||||||
append List<(String Name, String Path)> _recentScenePaths = .() ~ {
|
|
||||||
for (var item in _)
|
|
||||||
{
|
|
||||||
delete item.Name;
|
|
||||||
delete item.Path;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private GameTime _simulationGameTime = new .() ~ delete _;
|
private GameTime _simulationGameTime = new .() ~ delete _;
|
||||||
|
|
||||||
/// Gets or sets the path of the current scene.
|
/// Gets or sets the path of the current scene.
|
||||||
@@ -720,6 +712,8 @@ namespace GlitchyEditor
|
|||||||
|
|
||||||
private void CloseCurrentProject()
|
private void CloseCurrentProject()
|
||||||
{
|
{
|
||||||
|
Log.EngineLogger.Trace("Closing current Project...");
|
||||||
|
|
||||||
CloseCurrentScene();
|
CloseCurrentScene();
|
||||||
|
|
||||||
SetEditorScene(null);
|
SetEditorScene(null);
|
||||||
@@ -729,6 +723,8 @@ namespace GlitchyEditor
|
|||||||
// TODO: Do actual work here!
|
// TODO: Do actual work here!
|
||||||
delete _currentProject;
|
delete _currentProject;
|
||||||
_currentProject = null;
|
_currentProject = null;
|
||||||
|
|
||||||
|
Log.EngineLogger.Trace("Project closed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<void> LoadAndOpenProject(StringView workspacePath)
|
private Result<void> LoadAndOpenProject(StringView workspacePath)
|
||||||
@@ -752,29 +748,43 @@ namespace GlitchyEditor
|
|||||||
|
|
||||||
CloseCurrentProject();
|
CloseCurrentProject();
|
||||||
|
|
||||||
|
Log.EngineLogger.Trace("Opening Project...");
|
||||||
|
|
||||||
_currentProject = project;
|
_currentProject = project;
|
||||||
|
|
||||||
_editor.CurrentProject = _currentProject;
|
_editor.CurrentProject = _currentProject;
|
||||||
|
|
||||||
|
Log.EngineLogger.Trace("Preparing asset manager...");
|
||||||
|
|
||||||
String appAssemblyPath = scope String();
|
String appAssemblyPath = scope String();
|
||||||
_contentManager.SetAssetCacheDirectory(_currentProject.GetScopedPath!(".cache"));
|
_contentManager.SetAssetCacheDirectory(_currentProject.GetScopedPath!(".cache"));
|
||||||
_contentManager.SetAssetDirectory(_currentProject.AssetsFolder);
|
_contentManager.SetAssetDirectory(_currentProject.AssetsFolder);
|
||||||
|
|
||||||
_currentProject.PathInProject(appAssemblyPath, scope $".cache/bin/{_currentProject.Name}.dll");
|
_currentProject.PathInProject(appAssemblyPath, scope $".cache/bin/{_currentProject.Name}.dll");
|
||||||
|
|
||||||
ScriptEngine.SetAppAssemblyPath(appAssemblyPath);
|
Log.EngineLogger.Trace("Loading script assembly...");
|
||||||
|
ScriptEngine.SetAppAssemblyPath(appAssemblyPath, retainScriptData: false);
|
||||||
|
|
||||||
if (!_currentProject.UserSettings.LastOpenedScene.IsWhiteSpace)
|
if (!_currentProject.UserSettings.LastOpenedScene.IsWhiteSpace)
|
||||||
{
|
{
|
||||||
String lastSceneFile = _currentProject.GetScopedPath!(_currentProject.UserSettings.LastOpenedScene);
|
String lastSceneFile = _currentProject.GetScopedPath!(_currentProject.UserSettings.LastOpenedScene);
|
||||||
|
|
||||||
if (File.Exists(lastSceneFile))
|
if (File.Exists(lastSceneFile))
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Trace("Opening last opened scene...");
|
||||||
LoadSceneFile(lastSceneFile);
|
LoadSceneFile(lastSceneFile);
|
||||||
else
|
|
||||||
CreateAndOpenNewScene();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Trace("Opening new scene...");
|
||||||
CreateAndOpenNewScene();
|
CreateAndOpenNewScene();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Trace("Opening new scene...");
|
||||||
|
CreateAndOpenNewScene();
|
||||||
|
}
|
||||||
|
|
||||||
UpdateWindowTitle();
|
UpdateWindowTitle();
|
||||||
|
|
||||||
@@ -784,6 +794,8 @@ namespace GlitchyEditor
|
|||||||
EditorApp.Instance.Settings.Save();
|
EditorApp.Instance.Settings.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.EngineLogger.Trace("Project opened.");
|
||||||
|
|
||||||
return .Ok;
|
return .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -808,28 +820,35 @@ namespace GlitchyEditor
|
|||||||
append ScriptInstanceSerializer _scriptSerializer = .();
|
append ScriptInstanceSerializer _scriptSerializer = .();
|
||||||
|
|
||||||
/// Starts the play mode for the current scene
|
/// Starts the play mode for the current scene
|
||||||
private void OnScenePlay()
|
private void OnScenePlay(SceneState newSceneState)
|
||||||
{
|
{
|
||||||
Debug.Profiler.ProfileFunction!();
|
Debug.Profiler.ProfileFunction!();
|
||||||
|
|
||||||
|
Runtime.Assert(newSceneState == .Play || newSceneState == .Simulate, "newSceneState must either be Play or Simulate");
|
||||||
|
|
||||||
Log.EngineLogger.AssertDebug(_scriptSerializer.SerializedObjectCount == 0, "Somehow some entities are serialized.");
|
Log.EngineLogger.AssertDebug(_scriptSerializer.SerializedObjectCount == 0, "Somehow some entities are serialized.");
|
||||||
|
|
||||||
if (EditorApp.Instance.Settings.EditorSettings.ClearLogOnPlay)
|
if (EditorApp.Instance.Settings.EditorSettings.ClearLogOnPlay)
|
||||||
Editor.Instance.LogWindow.ClearLog();
|
Editor.Instance.LogWindow.ClearLog();
|
||||||
|
|
||||||
|
/// Save the in memory state of the script intances so se can recreate it after stopping play/simulation
|
||||||
_scriptSerializer.SerializeScriptInstances();
|
_scriptSerializer.SerializeScriptInstances();
|
||||||
|
|
||||||
_editor.SceneViewportWindow.EditorMode = false;
|
_editor.SceneViewportWindow.EditorMode = false;
|
||||||
_sceneState = .Play;
|
_sceneState = newSceneState;
|
||||||
|
|
||||||
using (Scene runtimeScene = new Scene())
|
using (Scene runtimeScene = new Scene())
|
||||||
{
|
{
|
||||||
_editorScene.CopyTo(runtimeScene, true);
|
// In simulation mode we don't want any scripts to run, just physics.
|
||||||
|
_editorScene.CopyTo(runtimeScene, _sceneState == .Play);
|
||||||
|
|
||||||
SetActiveScene(runtimeScene, startRuntime: true, startSimulation: true, newPlayMode: .Play);
|
SetActiveScene(runtimeScene, startRuntime: (_sceneState == .Play), startSimulation: true, _sceneState);
|
||||||
|
|
||||||
|
if (_sceneState == .Play)
|
||||||
|
{
|
||||||
_scriptSerializer.DeserializeScriptInstances();
|
_scriptSerializer.DeserializeScriptInstances();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_editor.CurrentScene = _activeScene;
|
_editor.CurrentScene = _activeScene;
|
||||||
|
|
||||||
@@ -837,35 +856,26 @@ namespace GlitchyEditor
|
|||||||
SwitchToPlayWindow();
|
SwitchToPlayWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PlayMode
|
|
||||||
{
|
|
||||||
Play,
|
|
||||||
Editor,
|
|
||||||
Simulation
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Activates the given scene.
|
/// Activates the given scene.
|
||||||
/// @param scene The scene to be activated.
|
/// @param scene The scene to be activated.
|
||||||
/// @param startRuntime If set to true, the script runtime will be initialized for the given scene.
|
/// @param startRuntime If set to true, the script runtime will be initialized for the given scene.
|
||||||
/// @param startSimulation If set to true, the physics simulation will be initialized for the given scene.
|
/// @param startSimulation If set to true, the physics simulation will be initialized for the given scene.
|
||||||
private void SetActiveScene(Scene scene, bool startRuntime, bool startSimulation, PlayMode? newPlayMode = null)
|
/// @param newSceneState The new scene state.
|
||||||
|
private void SetActiveScene(Scene scene, bool startRuntime, bool startSimulation, SceneState newSceneState)
|
||||||
{
|
{
|
||||||
_activeScene?.Stop();
|
_activeScene?.Stop();
|
||||||
|
|
||||||
if (newPlayMode != null)
|
switch (newSceneState)
|
||||||
{
|
|
||||||
switch (newPlayMode.Value)
|
|
||||||
{
|
{
|
||||||
case .Play:
|
case .Play:
|
||||||
ScriptEngine.ApplicationInfo.IsInPlayMode = true;
|
ScriptEngine.ApplicationInfo.IsInPlayMode = true;
|
||||||
case .Editor:
|
case .Edit:
|
||||||
ScriptEngine.ApplicationInfo.IsInEditMode = true;
|
ScriptEngine.ApplicationInfo.IsInEditMode = true;
|
||||||
case .Simulation:
|
case .Simulate:
|
||||||
// No scripts in Simulation
|
// No scripts in Simulation
|
||||||
ScriptEngine.ApplicationInfo.IsInEditMode = false;
|
ScriptEngine.ApplicationInfo.IsInEditMode = false;
|
||||||
ScriptEngine.ApplicationInfo.IsInPlayMode = false;
|
ScriptEngine.ApplicationInfo.IsInPlayMode = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (scene != null)
|
if (scene != null)
|
||||||
{
|
{
|
||||||
@@ -875,25 +885,6 @@ namespace GlitchyEditor
|
|||||||
SetReference!(_activeScene, scene);
|
SetReference!(_activeScene, scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts the physics simulation mode for the current scene
|
|
||||||
private void OnSceneSimulate()
|
|
||||||
{
|
|
||||||
_editor.SceneViewportWindow.EditorMode = false;
|
|
||||||
_sceneState = .Simulate;
|
|
||||||
|
|
||||||
using (Scene simulationScene = new Scene())
|
|
||||||
{
|
|
||||||
_editorScene.CopyTo(simulationScene, false);
|
|
||||||
|
|
||||||
SetActiveScene(simulationScene, startRuntime: false, startSimulation: true, newPlayMode: .Simulation);
|
|
||||||
}
|
|
||||||
|
|
||||||
_editor.CurrentScene = _activeScene;
|
|
||||||
|
|
||||||
if (EditorApp.Instance.Settings.EditorSettings.SwitchToPlayerOnSimulate)
|
|
||||||
SwitchToPlayWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pauses the scene
|
/// Pauses the scene
|
||||||
private void OnScenePause()
|
private void OnScenePause()
|
||||||
{
|
{
|
||||||
@@ -924,13 +915,13 @@ namespace GlitchyEditor
|
|||||||
if (_activeScene == null)
|
if (_activeScene == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SetActiveScene(_editorScene, startRuntime: true, startSimulation: false, newPlayMode: .Editor);
|
Log.EngineLogger.Trace("Stopping current scene and returing to edit mode...");
|
||||||
|
|
||||||
// TODO: _editorScene.DeserializeScripts(scriptData);
|
|
||||||
|
|
||||||
_editor.SceneViewportWindow.EditorMode = true;
|
_editor.SceneViewportWindow.EditorMode = true;
|
||||||
_sceneState = .Edit;
|
_sceneState = .Edit;
|
||||||
|
|
||||||
|
SetActiveScene(_editorScene, startRuntime: true, startSimulation: false, _sceneState);
|
||||||
|
|
||||||
_editor.CurrentScene = _activeScene;
|
_editor.CurrentScene = _activeScene;
|
||||||
|
|
||||||
_isPaused = false;
|
_isPaused = false;
|
||||||
@@ -943,14 +934,19 @@ namespace GlitchyEditor
|
|||||||
* into "Editor"-mode (because Game-Mode works on a copy of the scene).
|
* into "Editor"-mode (because Game-Mode works on a copy of the scene).
|
||||||
*/
|
*/
|
||||||
GameViewportSizeChanged(null, _editor.GameViewportWindow.RenderedViewportSize);
|
GameViewportSizeChanged(null, _editor.GameViewportWindow.RenderedViewportSize);
|
||||||
|
|
||||||
|
if (_scriptSerializer.SerializedObjectCount != 0)
|
||||||
|
{
|
||||||
|
// Reconstruct state before play
|
||||||
|
_scriptSerializer.DeserializeScriptInstances();
|
||||||
|
_scriptSerializer.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EditorApp.Instance.Settings.EditorSettings.SwitchToEditorOnStop)
|
if (EditorApp.Instance.Settings.EditorSettings.SwitchToEditorOnStop)
|
||||||
SwitchToEditorWindow();
|
SwitchToEditorWindow();
|
||||||
|
|
||||||
// Reconstruct state before play
|
Log.EngineLogger.Trace("Switched to edit mode.");
|
||||||
_scriptSerializer.DeserializeScriptInstances();
|
|
||||||
_scriptSerializer.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops the scene and cleans up the subsystems to allow loading another scene.
|
/// Stops the scene and cleans up the subsystems to allow loading another scene.
|
||||||
@@ -983,6 +979,8 @@ namespace GlitchyEditor
|
|||||||
/// Sets the current editor scene
|
/// Sets the current editor scene
|
||||||
private void SetEditorScene(Scene scene, ScriptInstanceSerializer serializedObjects = null)
|
private void SetEditorScene(Scene scene, ScriptInstanceSerializer serializedObjects = null)
|
||||||
{
|
{
|
||||||
|
Log.EngineLogger.Trace($"Switching to scene \"{(scene?.Name ?? "<none>")}\"...");
|
||||||
|
|
||||||
_editorScene?.Stop();
|
_editorScene?.Stop();
|
||||||
|
|
||||||
SetReference!(_editorScene, scene);
|
SetReference!(_editorScene, scene);
|
||||||
@@ -1002,6 +1000,7 @@ namespace GlitchyEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
UpdateWindowTitle();
|
UpdateWindowTitle();
|
||||||
|
Log.EngineLogger.Trace($"Switched scene to \"{(scene?.Name ?? "<none>")}\".");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new default scene.
|
/// Creates a new default scene.
|
||||||
@@ -1239,7 +1238,7 @@ namespace GlitchyEditor
|
|||||||
ImGui.PushID(0);
|
ImGui.PushID(0);
|
||||||
|
|
||||||
if (ImGui.ImageButton("", _editorIcons.Play, .(size, size), .Zero, .Ones))
|
if (ImGui.ImageButton("", _editorIcons.Play, .(size, size), .Zero, .Ones))
|
||||||
OnScenePlay();
|
OnScenePlay(.Play);
|
||||||
|
|
||||||
ImGui.PopID();
|
ImGui.PopID();
|
||||||
|
|
||||||
@@ -1250,7 +1249,7 @@ namespace GlitchyEditor
|
|||||||
ImGui.PushID(1);
|
ImGui.PushID(1);
|
||||||
|
|
||||||
if (ImGui.ImageButton("", _editorIcons.Simulate, .(size, size), .Zero, .Ones))
|
if (ImGui.ImageButton("", _editorIcons.Simulate, .(size, size), .Zero, .Ones))
|
||||||
OnSceneSimulate();
|
OnScenePlay(.Simulate);
|
||||||
|
|
||||||
ImGui.PopID();
|
ImGui.PopID();
|
||||||
|
|
||||||
@@ -1522,7 +1521,7 @@ namespace GlitchyEditor
|
|||||||
if(ImGui.BeginMenu("Tools", true))
|
if(ImGui.BeginMenu("Tools", true))
|
||||||
{
|
{
|
||||||
if(ImGui.MenuItem("Reload Scripts"))
|
if(ImGui.MenuItem("Reload Scripts"))
|
||||||
ScriptEngine.ReloadAssemblies();
|
ScriptEngine.ReloadAssemblies(retainScriptData: true);
|
||||||
|
|
||||||
#if GE_EDITOR_IMGUI_DEMO
|
#if GE_EDITOR_IMGUI_DEMO
|
||||||
ImGui.Checkbox("Show ImGui Demo", &_showImguiDemoWindow);
|
ImGui.Checkbox("Show ImGui Demo", &_showImguiDemoWindow);
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ public class EditorLogger : Logger
|
|||||||
}
|
}
|
||||||
else if (args.Count > 0 && messageOrigin != null)
|
else if (args.Count > 0 && messageOrigin != null)
|
||||||
{
|
{
|
||||||
Editor.Instance.LogWindow.Log(timestamp, level, message, new .() {IsEngineMessage = IsEngineLogger, MessageOrigin = new MessageOrigin(messageOrigin.FileName, messageOrigin.LineNumber)});
|
Editor.Instance.LogWindow.Log(timestamp, level, message, new .() {Entity = (messageOrigin.EntityId != .Zero ? messageOrigin.EntityId : null), IsEngineMessage = IsEngineLogger, MessageOrigin = new MessageOrigin(messageOrigin.EntityId, messageOrigin.FileName, messageOrigin.CallerMemberName, messageOrigin.LineNumber, messageOrigin.ColumnNumber)});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
+135
-35
@@ -36,7 +36,10 @@ class Project
|
|||||||
[AllowAppend]
|
[AllowAppend]
|
||||||
private this(StringView workspacePath)
|
private this(StringView workspacePath)
|
||||||
{
|
{
|
||||||
_workspacePath = new String(workspacePath);
|
_workspacePath = new String(workspacePath.Length);
|
||||||
|
Path.GetFullPath(workspacePath, _workspacePath);
|
||||||
|
if (_workspacePath.EndsWith(Path.DirectorySeparatorChar))
|
||||||
|
_workspacePath.RemoveFromEnd(1);
|
||||||
|
|
||||||
_assetsFolder = new String();
|
_assetsFolder = new String();
|
||||||
PathInProject(_assetsFolder, "Assets");
|
PathInProject(_assetsFolder, "Assets");
|
||||||
@@ -80,6 +83,27 @@ class Project
|
|||||||
Path.ToActualPath(outTarget);
|
Path.ToActualPath(outTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void GetScriptCoreGeneratorProjectFilePath(String outTarget)
|
||||||
|
{
|
||||||
|
Directory.GetCurrentDirectory(outTarget);
|
||||||
|
Path.Combine(outTarget, "../ScriptCoreGenerator/ScriptCoreGenerator.csproj");
|
||||||
|
Path.ToActualPath(outTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GetScriptCoreGeneratorTestProjectFilePath(String outTarget)
|
||||||
|
{
|
||||||
|
Directory.GetCurrentDirectory(outTarget);
|
||||||
|
Path.Combine(outTarget, "../ScriptCoreGenerator.Test/ScriptCoreGenerator.Test.csproj");
|
||||||
|
Path.ToActualPath(outTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GetImGuiDotNetProjectFilePath(String outTarget)
|
||||||
|
{
|
||||||
|
Directory.GetCurrentDirectory(outTarget);
|
||||||
|
Path.Combine(outTarget, "../vendor/ImGui.NET/src/ImGui.NET/ImGui.NET.csproj");
|
||||||
|
Path.ToActualPath(outTarget);
|
||||||
|
}
|
||||||
|
|
||||||
/// Loads or creates the user specific settings for this project.
|
/// Loads or creates the user specific settings for this project.
|
||||||
private void InitUserSettings()
|
private void InitUserSettings()
|
||||||
{
|
{
|
||||||
@@ -274,7 +298,8 @@ class Project
|
|||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If necessary adds or removes the reference to ScriptCore.csproj from the solution (.slnx) file.
|
/// If missing adds the reference to ScriptCore and other script dependencies from the solution (.slnx) file.
|
||||||
|
/// @param referenceScriptCoreProject If true, ScriptCore and other dependencies will be referenced using their .csproj-files. Otherwise the ScriptCore-dll will be referenced.
|
||||||
private Result<void> FixupScriptSolutionFile(bool referenceScriptCoreProject)
|
private Result<void> FixupScriptSolutionFile(bool referenceScriptCoreProject)
|
||||||
{
|
{
|
||||||
String solutionPath = scope .();
|
String solutionPath = scope .();
|
||||||
@@ -293,38 +318,27 @@ class Project
|
|||||||
|
|
||||||
solution.LoadFromString(solutionFileContent);
|
solution.LoadFromString(solutionFileContent);
|
||||||
|
|
||||||
XmlNode scriptCoreProjectReference = null;
|
bool changedContent = false;
|
||||||
for (XmlNode projectNode in solution.DocumentElement.EnumerateNodes("Project", recursive: true))
|
|
||||||
{
|
|
||||||
if (projectNode.TryGetAttribute("Path", let includeAttribute))
|
|
||||||
{
|
|
||||||
if (includeAttribute.Value.Contains("ScriptCore.csproj", true))
|
|
||||||
{
|
|
||||||
scriptCoreProjectReference = projectNode;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (referenceScriptCoreProject)
|
String scriptCoreFilePath = scope .();
|
||||||
{
|
GetScriptCoreProjectFilePath(scriptCoreFilePath);
|
||||||
Log.EngineLogger.Info("Debug mode enabled. Referencing ScriptCore.csproj in Script-Solution.");
|
changedContent |= Try!(AddProjectToSolution(solution, scriptCoreFilePath, referenceScriptCoreProject));
|
||||||
|
|
||||||
if (scriptCoreProjectReference == null)
|
String scriptCoreGeneratorFilePath = scope .();
|
||||||
{
|
GetScriptCoreGeneratorProjectFilePath(scriptCoreGeneratorFilePath);
|
||||||
Log.EngineLogger.Warning("No reference to ScriptCore.csproj found. Adding...");
|
changedContent |= Try!(AddProjectToSolution(solution, scriptCoreGeneratorFilePath, referenceScriptCoreProject));
|
||||||
scriptCoreProjectReference = solution.DocumentElement.AddChild("Project");
|
|
||||||
}
|
|
||||||
|
|
||||||
String projectFilePath = scope .();
|
String scriptCoreGeneratorTestFilePath = scope .();
|
||||||
GetScriptCoreProjectFilePath(projectFilePath);
|
GetScriptCoreGeneratorTestProjectFilePath(scriptCoreGeneratorTestFilePath);
|
||||||
scriptCoreProjectReference["Path"] = projectFilePath;
|
changedContent |= Try!(AddProjectToSolution(solution, scriptCoreGeneratorTestFilePath, referenceScriptCoreProject));
|
||||||
}
|
|
||||||
else if (scriptCoreProjectReference != null)
|
String imGuiDotNetFilePath = scope .();
|
||||||
|
GetImGuiDotNetProjectFilePath(imGuiDotNetFilePath);
|
||||||
|
changedContent |= Try!(AddProjectToSolution(solution, imGuiDotNetFilePath, referenceScriptCoreProject));
|
||||||
|
|
||||||
|
if (changedContent)
|
||||||
{
|
{
|
||||||
Log.EngineLogger.Warning("Reference to ScriptCore.csproj found. Removing...");
|
Log.EngineLogger.Info("Script solution file updated, saving...");
|
||||||
scriptCoreProjectReference.RemoveFromParent(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
String modifiedSolution = new String(solutionFileContent.Length * 2);
|
String modifiedSolution = new String(solutionFileContent.Length * 2);
|
||||||
defer delete modifiedSolution;
|
defer delete modifiedSolution;
|
||||||
@@ -335,10 +349,63 @@ class Project
|
|||||||
Log.EngineLogger.Error($"Failed to modify {solutionPath}");
|
Log.EngineLogger.Error($"Failed to modify {solutionPath}");
|
||||||
return .Err;
|
return .Err;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Info("Script solution file already up to date.");
|
||||||
|
}
|
||||||
|
|
||||||
return .Ok;
|
return .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Result<bool> AddProjectToSolution(Xml solution, StringView projectFilePath, bool referenceScriptCoreProject)
|
||||||
|
{
|
||||||
|
String projectName = scope .();
|
||||||
|
Path.GetFileName(projectFilePath, projectName);
|
||||||
|
|
||||||
|
XmlNode projectReference = null;
|
||||||
|
for (XmlNode projectNode in solution.DocumentElement.EnumerateNodes("Project", recursive: true))
|
||||||
|
{
|
||||||
|
if (projectNode.TryGetAttribute("Path", let includeAttribute))
|
||||||
|
{
|
||||||
|
if (includeAttribute.Value.Contains(projectName, true))
|
||||||
|
{
|
||||||
|
projectReference = projectNode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool changedContent = false;
|
||||||
|
|
||||||
|
if (referenceScriptCoreProject)
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Info($"Debug mode enabled. Referencing {projectName} in Script-Solution.");
|
||||||
|
|
||||||
|
if (projectReference == null)
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Warning($"No reference to {projectName} found. Adding...");
|
||||||
|
projectReference = solution.DocumentElement.AddChild("Project");
|
||||||
|
|
||||||
|
changedContent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectReference["Path"] != projectFilePath)
|
||||||
|
{
|
||||||
|
projectReference["Path"] = projectFilePath;
|
||||||
|
changedContent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (projectReference != null)
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Warning($"Reference to {projectName} found. Removing...");
|
||||||
|
projectReference.RemoveFromParent(true);
|
||||||
|
changedContent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changedContent;
|
||||||
|
}
|
||||||
|
|
||||||
/// If necessary adds or removes the reference to ScriptCore.dll from the project (.csproj) file.
|
/// If necessary adds or removes the reference to ScriptCore.dll from the project (.csproj) file.
|
||||||
private Result<void> FixupScriptProjectFile(bool referenceScriptCoreProject)
|
private Result<void> FixupScriptProjectFile(bool referenceScriptCoreProject)
|
||||||
{
|
{
|
||||||
@@ -372,9 +439,20 @@ class Project
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool changedContent = false;
|
||||||
|
|
||||||
// Try to find a reference to the dll
|
// Try to find a reference to the dll
|
||||||
XmlNode dllReferenceNode = project.DocumentElement.Find("Reference", "Include", "ScriptCore", recursive: true);
|
XmlNode dllReferenceNode = project.DocumentElement.Find("Reference", "Include", "ScriptCore", recursive: true);
|
||||||
|
|
||||||
|
mixin SetIfDifferent(XmlNode node, StringView attributeName, StringView wantedValue)
|
||||||
|
{
|
||||||
|
if (node.GetAttribute(attributeName)?.Value != wantedValue)
|
||||||
|
{
|
||||||
|
node.SetAttribute(attributeName, wantedValue);
|
||||||
|
changedContent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (referenceScriptCoreProject)
|
if (referenceScriptCoreProject)
|
||||||
{
|
{
|
||||||
Log.EngineLogger.Info("Debug mode enabled. Referencing ScriptCore.csproj in Script-Project.");
|
Log.EngineLogger.Info("Debug mode enabled. Referencing ScriptCore.csproj in Script-Project.");
|
||||||
@@ -383,6 +461,7 @@ class Project
|
|||||||
{
|
{
|
||||||
Log.EngineLogger.Warning("Reference to ScriptCore.dll found. Removing...");
|
Log.EngineLogger.Warning("Reference to ScriptCore.dll found. Removing...");
|
||||||
dllReferenceNode.RemoveFromParent(true);
|
dllReferenceNode.RemoveFromParent(true);
|
||||||
|
changedContent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scriptCoreProjectReference == null)
|
if (scriptCoreProjectReference == null)
|
||||||
@@ -392,16 +471,21 @@ class Project
|
|||||||
XmlNode itemGroup = project.DocumentElement.Find("ItemGroup", recursive: true);
|
XmlNode itemGroup = project.DocumentElement.Find("ItemGroup", recursive: true);
|
||||||
|
|
||||||
scriptCoreProjectReference = itemGroup.AddChild("ProjectReference");
|
scriptCoreProjectReference = itemGroup.AddChild("ProjectReference");
|
||||||
|
changedContent = true;
|
||||||
|
}
|
||||||
|
|
||||||
String scriptCoreProjectPath = scope .();
|
String scriptCoreProjectPath = scope .();
|
||||||
Directory.GetCurrentDirectory(scriptCoreProjectPath);
|
Directory.GetCurrentDirectory(scriptCoreProjectPath);
|
||||||
Path.Combine(scriptCoreProjectPath, "../ScriptCore/ScriptCore.csproj");
|
Path.Combine(scriptCoreProjectPath, "../ScriptCore/ScriptCore.csproj");
|
||||||
Path.ToActualPath(scriptCoreProjectPath);
|
Path.ToActualPath(scriptCoreProjectPath);
|
||||||
|
|
||||||
|
if (!Path.Equals(scriptCoreProjectReference.GetAttribute("Include")?.Value, scriptCoreProjectPath))
|
||||||
|
{
|
||||||
scriptCoreProjectReference.SetAttribute("Include", scriptCoreProjectPath);
|
scriptCoreProjectReference.SetAttribute("Include", scriptCoreProjectPath);
|
||||||
scriptCoreProjectReference.SetAttribute("OutputItemType", "Analyzer");
|
changedContent = true;
|
||||||
scriptCoreProjectReference.SetAttribute("ReferenceOutputAssembly", "true");
|
|
||||||
}
|
}
|
||||||
|
SetIfDifferent!(scriptCoreProjectReference, "OutputItemType", "Analyzer");
|
||||||
|
SetIfDifferent!(scriptCoreProjectReference, "ReferenceOutputAssembly", "true");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -409,6 +493,7 @@ class Project
|
|||||||
{
|
{
|
||||||
Log.EngineLogger.Warning("Reference to ScriptCore.csproj found. Removing...");
|
Log.EngineLogger.Warning("Reference to ScriptCore.csproj found. Removing...");
|
||||||
scriptCoreProjectReference.RemoveFromParent(true);
|
scriptCoreProjectReference.RemoveFromParent(true);
|
||||||
|
changedContent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dllReferenceNode == null)
|
if (dllReferenceNode == null)
|
||||||
@@ -418,16 +503,18 @@ class Project
|
|||||||
XmlNode itemGroup = project.DocumentElement.Find("ItemGroup", recursive: true);
|
XmlNode itemGroup = project.DocumentElement.Find("ItemGroup", recursive: true);
|
||||||
|
|
||||||
dllReferenceNode = itemGroup.AddChild("Reference");
|
dllReferenceNode = itemGroup.AddChild("Reference");
|
||||||
|
changedContent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
dllReferenceNode.SetAttribute("Include", "ScriptCore");
|
SetIfDifferent!(dllReferenceNode, "Include", "ScriptCore");
|
||||||
dllReferenceNode.SetAttribute("OutputItemType", "Analyzer");
|
SetIfDifferent!(dllReferenceNode, "OutputItemType", "Analyzer");
|
||||||
dllReferenceNode.SetAttribute("ReferenceOutputAssembly", "true");
|
SetIfDifferent!(dllReferenceNode, "ReferenceOutputAssembly", "true");
|
||||||
|
|
||||||
XmlNode hintPathNode = dllReferenceNode.Find("HintPath");
|
XmlNode hintPathNode = dllReferenceNode.Find("HintPath");
|
||||||
if (hintPathNode == null)
|
if (hintPathNode == null)
|
||||||
{
|
{
|
||||||
hintPathNode = dllReferenceNode.AddChild("HintPath");
|
hintPathNode = dllReferenceNode.AddChild("HintPath");
|
||||||
|
changedContent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String pathToScriptCoreDll = scope .();
|
String pathToScriptCoreDll = scope .();
|
||||||
@@ -441,8 +528,16 @@ class Project
|
|||||||
return .Err;
|
return .Err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Path.Equals(hintPathNode.Text, pathToScriptCoreDll))
|
||||||
|
{
|
||||||
hintPathNode.SetText(pathToScriptCoreDll);
|
hintPathNode.SetText(pathToScriptCoreDll);
|
||||||
|
changedContent = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedContent)
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Info("Script project file updated, saving...");
|
||||||
|
|
||||||
String modfiedProject = new String(projectFileContent.Length * 2);
|
String modfiedProject = new String(projectFileContent.Length * 2);
|
||||||
defer delete modfiedProject;
|
defer delete modfiedProject;
|
||||||
@@ -452,6 +547,11 @@ class Project
|
|||||||
{
|
{
|
||||||
Log.EngineLogger.Error($"Failed to modify {csprojPath}");
|
Log.EngineLogger.Error($"Failed to modify {csprojPath}");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Info("Script project file already up to date.");
|
||||||
|
}
|
||||||
|
|
||||||
return .Ok;
|
return .Ok;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -375,17 +375,25 @@ class EditorSettings
|
|||||||
if (_recentProjects == null)
|
if (_recentProjects == null)
|
||||||
_recentProjects = new List<String>();
|
_recentProjects = new List<String>();
|
||||||
|
|
||||||
|
String absolutePath = new String(value.Length);
|
||||||
|
Path.GetFullPath(value, absolutePath);
|
||||||
|
if (absolutePath.EndsWith(Path.DirectorySeparatorChar))
|
||||||
|
absolutePath.RemoveFromEnd(1);
|
||||||
|
|
||||||
// Remove duplicates
|
// Remove duplicates
|
||||||
for (var entry in _recentProjects)
|
for (var entry in _recentProjects)
|
||||||
{
|
{
|
||||||
if (entry == value)
|
if (entry.EndsWith(Path.DirectorySeparatorChar))
|
||||||
|
entry.RemoveFromEnd(1);
|
||||||
|
|
||||||
|
if (Path.Equals(entry, absolutePath))
|
||||||
{
|
{
|
||||||
delete entry;
|
delete entry;
|
||||||
@entry.Remove();
|
@entry.Remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_recentProjects.Insert(0, new String(value));
|
_recentProjects.Insert(0, absolutePath);
|
||||||
|
|
||||||
if (_recentProjects.Count > 10)
|
if (_recentProjects.Count > 10)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
namespace System.Collections;
|
||||||
|
|
||||||
|
extension List<T>
|
||||||
|
{
|
||||||
|
public bool Equals(List<T> other)
|
||||||
|
{
|
||||||
|
if (this === other)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (this.Count != other.Count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i < this.Count)
|
||||||
|
{
|
||||||
|
if (this[i] != other[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,7 +29,9 @@ static class CoreClrHelper
|
|||||||
|
|
||||||
//static function void(char8* path) LoadScriptAssembly;
|
//static function void(char8* path) LoadScriptAssembly;
|
||||||
static LoadScriptAssemblyFunc _loadScriptAssembly;
|
static LoadScriptAssemblyFunc _loadScriptAssembly;
|
||||||
static function void() _unloadAssemblies;
|
static function void() _unloadScriptAssembly;
|
||||||
|
|
||||||
|
static function void() _waitUntilOldAssemblyDead;
|
||||||
|
|
||||||
// public static unsafe void GetScriptClasses(void** outBuffer, long* length)
|
// public static unsafe void GetScriptClasses(void** outBuffer, long* length)
|
||||||
private function void GetScriptClassesFunc(void** outBuffer, int64* length);
|
private function void GetScriptClassesFunc(void** outBuffer, int64* length);
|
||||||
@@ -108,7 +110,9 @@ static class CoreClrHelper
|
|||||||
{
|
{
|
||||||
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "LoadScriptAssembly", out _loadScriptAssembly);
|
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "LoadScriptAssembly", out _loadScriptAssembly);
|
||||||
|
|
||||||
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "UnloadAssemblies", out _unloadAssemblies);
|
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "UnloadScriptAssembly", out _unloadScriptAssembly);
|
||||||
|
|
||||||
|
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "WaitUntilOldAssemblyDead", out _waitUntilOldAssemblyDead);
|
||||||
|
|
||||||
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "GetScriptClasses", out _getScriptClasses);
|
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "GetScriptClasses", out _getScriptClasses);
|
||||||
|
|
||||||
@@ -130,14 +134,19 @@ static class CoreClrHelper
|
|||||||
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "InvokeEntityOnCollisionEnter2D", out _entityScriptFunctions.OnCollisionEnter2D);
|
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "InvokeEntityOnCollisionEnter2D", out _entityScriptFunctions.OnCollisionEnter2D);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LoadAppAssembly(Span<uint8> appAssemblyData, Span<uint8> pdbData)
|
public static void LoadScriptAssembly(Span<uint8> appAssemblyData, Span<uint8> pdbData)
|
||||||
{
|
{
|
||||||
_loadScriptAssembly(appAssemblyData.Ptr, appAssemblyData.Length, pdbData.Ptr, pdbData.Length);
|
_loadScriptAssembly(appAssemblyData.Ptr, appAssemblyData.Length, pdbData.Ptr, pdbData.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UnloadAssemblies()
|
public static void UnloadScriptAssembly()
|
||||||
{
|
{
|
||||||
_unloadAssemblies();
|
_unloadScriptAssembly();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WaitUntilOldAssemblyDead()
|
||||||
|
{
|
||||||
|
_waitUntilOldAssemblyDead();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets an array of script class infos. Use FreeScriptClassNames to release the buffer.
|
/// Gets an array of script class infos. Use FreeScriptClassNames to release the buffer.
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class EntitySerializerWrapper : NewScriptClass
|
|||||||
function void DeserializeMethod(UUID entityId, void* serializedObjPtr, void* serializerPtr);
|
function void DeserializeMethod(UUID entityId, void* serializedObjPtr, void* serializerPtr);
|
||||||
function void SerializeStaticMethod(char8* fullTypeName, void* serializedObjPtr, void* serializerPtr);
|
function void SerializeStaticMethod(char8* fullTypeName, void* serializedObjPtr, void* serializerPtr);
|
||||||
function void DeserializeStaticMethod(char8* fullTypeName, void* serializedObjPtr, void* serializerPtr);
|
function void DeserializeStaticMethod(char8* fullTypeName, void* serializedObjPtr, void* serializerPtr);
|
||||||
|
function void FinishSerializationMethod(void* serializerPtr);
|
||||||
|
|
||||||
private CreateSerializationContextMethod _createSerializationContextMethod;
|
private CreateSerializationContextMethod _createSerializationContextMethod;
|
||||||
//private ClearSerializationContextMethod _clearSerializationContextMethod;
|
//private ClearSerializationContextMethod _clearSerializationContextMethod;
|
||||||
@@ -25,16 +26,19 @@ class EntitySerializerWrapper : NewScriptClass
|
|||||||
private DeserializeMethod _deserializeMethod;
|
private DeserializeMethod _deserializeMethod;
|
||||||
private SerializeStaticMethod _serializeStaticMethod;
|
private SerializeStaticMethod _serializeStaticMethod;
|
||||||
private DeserializeStaticMethod _deserializeStaticMethod;
|
private DeserializeStaticMethod _deserializeStaticMethod;
|
||||||
|
private FinishSerializationMethod _finishSerializationMethod;
|
||||||
|
|
||||||
[AllowAppend]
|
[AllowAppend]
|
||||||
public this() : base("GlitchyEngine.Serialization.EntitySerializer", .Empty, .None, false)
|
public this() : base("GlitchyEngine.Serialization.EntitySerializer", .Empty, .None, false)
|
||||||
{
|
{
|
||||||
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "CreateSerializationContext", out _createSerializationContextMethod);
|
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "CreateSerializationContext", out _createSerializationContextMethod);
|
||||||
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "DestroySerializationContext", out _destroySerializationContextMethod);
|
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "DestroySerializationContext", out _destroySerializationContextMethod);
|
||||||
|
|
||||||
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "EntitySerializer_Serialize", out _serializeMethod);
|
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "EntitySerializer_Serialize", out _serializeMethod);
|
||||||
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "EntitySerializer_Deserialize", out _deserializeMethod);
|
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "EntitySerializer_Deserialize", out _deserializeMethod);
|
||||||
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "EntitySerializer_SerializeStaticFields", out _serializeStaticMethod);
|
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "EntitySerializer_SerializeStaticFields", out _serializeStaticMethod);
|
||||||
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "EntitySerializer_DeserializeStaticFields", out _deserializeStaticMethod);
|
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "EntitySerializer_DeserializeStaticFields", out _deserializeStaticMethod);
|
||||||
|
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "EntitySerializer_FinishSerialization", out _finishSerializationMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CreateSerializationContext(ScriptInstanceSerializer serializer)
|
public void CreateSerializationContext(ScriptInstanceSerializer serializer)
|
||||||
@@ -93,4 +97,10 @@ class EntitySerializerWrapper : NewScriptClass
|
|||||||
|
|
||||||
_deserializeStaticMethod(@class.FullName.CStr(), serializedObjectPtr, serializerPtr);
|
_deserializeStaticMethod(@class.FullName.CStr(), serializedObjectPtr, serializerPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void FinishSerialization(ScriptInstanceSerializer serializer)
|
||||||
|
{
|
||||||
|
void* serializerPtr = Internal.UnsafeCastToPtr(serializer);
|
||||||
|
_finishSerializationMethod(serializerPtr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ class EngineClasses
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Init after FileSystemWatcher so that ScriptEngine's destructor runs before FSW's. The proper fix might be to not delete _userAssemblyWatcher in the static destructor...
|
||||||
|
[StaticInitAfter(typeof(FileSystemWatcher))]
|
||||||
static class ScriptEngine
|
static class ScriptEngine
|
||||||
{
|
{
|
||||||
// TODO: Maybe make it possible to set the path to ScriptCore just like for the script project?
|
// TODO: Maybe make it possible to set the path to ScriptCore just like for the script project?
|
||||||
@@ -190,11 +192,11 @@ static class ScriptEngine
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the path to the current app assembly.
|
/// Changes the path to the current app assembly.
|
||||||
public static void SetAppAssemblyPath(StringView appAssemblyPath)
|
public static void SetAppAssemblyPath(StringView appAssemblyPath, bool retainScriptData)
|
||||||
{
|
{
|
||||||
_appAssemblyPath.Set(appAssemblyPath);
|
_appAssemblyPath.Set(appAssemblyPath);
|
||||||
|
|
||||||
ReloadAssemblies();
|
ReloadAssemblies(retainScriptData);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void InitRuntime()
|
static void InitRuntime()
|
||||||
@@ -207,32 +209,6 @@ static class ScriptEngine
|
|||||||
CoreClrHelper.Init(ScriptCorePath);
|
CoreClrHelper.Init(ScriptCorePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static void InitMono()
|
|
||||||
{
|
|
||||||
Mono.mono_set_assemblies_path("mono/lib/4.5");
|
|
||||||
|
|
||||||
if (_debuggingEnabled)
|
|
||||||
{
|
|
||||||
char8*[2] options = .(
|
|
||||||
"--debugger-agent=transport=dt_socket,address=127.0.0.1:2550,server=y,suspend=n,loglevel=3,logfile=MonoDebugger.log",
|
|
||||||
"--soft-breakpoints"
|
|
||||||
);
|
|
||||||
|
|
||||||
Mono.mono_jit_parse_options(options.Count, &options);
|
|
||||||
Mono.mono_debug_init(.Mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
s_RootDomain = Mono.mono_jit_init("GlitchyEngineJITRuntime");
|
|
||||||
Log.EngineLogger.Assert(s_RootDomain != null, "Failed to initialize mono root domain");
|
|
||||||
|
|
||||||
if (_debuggingEnabled)
|
|
||||||
{
|
|
||||||
Mono.mono_debug_domain_create(s_RootDomain);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mono.mono_thread_set_main(Mono.mono_thread_current());
|
|
||||||
}*/
|
|
||||||
|
|
||||||
static bool _requestingReload = false;
|
static bool _requestingReload = false;
|
||||||
|
|
||||||
static void InitAssemblyWatcher()
|
static void InitAssemblyWatcher()
|
||||||
@@ -263,7 +239,7 @@ static class ScriptEngine
|
|||||||
|
|
||||||
Application.Instance.InvokeOnMainThread(new () =>
|
Application.Instance.InvokeOnMainThread(new () =>
|
||||||
{
|
{
|
||||||
ReloadAssemblies();
|
ReloadAssemblies(retainScriptData: true);
|
||||||
|
|
||||||
_requestingReload = false;
|
_requestingReload = false;
|
||||||
_userAssemblyWatcher.StartRaisingEvents();
|
_userAssemblyWatcher.StartRaisingEvents();
|
||||||
@@ -299,23 +275,27 @@ static class ScriptEngine
|
|||||||
|
|
||||||
File.ReadAll(pdbPath, pdbData);
|
File.ReadAll(pdbPath, pdbData);
|
||||||
|
|
||||||
CoreClrHelper.LoadAppAssembly(data, pdbData);
|
CoreClrHelper.LoadScriptAssembly(data, pdbData);
|
||||||
|
|
||||||
GetEntitiesFromAssemblies();
|
GetEntitiesFromAssemblies();
|
||||||
|
|
||||||
//ScriptGlue.RegisterManagedComponents();
|
//ScriptGlue.RegisterManagedComponents();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Error("Failed to load script assembly, because it doesn't exist.");
|
||||||
|
}
|
||||||
|
|
||||||
InitAssemblyWatcher();
|
InitAssemblyWatcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts the script runtime and sets the context scene.
|
/// Starts the script runtime and sets the context scene.
|
||||||
public static void StartRuntime(Scene context)
|
public static void StartRuntime(Scene context, bool retainScriptData = true)
|
||||||
{
|
{
|
||||||
Debug.Assert(s_Context == null, "StartRuntime was called twice without StopRuntime in between!");
|
Debug.Assert(s_Context == null, "StartRuntime was called twice without StopRuntime in between!");
|
||||||
Context = context;
|
Context = context;
|
||||||
|
|
||||||
ReloadAssemblies();
|
ReloadAssemblies(retainScriptData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stopts the script runtime and disposes of all script instances.
|
/// Stopts the script runtime and disposes of all script instances.
|
||||||
@@ -435,18 +415,23 @@ static class ScriptEngine
|
|||||||
CoreClrHelper.FreeScriptClassNames();
|
CoreClrHelper.FreeScriptClassNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ReloadAssemblies()
|
public static void ReloadAssemblies(bool retainScriptData = true)
|
||||||
{
|
{
|
||||||
Debug.Profiler.ProfileFunction!();
|
Debug.Profiler.ProfileFunction!();
|
||||||
|
|
||||||
|
// TODO: Assembly reload might actually take a second. We should consider doing it in a separate thread in the editor.
|
||||||
|
|
||||||
Log.EngineLogger.Info("Reloading script assemblies.");
|
Log.EngineLogger.Info("Reloading script assemblies.");
|
||||||
|
|
||||||
|
ScriptInstanceSerializer contextSerializer = null;
|
||||||
|
|
||||||
ScriptInstanceSerializer contextSerializer = scope .();
|
if (retainScriptData)
|
||||||
|
{
|
||||||
|
contextSerializer = scope:: .();
|
||||||
contextSerializer.SerializeScriptInstances();
|
contextSerializer.SerializeScriptInstances();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Unload script assemblies, remove class handles, fire unload events?
|
UnloadScriptAssemblies();
|
||||||
|
|
||||||
LoadScriptAssemblies();
|
LoadScriptAssemblies();
|
||||||
|
|
||||||
@@ -468,20 +453,36 @@ static class ScriptEngine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (retainScriptData)
|
||||||
|
{
|
||||||
contextSerializer.DeserializeScriptInstances();
|
contextSerializer.DeserializeScriptInstances();
|
||||||
|
}
|
||||||
|
|
||||||
Log.EngineLogger.Info("Script assemblies reloaded!");
|
Log.EngineLogger.Info("Script assemblies reloaded!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void UnloadScriptAssemblies()
|
||||||
|
{
|
||||||
|
for (let (id, scriptInstance) in _entityScriptInstances)
|
||||||
|
{
|
||||||
|
if (Context.GetEntityByID(id) case .Ok(let entity))
|
||||||
|
{
|
||||||
|
ScriptComponent* script = entity.GetComponent<ScriptComponent>();
|
||||||
|
DestroyInstance(entity, script);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.EngineLogger.AssertDebug(false, "Entity script instance exists but no entity?!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreClrHelper.UnloadScriptAssembly();
|
||||||
|
CoreClrHelper.WaitUntilOldAssemblyDead();
|
||||||
|
}
|
||||||
|
|
||||||
public static void Shutdown()
|
public static void Shutdown()
|
||||||
{
|
{
|
||||||
/*Mono.mono_domain_set(s_RootDomain, false);
|
UnloadScriptAssemblies();
|
||||||
|
|
||||||
Mono.mono_domain_unload(s_AppDomain);
|
|
||||||
s_AppDomain = null;
|
|
||||||
|
|
||||||
Mono.mono_jit_cleanup(s_RootDomain);
|
|
||||||
s_RootDomain = null;*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the script class with the given name, or null, if no such class exists.
|
/// Returns the script class with the given name, or null, if no such class exists.
|
||||||
@@ -492,19 +493,17 @@ static class ScriptEngine
|
|||||||
return scriptClass;
|
return scriptClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void LogScriptException(ScriptException exception, UUID entityId)
|
internal static void LogScriptException(ScriptException exception)
|
||||||
{
|
{
|
||||||
String entityInfo = scope .();
|
String entityInfo = scope .();
|
||||||
|
|
||||||
if (entityId != .Zero && Context != null)
|
if (exception.EntityId != .Zero && Context != null)
|
||||||
{
|
{
|
||||||
exception.EntityId = entityId;
|
Result<Entity> sourceEntity = Context.GetEntityByID(exception.EntityId);
|
||||||
|
|
||||||
Result<Entity> sourceEntity = Context.GetEntityByID(entityId);
|
|
||||||
|
|
||||||
if (sourceEntity case .Ok(let e))
|
if (sourceEntity case .Ok(let e))
|
||||||
{
|
{
|
||||||
entityInfo.AppendF($" ({e.Name} | {entityId})");
|
entityInfo.AppendF($" ({e.Name} | {exception.EntityId})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +1,107 @@
|
|||||||
using System;
|
using System;
|
||||||
using GlitchyEngine.Core;
|
using GlitchyEngine.Core;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
namespace GlitchyEngine.Scripting;
|
namespace GlitchyEngine.Scripting;
|
||||||
|
|
||||||
|
|
||||||
public class ScriptException
|
public class ScriptException
|
||||||
{
|
{
|
||||||
private String _fullName ~ delete:append _;
|
private String _fullName ~ delete:append _;
|
||||||
|
|
||||||
private String _message ~ delete:append _;
|
private String _message ~ delete:append _;
|
||||||
|
|
||||||
private String _stackTrace ~ delete:append _;
|
private StackTrace _stackTrace ~ delete _;
|
||||||
/// The clean stack trace only contains the Managed Stack (the full trace contains one line for the native-to-managed entry)
|
|
||||||
private StringView _cleanStackTrace;
|
|
||||||
|
|
||||||
//TODO
|
|
||||||
//private ScriptException _innerException ~ _?.ReleaseRef();
|
|
||||||
|
|
||||||
public StringView FullName => _fullName;
|
public StringView FullName => _fullName;
|
||||||
public StringView Message => _message;
|
public StringView Message => _message;
|
||||||
|
|
||||||
public StringView StackTrace => _stackTrace;
|
public StackTrace StackTrace => _stackTrace;
|
||||||
public StringView CleanStackTrace => _cleanStackTrace;
|
|
||||||
|
|
||||||
//public ScriptException InnerException => _innerException;
|
public UUID EntityId { get; private set; }
|
||||||
|
|
||||||
public UUID EntityId { get; set; }
|
|
||||||
|
|
||||||
[AllowAppend]
|
[AllowAppend]
|
||||||
public this(UUID entityId, StringView fullExceptionClassName, StringView message, StringView stackTrace)
|
public this(UUID entityId, StringView fullExceptionClassName, StringView message, StackTrace ownStackTrace)
|
||||||
{
|
{
|
||||||
String allocFullExceptionClassName = append String(fullExceptionClassName);
|
String allocFullExceptionClassName = append String(fullExceptionClassName);
|
||||||
String allocMessage = append String(message);
|
String allocMessage = append String(message);
|
||||||
String allocStackTrace = append String(stackTrace);
|
|
||||||
|
EntityId = entityId;
|
||||||
|
|
||||||
|
_stackTrace = ownStackTrace;
|
||||||
|
|
||||||
_fullName = allocFullExceptionClassName;
|
_fullName = allocFullExceptionClassName;
|
||||||
_message = allocMessage;
|
_message = allocMessage;
|
||||||
_stackTrace = allocStackTrace;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO
|
public class StackFrameInfo
|
||||||
_cleanStackTrace = _stackTrace;
|
{
|
||||||
|
private String _fileName ~ delete:append _;
|
||||||
|
private String _methodSignature ~ delete:append _;
|
||||||
|
public int Line;
|
||||||
|
public int Column;
|
||||||
|
|
||||||
|
public StringView FileName => _fileName;
|
||||||
|
|
||||||
|
public StringView MethodSignature => _methodSignature;
|
||||||
|
|
||||||
|
[AllowAppend]
|
||||||
|
public this(StringView fileName, StringView methodSignature, int line, int column)
|
||||||
|
{
|
||||||
|
String allocFileName = append String(fileName);
|
||||||
|
String allocMethodSignature = append String(methodSignature);
|
||||||
|
|
||||||
|
_fileName = allocFileName;
|
||||||
|
_methodSignature = allocMethodSignature;
|
||||||
|
|
||||||
|
Line = line;
|
||||||
|
Column = column;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ToString(String outBuffer)
|
||||||
|
{
|
||||||
|
outBuffer.Append("at ");
|
||||||
|
|
||||||
|
if (MethodSignature.IsEmpty)
|
||||||
|
outBuffer.Append("<unknown>");
|
||||||
|
else
|
||||||
|
outBuffer.Append(MethodSignature);
|
||||||
|
|
||||||
|
outBuffer.Append(" in ");
|
||||||
|
|
||||||
|
if (!_fileName.IsEmpty)
|
||||||
|
outBuffer.AppendF($"{FileName}, line {Line}:{Column}");
|
||||||
|
else
|
||||||
|
outBuffer.Append("<unknown>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StackTrace
|
||||||
|
{
|
||||||
|
private List<StackFrameInfo> _stackFrameInfos ~ DeleteContainerAndItems!(_);
|
||||||
|
|
||||||
|
public Span<StackFrameInfo> Frames => _stackFrameInfos;
|
||||||
|
|
||||||
|
public this(List<StackFrameInfo> ownStackFrameInfos)
|
||||||
|
{
|
||||||
|
_stackFrameInfos = ownStackFrameInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Commutable]
|
||||||
|
public static bool operator ==(StackTrace a, StackTrace b)
|
||||||
|
{
|
||||||
|
if (a == null || b == null)
|
||||||
|
return a === b;
|
||||||
|
|
||||||
|
return a._stackFrameInfos.Equals(b._stackFrameInfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ToString(String outBuffer)
|
||||||
|
{
|
||||||
|
for (let frame in _stackFrameInfos)
|
||||||
|
{
|
||||||
|
frame.ToString(outBuffer);
|
||||||
|
outBuffer.Append('\n');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,19 +28,29 @@ using internal GlitchyEngine.Content;
|
|||||||
// TODO: Move to logger?
|
// TODO: Move to logger?
|
||||||
class MessageOrigin
|
class MessageOrigin
|
||||||
{
|
{
|
||||||
|
private UUID _entityId;
|
||||||
private String _fileName ~ delete:append _;
|
private String _fileName ~ delete:append _;
|
||||||
|
private String _callerMemberName ~ delete:append _;
|
||||||
private int _lineNumber;
|
private int _lineNumber;
|
||||||
|
private int _columnNumber;
|
||||||
|
|
||||||
|
public UUID EntityId => _entityId;
|
||||||
public StringView FileName => _fileName;
|
public StringView FileName => _fileName;
|
||||||
|
public StringView CallerMemberName => _callerMemberName;
|
||||||
public int LineNumber => _lineNumber;
|
public int LineNumber => _lineNumber;
|
||||||
|
public int ColumnNumber => _columnNumber;
|
||||||
|
|
||||||
[AllowAppend]
|
[AllowAppend]
|
||||||
public this(StringView fileName, int lineNumber)
|
public this(UUID entityId, StringView fileName, StringView callerMemberName, int lineNumber, int columnNumber)
|
||||||
{
|
{
|
||||||
String file = append String(fileName);
|
String file = append String(fileName);
|
||||||
|
String caller = append String(callerMemberName);
|
||||||
|
|
||||||
|
_entityId = entityId;
|
||||||
_fileName = file;
|
_fileName = file;
|
||||||
|
_callerMemberName = caller;
|
||||||
_lineNumber = lineNumber;
|
_lineNumber = lineNumber;
|
||||||
|
_columnNumber = columnNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +86,7 @@ struct GlueParamAttribute : Attribute
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Collects all methods in ScriptGlue that have a RegisterCallAttribute. Generates a function pointer for them as well as outputting a json file that can be used to generate the script library side of the ScriptGlue construct.
|
||||||
[AttributeUsage(.Struct)]
|
[AttributeUsage(.Struct)]
|
||||||
struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
||||||
{
|
{
|
||||||
@@ -185,11 +196,13 @@ struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A struct containing all pointers to functions that can be called from the script library.
|
||||||
[EngineFunctionsGenerator]
|
[EngineFunctionsGenerator]
|
||||||
struct EngineFunctions
|
struct EngineFunctions
|
||||||
{
|
{
|
||||||
static Self _functions;
|
static Self _functions;
|
||||||
|
|
||||||
|
/// Sends the references to all ScriptGlue functions to the script library.
|
||||||
[RegisterMethod]
|
[RegisterMethod]
|
||||||
public static void FillEngineFunctions()
|
public static void FillEngineFunctions()
|
||||||
{
|
{
|
||||||
@@ -200,7 +213,7 @@ struct EngineFunctions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adding this attribute to a method will log method entry and returned Result<T> errors */
|
/// Collects all methods in ScriptGlue that have a RegisterCallAttribute. Generates the assignment of the corresponding function pointer in EngineFunctions.
|
||||||
[AttributeUsage(.Method | .Constructor)]
|
[AttributeUsage(.Method | .Constructor)]
|
||||||
public struct RegisterMethodAttribute : Attribute, IOnMethodInit
|
public struct RegisterMethodAttribute : Attribute, IOnMethodInit
|
||||||
{
|
{
|
||||||
@@ -252,6 +265,7 @@ static class ScriptGlue
|
|||||||
|
|
||||||
private static String _lastExceptionMessage = new .() ~ delete _;
|
private static String _lastExceptionMessage = new .() ~ delete _;
|
||||||
|
|
||||||
|
/// Initializes the glue layer between native engine and script library.
|
||||||
public static void Init()
|
public static void Init()
|
||||||
{
|
{
|
||||||
if (_setEngineFunctions == null)
|
if (_setEngineFunctions == null)
|
||||||
@@ -264,6 +278,13 @@ static class ScriptGlue
|
|||||||
RegisterManagedComponents();
|
RegisterManagedComponents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Collects and sends the function pointers to all glue functions to the script library.
|
||||||
|
private static void RegisterCalls()
|
||||||
|
{
|
||||||
|
EngineFunctions.FillEngineFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the function pointers to create, remove and check for components and sends them to the script library.
|
||||||
public static void RegisterManagedComponents()
|
public static void RegisterManagedComponents()
|
||||||
{
|
{
|
||||||
Debug.Profiler.ProfileFunction!();
|
Debug.Profiler.ProfileFunction!();
|
||||||
@@ -278,11 +299,7 @@ static class ScriptGlue
|
|||||||
RegisterComponent<MeshRendererComponent>();
|
RegisterComponent<MeshRendererComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RegisterCalls()
|
/// Register the function pointers to create, remove and check for components of the specified type and sends them to the script library.
|
||||||
{
|
|
||||||
EngineFunctions.FillEngineFunctions();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RegisterComponent<T>() where T : struct, new
|
private static void RegisterComponent<T>() where T : struct, new
|
||||||
{
|
{
|
||||||
String fullComponentTypeName = scope String();
|
String fullComponentTypeName = scope String();
|
||||||
@@ -445,7 +462,7 @@ static class ScriptGlue
|
|||||||
#region Log
|
#region Log
|
||||||
|
|
||||||
[RegisterCall, CallingConvention(.Cdecl)]
|
[RegisterCall, CallingConvention(.Cdecl)]
|
||||||
static void Log_LogMessage(LogLevel logLevel, StringView messagePtr, StringView fileNamePtr, int lineNumber)
|
static void Log_LogMessage(LogLevel logLevel, UUID entityId, StringView messagePtr, StringView fileNamePtr, StringView callerMemberNamePtr, int lineNumber, int columnNumber)
|
||||||
{
|
{
|
||||||
String escapedMessage = new:ScopedAlloc! String(messagePtr);
|
String escapedMessage = new:ScopedAlloc! String(messagePtr);
|
||||||
|
|
||||||
@@ -455,9 +472,7 @@ static class ScriptGlue
|
|||||||
|
|
||||||
if (!fileNamePtr.IsEmpty)
|
if (!fileNamePtr.IsEmpty)
|
||||||
{
|
{
|
||||||
String fileName = new:ScopedAlloc! String(fileNamePtr);
|
MessageOrigin messageOrigin = scope MessageOrigin(entityId, fileNamePtr, callerMemberNamePtr, lineNumber, columnNumber);
|
||||||
|
|
||||||
MessageOrigin messageOrigin = scope MessageOrigin(fileName, lineNumber);
|
|
||||||
|
|
||||||
Log.ClientLogger.Log((LogLevel)logLevel, escapedMessage, messageOrigin);
|
Log.ClientLogger.Log((LogLevel)logLevel, escapedMessage, messageOrigin);
|
||||||
}
|
}
|
||||||
@@ -467,11 +482,27 @@ static class ScriptGlue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[RegisterCall, CallingConvention(.Cdecl)]
|
[Ordered]
|
||||||
static void Log_LogException(UUID entityId, StringView fullExceptionClassName, StringView exceptionMessage, StringView stackTrace)
|
public struct GlueStackFrameInfo
|
||||||
{
|
{
|
||||||
ScriptException exception = new ScriptException(entityId, fullExceptionClassName, exceptionMessage, stackTrace);
|
public StringView FileName;
|
||||||
ScriptEngine.LogScriptException(exception, entityId);
|
public StringView MethodSignature;
|
||||||
|
public int32 Line;
|
||||||
|
public int32 Column;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RegisterCall, CallingConvention(.Cdecl)]
|
||||||
|
static void Log_LogException(UUID entityId, StringView fullExceptionClassName, StringView exceptionMessage, GlueStackFrameInfo* stackFrameInfos, int32 stackFrameCount)
|
||||||
|
{
|
||||||
|
List<StackFrameInfo> frames = new .(stackFrameCount);
|
||||||
|
|
||||||
|
for (GlueStackFrameInfo frameInfo in Span<GlueStackFrameInfo>(stackFrameInfos, stackFrameCount))
|
||||||
|
{
|
||||||
|
frames.Add(new StackFrameInfo(frameInfo.FileName, frameInfo.MethodSignature, frameInfo.Line, frameInfo.Column));
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptException exception = new ScriptException(entityId, fullExceptionClassName, exceptionMessage, new StackTrace(frames));
|
||||||
|
ScriptEngine.LogScriptException(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -642,7 +673,7 @@ static class ScriptGlue
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region TransformComponen
|
#region TransformComponent
|
||||||
|
|
||||||
[RegisterCall, CallingConvention(.Cdecl)]
|
[RegisterCall, CallingConvention(.Cdecl)]
|
||||||
static EngineResult Transform_GetParent(UUID entityId, out UUID parentId)
|
static EngineResult Transform_GetParent(UUID entityId, out UUID parentId)
|
||||||
@@ -966,7 +997,7 @@ static class ScriptGlue
|
|||||||
|
|
||||||
#endregion Rigidbody2D
|
#endregion Rigidbody2D
|
||||||
|
|
||||||
#region Camer
|
#region Camera
|
||||||
|
|
||||||
[RegisterCall, CallingConvention(.Cdecl)]
|
[RegisterCall, CallingConvention(.Cdecl)]
|
||||||
static void Camera_GetProjectionType(UUID entityId, out SceneCamera.ProjectionType projectionType)
|
static void Camera_GetProjectionType(UUID entityId, out SceneCamera.ProjectionType projectionType)
|
||||||
@@ -1534,6 +1565,7 @@ static class ScriptGlue
|
|||||||
asset
|
asset
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
/// Set an exception message that the script library can collect when a glue function returned an error.
|
||||||
static void SetExceptionMessage(StringView message)
|
static void SetExceptionMessage(StringView message)
|
||||||
{
|
{
|
||||||
_lastExceptionMessage.Set(message);
|
_lastExceptionMessage.Set(message);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using GlitchyEngine.Core;
|
using GlitchyEngine.Core;
|
||||||
using GlitchyEngine.Serialization;
|
using GlitchyEngine.Serialization;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace GlitchyEngine.Scripting;
|
namespace GlitchyEngine.Scripting;
|
||||||
|
|
||||||
@@ -31,11 +32,22 @@ public class ScriptInstanceSerializer
|
|||||||
ScriptEngine.Classes.EntitySerializer.CreateSerializationContext(this);
|
ScriptEngine.Classes.EntitySerializer.CreateSerializationContext(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Signals, that no more objects will be serialized into this context.
|
||||||
|
/// This allows the managed serialization context to do cleanup:
|
||||||
|
/// i. e. for C# we can release references to types which allows us to cleanup the old script assembly.
|
||||||
|
public void FinishSerialization()
|
||||||
|
{
|
||||||
|
ScriptEngine.Classes.EntitySerializer.FinishSerialization(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Serializes all script instances that are currently managed by the ScriptEngine.
|
/// Serializes all script instances that are currently managed by the ScriptEngine.
|
||||||
public void SerializeScriptInstances()
|
public void SerializeScriptInstances()
|
||||||
{
|
{
|
||||||
Debug.Profiler.ProfileFunction!();
|
Debug.Profiler.ProfileFunction!();
|
||||||
|
|
||||||
|
Log.EngineLogger.Trace($"Serializing {Internal.UnsafeCastToPtr(this)}...");
|
||||||
|
|
||||||
Init();
|
Init();
|
||||||
|
|
||||||
for (let (id, scriptInstance) in ScriptEngine._entityScriptInstances)
|
for (let (id, scriptInstance) in ScriptEngine._entityScriptInstances)
|
||||||
@@ -47,6 +59,8 @@ public class ScriptInstanceSerializer
|
|||||||
{
|
{
|
||||||
SerializeStaticScriptClassFields(scriptClass);
|
SerializeStaticScriptClassFields(scriptClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FinishSerialization();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes the given script instance.
|
/// Serializes the given script instance.
|
||||||
@@ -68,6 +82,8 @@ public class ScriptInstanceSerializer
|
|||||||
{
|
{
|
||||||
Debug.Profiler.ProfileFunction!();
|
Debug.Profiler.ProfileFunction!();
|
||||||
|
|
||||||
|
Log.EngineLogger.Trace($"Deserializing {Internal.UnsafeCastToPtr(this)}...");
|
||||||
|
|
||||||
Init();
|
Init();
|
||||||
|
|
||||||
for (let (id, script) in ScriptEngine._entityScriptInstances)
|
for (let (id, script) in ScriptEngine._entityScriptInstances)
|
||||||
@@ -79,6 +95,8 @@ public class ScriptInstanceSerializer
|
|||||||
{
|
{
|
||||||
DeserializeStaticScriptClassFields(scriptClass);
|
DeserializeStaticScriptClassFields(scriptClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FinishSerialization();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserializes the data into the given script instance, if there is data available.
|
/// Deserializes the data into the given script instance, if there is data available.
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ class SerializedObject
|
|||||||
Serialize.Value(writer, ValueView(typeof(StringView), &dataView), environment);
|
Serialize.Value(writer, ValueView(typeof(StringView), &dataView), environment);
|
||||||
case .EngineObjectReference:
|
case .EngineObjectReference:
|
||||||
writer.Type("EngineObject");
|
writer.Type("EngineObject");
|
||||||
if (field.Data.EngineObject.FullTypeName.IsEmpty)
|
if (!((StringView)field.Data.ListEngineObject.FullTypeName).IsEmpty)
|
||||||
writer.Type((StringView)field.Data.ListEngineObject.FullTypeName);
|
writer.Type((StringView)field.Data.ListEngineObject.FullTypeName);
|
||||||
Serialize.Value(writer, field.Data.EngineObject.ID, environment);
|
Serialize.Value(writer, field.Data.EngineObject.ID, environment);
|
||||||
case .ObjectReference:
|
case .ObjectReference:
|
||||||
|
|||||||
@@ -293,7 +293,8 @@ namespace GlitchyEngine.World
|
|||||||
|
|
||||||
private void StartRuntime()
|
private void StartRuntime()
|
||||||
{
|
{
|
||||||
ScriptEngine.StartRuntime(this);
|
// We don't retain data of script instances, because our callers do that if necessary
|
||||||
|
ScriptEngine.StartRuntime(this, retainScriptData: false);
|
||||||
|
|
||||||
// Initialize ScriptComponents
|
// Initialize ScriptComponents
|
||||||
for (let (handle, scriptComponent) in _ecsWorld.Enumerate<ScriptComponent>())
|
for (let (handle, scriptComponent) in _ecsWorld.Enumerate<ScriptComponent>())
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
using System;
|
using GlitchyEngine.Core;
|
||||||
|
using GlitchyEngine.Extensions;
|
||||||
|
using GlitchyEngine.Graphics;
|
||||||
|
using GlitchyEngine.Math;
|
||||||
|
using GlitchyEngine.Math.Attributes;
|
||||||
|
using ImGuiNET;
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
@@ -10,12 +17,7 @@ using System.Reflection;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using GlitchyEngine.Core;
|
using System.Text.Unicode;
|
||||||
using GlitchyEngine.Extensions;
|
|
||||||
using GlitchyEngine.Graphics;
|
|
||||||
using GlitchyEngine.Math;
|
|
||||||
using GlitchyEngine.Math.Attributes;
|
|
||||||
using ImGuiNET;
|
|
||||||
using Component = GlitchyEngine.Core.Component;
|
using Component = GlitchyEngine.Core.Component;
|
||||||
|
|
||||||
namespace GlitchyEngine.Editor;
|
namespace GlitchyEngine.Editor;
|
||||||
@@ -282,10 +284,10 @@ internal class EntityEditor
|
|||||||
if (value is float || value is double || value is Half)
|
if (value is float || value is double || value is Half)
|
||||||
format = "0.#####";
|
format = "0.#####";
|
||||||
else
|
else
|
||||||
format = "0";
|
format = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!format.StartsWith("%"))
|
if (format?.StartsWith("%") == false)
|
||||||
{
|
{
|
||||||
if (value is float f)
|
if (value is float f)
|
||||||
{
|
{
|
||||||
@@ -303,14 +305,14 @@ internal class EntityEditor
|
|||||||
|
|
||||||
if (range?.Slider == true)
|
if (range?.Slider == true)
|
||||||
{
|
{
|
||||||
if (ImGui.SliderScalar(fieldId, dataType, (IntPtr)(&value), (IntPtr)(&min), (IntPtr)(&max), format))
|
if (ImGui.SliderScalar(fieldId, dataType, (IntPtr)(&value), (IntPtr)(&min), (IntPtr)(&max), format, flags: ImGuiSliderFlags.AlwaysClamp))
|
||||||
{
|
{
|
||||||
newValue = value;
|
newValue = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (ImGui.DragScalar(fieldId, dataType, (IntPtr)(&value), dragSpeed, (IntPtr)(&min), (IntPtr)(&max), format))
|
if (ImGui.DragScalar(fieldId, dataType, (IntPtr)(&value), dragSpeed, (IntPtr)(&min), (IntPtr)(&max), format, flags: ImGuiSliderFlags.AlwaysClamp))
|
||||||
{
|
{
|
||||||
newValue = value;
|
newValue = value;
|
||||||
}
|
}
|
||||||
@@ -327,9 +329,6 @@ internal class EntityEditor
|
|||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
// TODO: Allow escaped unicode chars \u XXXX
|
|
||||||
// TODO: Add text input validation, so that it isn't possible to type invalid chars
|
|
||||||
|
|
||||||
char* value = stackalloc char[4];
|
char* value = stackalloc char[4];
|
||||||
switch((char)reference!)
|
switch((char)reference!)
|
||||||
{
|
{
|
||||||
@@ -374,20 +373,143 @@ internal class EntityEditor
|
|||||||
// Place string delimiter after encoded UTF8 sequence.
|
// Place string delimiter after encoded UTF8 sequence.
|
||||||
buffer[encodedBytes] = (byte)'\0';
|
buffer[encodedBytes] = (byte)'\0';
|
||||||
|
|
||||||
if (ImGui.InputText(fieldId, (IntPtr)buffer, 8))
|
// Makes sure the data of a char text field is always valid or can be extended to be a valid char.
|
||||||
|
int CharTextFieldFilter(ImGuiInputTextCallbackData* data)
|
||||||
{
|
{
|
||||||
if (buffer[0] == '\\' && buffer[1] != '\0')
|
try
|
||||||
{
|
{
|
||||||
newValue = (char)buffer[1] switch
|
Span<byte> span = new Span<byte>(data->Buf, data->BufTextLen);
|
||||||
|
|
||||||
|
if (span.IsEmpty)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (span[0] == '\\')
|
||||||
{
|
{
|
||||||
'a' => newValue = '\a',
|
// It's just the backslash, so we don't need to do anything yet.
|
||||||
'b' => newValue = '\b',
|
if (span.Length == 1)
|
||||||
'f' => newValue = '\f',
|
return 0;
|
||||||
'n' => newValue = '\n',
|
|
||||||
'r' => newValue = '\r',
|
if (span[1] == 'a' || span[1] == 'b' || span[1] == 'f' || span[1] == 'n' ||
|
||||||
't' => newValue = '\t',
|
span[1] == 'r' || span[1] == 't' || span[1] == 'v' || span[1] == '\0')
|
||||||
'v' => newValue = '\v',
|
{
|
||||||
_ => newValue = '\\'
|
if (span.Length == 2)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Handle simply ascii escape sequence
|
||||||
|
if (span[2] != '\0')
|
||||||
|
{
|
||||||
|
span[2] = 0;
|
||||||
|
data->BufTextLen = 2;
|
||||||
|
data->BufDirty = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (span[1] == 'u')
|
||||||
|
{
|
||||||
|
// Handle unicode escape sequence \uXXXX
|
||||||
|
// Check if the next four characters are valid hex digits
|
||||||
|
for (int i = 2; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
char c = (char)span[i];
|
||||||
|
|
||||||
|
if (c == '\0')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Uri.IsHexDigit(c))
|
||||||
|
{
|
||||||
|
span[i] = 0;
|
||||||
|
data->BufTextLen = i;
|
||||||
|
data->BufDirty = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (span.Length > 6)
|
||||||
|
{
|
||||||
|
span[6] = 0;
|
||||||
|
data->BufTextLen = 6;
|
||||||
|
data->BufDirty = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (span[1] != '\0')
|
||||||
|
{
|
||||||
|
span[1] = 0;
|
||||||
|
data->BufTextLen = 1;
|
||||||
|
data->BufDirty = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var status = Rune.DecodeFromUtf8(span, out Rune rune, out int bytesConsumed);
|
||||||
|
|
||||||
|
// invalid UTF-8 oder Unicode-symbol needs two UTF-16 chars.
|
||||||
|
if (status != OperationStatus.Done || !rune.IsBmp)
|
||||||
|
{
|
||||||
|
span[0] = 0;
|
||||||
|
data->BufTextLen = 0;
|
||||||
|
data->BufDirty = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: if we want to support Rune as a type, we "just" need to disable this if-check
|
||||||
|
// Cut off everything after the first C# char (which is the first UTF-16 char of the Rune)
|
||||||
|
if (bytesConsumed < span.Length)
|
||||||
|
{
|
||||||
|
span[bytesConsumed] = 0;
|
||||||
|
data->BufTextLen = bytesConsumed;
|
||||||
|
data->BufDirty = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error($"Failed to process char field {fieldName}: {e.Message}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make space for the hex code of the char after the textbox
|
||||||
|
ImGui.SetNextItemWidth(ImGui.CalcItemWidth() / 2);
|
||||||
|
|
||||||
|
if (ImGui.InputText(fieldId, (IntPtr)buffer, 8, ImGuiInputTextFlags.CallbackEdit, CharTextFieldFilter))
|
||||||
|
{
|
||||||
|
var dataSpan = new ReadOnlySpan<byte>(buffer, 8);
|
||||||
|
|
||||||
|
if (dataSpan[0] == '\\' && dataSpan[1] == 'u')
|
||||||
|
{
|
||||||
|
// Decode unicode escape sequence \uXXXX
|
||||||
|
string hex = Encoding.UTF8.GetString(dataSpan[2..]);
|
||||||
|
if (int.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int codePoint))
|
||||||
|
{
|
||||||
|
newValue = (char)codePoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dataSpan[0] == '\\' && dataSpan[1] != '\0')
|
||||||
|
{
|
||||||
|
newValue = (char)dataSpan[1] switch
|
||||||
|
{
|
||||||
|
'a' => '\a',
|
||||||
|
'b' => '\b',
|
||||||
|
'f' => '\f',
|
||||||
|
'n' => '\n',
|
||||||
|
'r' => '\r',
|
||||||
|
't' => '\t',
|
||||||
|
'v' => '\v',
|
||||||
|
_ => '\\'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -398,6 +520,10 @@ internal class EntityEditor
|
|||||||
newValue = value[0];
|
newValue = value[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show the char as hex code
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted($"(U+{(ushort)(char)reference:X4})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (fieldType == typeof(byte))
|
else if (fieldType == typeof(byte))
|
||||||
|
|||||||
+10
-18
@@ -1,4 +1,5 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace GlitchyEngine;
|
namespace GlitchyEngine;
|
||||||
|
|
||||||
@@ -11,66 +12,57 @@ public static class Input
|
|||||||
/// Returns <see langword="true"/> if the specified key is in the pressed down state.
|
/// Returns <see langword="true"/> if the specified key is in the pressed down state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The key to query.</param>
|
/// <param name="key">The key to query.</param>
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
public static bool IsKeyPressed(Key key) => ScriptGlue.Input_IsKeyPressed(key);
|
||||||
public static extern bool IsKeyPressed(Key key);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns <see langword="true"/> if the specified <see cref="key"/> is in the released state.
|
/// Returns <see langword="true"/> if the specified <see cref="key"/> is in the released state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The key to query.</param>
|
/// <param name="key">The key to query.</param>
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
public static bool IsKeyReleased(Key key) => ScriptGlue.Input_IsKeyReleased(key);
|
||||||
public static extern bool IsKeyReleased(Key key);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns <see langword="true"/> if the state of the specified <see cref="key"/> changed this frame (i.e. changed from pressed to released or from released to pressed).
|
/// Returns <see langword="true"/> if the state of the specified <see cref="key"/> changed this frame (i.e. changed from pressed to released or from released to pressed).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The key to query.</param>
|
/// <param name="key">The key to query.</param>
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
public static bool IsKeyToggled(Key key) => ScriptGlue.Input_IsKeyToggled(key);
|
||||||
public static extern bool IsKeyToggled(Key key);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns <see langword="true"/> if <see cref="key"/> is being pressed down this frame
|
/// Returns <see langword="true"/> if <see cref="key"/> is being pressed down this frame
|
||||||
/// (<see cref="IsKeyPressed"/> was <see langword="false"/> last frame and is <see langword="true"/> this frame).
|
/// (<see cref="IsKeyPressed"/> was <see langword="false"/> last frame and is <see langword="true"/> this frame).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The key to query.</param>
|
/// <param name="key">The key to query.</param>
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
public static bool IsKeyPressing(Key key) => ScriptGlue.Input_IsKeyPressing(key);
|
||||||
public static extern bool IsKeyPressing(Key key);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns <see langword="true"/> if <see cref="key"/> is being released this frame
|
/// Returns <see langword="true"/> if <see cref="key"/> is being released this frame
|
||||||
/// (<see cref="IsKeyPressed"/> was <see langword="true"/> last frame and is <see langword="false"/> this frame).
|
/// (<see cref="IsKeyPressed"/> was <see langword="true"/> last frame and is <see langword="false"/> this frame).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The key to query.</param>
|
/// <param name="key">The key to query.</param>
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
public static bool IsKeyReleasing(Key key) => ScriptGlue.Input_IsKeyReleasing(key);
|
||||||
public static extern bool IsKeyReleasing(Key key);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns <see langword="true"/> if the specified mouse button is in the pressed down state.
|
/// Returns <see langword="true"/> if the specified mouse button is in the pressed down state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mouseButton">The button to query.</param>
|
/// <param name="mouseButton">The button to query.</param>
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
public static bool IsMouseButtonPressed(MouseButton mouseButton) => ScriptGlue.Input_IsMouseButtonPressed(mouseButton);
|
||||||
public static extern bool IsMouseButtonPressed(MouseButton mouseButton);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns <see langword="true"/> if the specified mouse button is in the released state.
|
/// Returns <see langword="true"/> if the specified mouse button is in the released state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mouseButton">The button to query.</param>
|
/// <param name="mouseButton">The button to query.</param>
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
public static bool IsMouseButtonReleased(MouseButton mouseButton) => ScriptGlue.Input_IsMouseButtonReleased(mouseButton);
|
||||||
public static extern bool IsMouseButtonReleased(MouseButton mouseButton);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns <see langword="true"/> if the mouse button is being pressed down this frame
|
/// Returns <see langword="true"/> if the mouse button is being pressed down this frame
|
||||||
/// (<see cref="IsMouseButtonPressed"/> was <see langword="false"/> last frame and is <see langword="true"/> this frame).
|
/// (<see cref="IsMouseButtonPressed"/> was <see langword="false"/> last frame and is <see langword="true"/> this frame).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mouseButton">The button to query.</param>
|
/// <param name="mouseButton">The button to query.</param>
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
public static bool IsMouseButtonPressing(MouseButton mouseButton) => ScriptGlue.Input_IsMouseButtonPressing(mouseButton);
|
||||||
public static extern bool IsMouseButtonPressing(MouseButton mouseButton);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns <see langword="true"/> if the mouse button is being released this frame
|
/// Returns <see langword="true"/> if the mouse button is being released this frame
|
||||||
/// (<see cref="IsMouseButtonPressed"/> was <see langword="true"/> last frame and is <see langword="false"/> this frame).
|
/// (<see cref="IsMouseButtonPressed"/> was <see langword="true"/> last frame and is <see langword="false"/> this frame).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mouseButton">The button to query.</param>
|
/// <param name="mouseButton">The button to query.</param>
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
public static bool IsMouseButtonReleasing(MouseButton mouseButton) => ScriptGlue.Input_IsMouseButtonReleasing(mouseButton);
|
||||||
public static extern bool IsMouseButtonReleasing(MouseButton mouseButton);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+86
-22
@@ -1,7 +1,11 @@
|
|||||||
|
using GlitchyEngine.Core;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using GlitchyEngine.Core;
|
using System.Runtime.InteropServices;
|
||||||
|
using static GlitchyEngine.Log;
|
||||||
|
|
||||||
namespace GlitchyEngine;
|
namespace GlitchyEngine;
|
||||||
|
|
||||||
@@ -31,9 +35,10 @@ public class Log
|
|||||||
/// <param name="message">The message to log.</param>
|
/// <param name="message">The message to log.</param>
|
||||||
/// <param name="callerFilePath">File path of the caller.</param>
|
/// <param name="callerFilePath">File path of the caller.</param>
|
||||||
/// <param name="callerLineNumber">Line number of the caller.</param>
|
/// <param name="callerLineNumber">Line number of the caller.</param>
|
||||||
public static void Trace(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0)
|
/// <param name="callerMemberName">Name of the calling member.</param>
|
||||||
|
public static void Trace(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "")
|
||||||
{
|
{
|
||||||
ScriptGlue.Log_LogMessage(LogLevel.Trace, message, callerFilePath, callerLineNumber);
|
ScriptGlue.Log_LogMessage(LogLevel.Trace, ScriptGlue.CurrentEntityId, message, callerFilePath, callerMemberName, callerLineNumber, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -42,9 +47,10 @@ public class Log
|
|||||||
/// <param name="message">The message to log.</param>
|
/// <param name="message">The message to log.</param>
|
||||||
/// <param name="callerFilePath">File path of the caller.</param>
|
/// <param name="callerFilePath">File path of the caller.</param>
|
||||||
/// <param name="callerLineNumber">Line number of the caller.</param>
|
/// <param name="callerLineNumber">Line number of the caller.</param>
|
||||||
public static void Info(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0)
|
/// <param name="callerMemberName">Name of the calling member.</param>
|
||||||
|
public static void Info(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "")
|
||||||
{
|
{
|
||||||
ScriptGlue.Log_LogMessage(LogLevel.Info, message, callerFilePath, callerLineNumber);
|
ScriptGlue.Log_LogMessage(LogLevel.Info, ScriptGlue.CurrentEntityId, message, callerFilePath, callerMemberName, callerLineNumber, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -53,9 +59,10 @@ public class Log
|
|||||||
/// <param name="message">The message to log.</param>
|
/// <param name="message">The message to log.</param>
|
||||||
/// <param name="callerFilePath">File path of the caller.</param>
|
/// <param name="callerFilePath">File path of the caller.</param>
|
||||||
/// <param name="callerLineNumber">Line number of the caller.</param>
|
/// <param name="callerLineNumber">Line number of the caller.</param>
|
||||||
public static void Warning(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0)
|
/// <param name="callerMemberName">Name of the calling member.</param>
|
||||||
|
public static void Warning(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "")
|
||||||
{
|
{
|
||||||
ScriptGlue.Log_LogMessage(LogLevel.Warning, message, callerFilePath, callerLineNumber);
|
ScriptGlue.Log_LogMessage(LogLevel.Warning, ScriptGlue.CurrentEntityId, message, callerFilePath, callerMemberName, callerLineNumber, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -64,9 +71,10 @@ public class Log
|
|||||||
/// <param name="message">The message to log.</param>
|
/// <param name="message">The message to log.</param>
|
||||||
/// <param name="callerFilePath">File path of the caller.</param>
|
/// <param name="callerFilePath">File path of the caller.</param>
|
||||||
/// <param name="callerLineNumber">Line number of the caller.</param>
|
/// <param name="callerLineNumber">Line number of the caller.</param>
|
||||||
public static void Error(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0)
|
/// <param name="callerMemberName">Name of the calling member.</param>
|
||||||
|
public static void Error(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "")
|
||||||
{
|
{
|
||||||
ScriptGlue.Log_LogMessage(LogLevel.Error, message, callerFilePath, callerLineNumber);
|
ScriptGlue.Log_LogMessage(LogLevel.Error, ScriptGlue.CurrentEntityId, message, callerFilePath, callerMemberName, callerLineNumber, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -75,9 +83,10 @@ public class Log
|
|||||||
/// <param name="message">The message to log.</param>
|
/// <param name="message">The message to log.</param>
|
||||||
/// <param name="callerFilePath">File path of the caller.</param>
|
/// <param name="callerFilePath">File path of the caller.</param>
|
||||||
/// <param name="callerLineNumber">Line number of the caller.</param>
|
/// <param name="callerLineNumber">Line number of the caller.</param>
|
||||||
public static void Critical(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0)
|
/// <param name="callerMemberName">Name of the calling member.</param>
|
||||||
|
public static void Critical(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "")
|
||||||
{
|
{
|
||||||
ScriptGlue.Log_LogMessage(LogLevel.Critical, message, callerFilePath, callerLineNumber);
|
ScriptGlue.Log_LogMessage(LogLevel.Critical, ScriptGlue.CurrentEntityId, message, callerFilePath, callerMemberName, callerLineNumber, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -86,9 +95,10 @@ public class Log
|
|||||||
/// <param name="obj">The object to serialize.</param>
|
/// <param name="obj">The object to serialize.</param>
|
||||||
/// <param name="callerFilePath">File path of the caller.</param>
|
/// <param name="callerFilePath">File path of the caller.</param>
|
||||||
/// <param name="callerLineNumber">Line number of the caller.</param>
|
/// <param name="callerLineNumber">Line number of the caller.</param>
|
||||||
public static void Trace(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0)
|
/// <param name="callerMemberName">Name of the calling member.</param>
|
||||||
|
public static void Trace(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "")
|
||||||
{
|
{
|
||||||
ScriptGlue.Log_LogMessage(LogLevel.Trace, obj.ToString(), callerFilePath, callerLineNumber);
|
ScriptGlue.Log_LogMessage(LogLevel.Trace, ScriptGlue.CurrentEntityId, obj.ToString(), callerFilePath, callerMemberName, callerLineNumber, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -97,9 +107,10 @@ public class Log
|
|||||||
/// <param name="obj">The object to serialize.</param>
|
/// <param name="obj">The object to serialize.</param>
|
||||||
/// <param name="callerFilePath">File path of the caller.</param>
|
/// <param name="callerFilePath">File path of the caller.</param>
|
||||||
/// <param name="callerLineNumber">Line number of the caller.</param>
|
/// <param name="callerLineNumber">Line number of the caller.</param>
|
||||||
public static void Info(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0)
|
/// <param name="callerMemberName">Name of the calling member.</param>
|
||||||
|
public static void Info(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "")
|
||||||
{
|
{
|
||||||
ScriptGlue.Log_LogMessage(LogLevel.Info, obj.ToString(), callerFilePath, callerLineNumber);
|
ScriptGlue.Log_LogMessage(LogLevel.Info, ScriptGlue.CurrentEntityId, obj.ToString(), callerFilePath, callerMemberName, callerLineNumber, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -108,9 +119,10 @@ public class Log
|
|||||||
/// <param name="obj">The object to serialize.</param>
|
/// <param name="obj">The object to serialize.</param>
|
||||||
/// <param name="callerFilePath">File path of the caller.</param>
|
/// <param name="callerFilePath">File path of the caller.</param>
|
||||||
/// <param name="callerLineNumber">Line number of the caller.</param>
|
/// <param name="callerLineNumber">Line number of the caller.</param>
|
||||||
public static void Warning(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0)
|
/// <param name="callerMemberName">Name of the calling member.</param>
|
||||||
|
public static void Warning(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "")
|
||||||
{
|
{
|
||||||
ScriptGlue.Log_LogMessage(LogLevel.Warning, obj.ToString(), callerFilePath, callerLineNumber);
|
ScriptGlue.Log_LogMessage(LogLevel.Warning, ScriptGlue.CurrentEntityId, obj.ToString(), callerFilePath, callerMemberName, callerLineNumber, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -119,9 +131,10 @@ public class Log
|
|||||||
/// <param name="obj">The object to serialize.</param>
|
/// <param name="obj">The object to serialize.</param>
|
||||||
/// <param name="callerFilePath">File path of the caller.</param>
|
/// <param name="callerFilePath">File path of the caller.</param>
|
||||||
/// <param name="callerLineNumber">Line number of the caller.</param>
|
/// <param name="callerLineNumber">Line number of the caller.</param>
|
||||||
public static void Error(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0)
|
/// <param name="callerMemberName">Name of the calling member.</param>
|
||||||
|
public static void Error(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "")
|
||||||
{
|
{
|
||||||
ScriptGlue.Log_LogMessage(LogLevel.Error, obj.ToString(), callerFilePath, callerLineNumber);
|
ScriptGlue.Log_LogMessage(LogLevel.Error, ScriptGlue.CurrentEntityId, obj.ToString(), callerFilePath, callerMemberName, callerLineNumber, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -130,9 +143,30 @@ public class Log
|
|||||||
/// <param name="obj">The object to serialize.</param>
|
/// <param name="obj">The object to serialize.</param>
|
||||||
/// <param name="callerFilePath">File path of the caller.</param>
|
/// <param name="callerFilePath">File path of the caller.</param>
|
||||||
/// <param name="callerLineNumber">Line number of the caller.</param>
|
/// <param name="callerLineNumber">Line number of the caller.</param>
|
||||||
public static void Critical(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0)
|
/// <param name="callerMemberName">Name of the calling member.</param>
|
||||||
|
public static void Critical(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "")
|
||||||
{
|
{
|
||||||
ScriptGlue.Log_LogMessage(LogLevel.Critical, obj.ToString(), callerFilePath, callerLineNumber);
|
ScriptGlue.Log_LogMessage(LogLevel.Critical, ScriptGlue.CurrentEntityId, obj.ToString(), callerFilePath, callerMemberName, callerLineNumber, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerDisplay("{ToString(),raw}")]
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 0)]
|
||||||
|
[EngineClass("GlitchyEngine.Scripting.ScriptGlue.GlueStackFrameInfo")]
|
||||||
|
internal struct StackFrameInfo
|
||||||
|
{
|
||||||
|
public Native.StringView FileName;
|
||||||
|
public Native.StringView MethodSignature;
|
||||||
|
public int LineNumber;
|
||||||
|
public int ColumnNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetFullSignature(MethodBase method)
|
||||||
|
{
|
||||||
|
var typeName = method.DeclaringType?.FullName ?? "<unknown>";
|
||||||
|
var parameters = string.Join(", ",
|
||||||
|
method.GetParameters().Select(p => $"{p.ParameterType.Name}"));
|
||||||
|
|
||||||
|
return $"{typeName}.{method.Name}({parameters})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -141,6 +175,36 @@ public class Log
|
|||||||
/// <param name="exception">The exception to log.</param>
|
/// <param name="exception">The exception to log.</param>
|
||||||
public static void Exception(Exception exception)
|
public static void Exception(Exception exception)
|
||||||
{
|
{
|
||||||
ScriptGlue.Log_LogException(UUID.Zero, exception.GetType().FullName, exception.Message, exception.StackTrace);
|
StackTrace st = new StackTrace(exception, true);
|
||||||
|
StackFrame[] frames = st.GetFrames();
|
||||||
|
|
||||||
|
StackFrameInfo[] stackFrameInfos = new StackFrameInfo[frames.Length];
|
||||||
|
|
||||||
|
// We only need StackFrameInfo for exception logging, so it's okay to manually handle the marshalling here.
|
||||||
|
foreach (StackFrame stackFrame in frames)
|
||||||
|
{
|
||||||
|
MethodBase? method = stackFrame.GetMethod();
|
||||||
|
string methodSignature = method != null ? GetFullSignature(method) : "<unknown>";
|
||||||
|
stackFrameInfos[frames.IndexOf(stackFrame)] = new StackFrameInfo
|
||||||
|
{
|
||||||
|
FileName = Native.StringView.FromManagedString(stackFrame.GetFileName()),
|
||||||
|
MethodSignature = Native.StringView.FromManagedString(methodSignature),
|
||||||
|
LineNumber = stackFrame.GetFileLineNumber(),
|
||||||
|
ColumnNumber = stackFrame.GetFileColumnNumber()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (StackFrameInfo* stackFrames = stackFrameInfos)
|
||||||
|
{
|
||||||
|
ScriptGlue.Log_LogException(ScriptGlue.CurrentEntityId, exception.GetType().FullName, exception.Message, stackFrames, stackFrameInfos.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (StackFrameInfo stackFrameInfo in stackFrameInfos)
|
||||||
|
{
|
||||||
|
Native.StringView.FreeNativeMemory(stackFrameInfo.FileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<BaseOutputPath></BaseOutputPath>
|
<BaseOutputPath></BaseOutputPath>
|
||||||
<RootNamespace>GlitchyEngine</RootNamespace>
|
<RootNamespace>GlitchyEngine</RootNamespace>
|
||||||
|
|||||||
+132
-6
@@ -8,12 +8,15 @@ using ImGuiNET;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.Metrics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Loader;
|
using System.Runtime.Loader;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace GlitchyEngine;
|
namespace GlitchyEngine;
|
||||||
|
|
||||||
@@ -111,25 +114,53 @@ internal static unsafe partial class ScriptGlue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Counter for the number of script contexts that have been created. This allows us to debug and track the number of script contexts that have been created and destroyed.
|
||||||
|
/// </summary>
|
||||||
|
private static int _contextCounter = 0;
|
||||||
|
|
||||||
private static void LoadAssembly(Stream assemblyStream, Stream? pdbStream)
|
private static void LoadAssembly(Stream assemblyStream, Stream? pdbStream)
|
||||||
{
|
{
|
||||||
_scriptAssemblyContext ??= new AssemblyLoadContext("ScriptContext", true);
|
Debug.Assert(_scriptAssemblyContext == null);
|
||||||
|
Debug.Assert(_appAssembly == null);
|
||||||
|
|
||||||
|
// A new AssemblyLoadContext for the script assembly allows us to unload the assembly later.
|
||||||
|
_scriptAssemblyContext = new AssemblyLoadContext($"ScriptContext_{Interlocked.Increment(ref _contextCounter)}", true);
|
||||||
|
|
||||||
_appAssembly = _scriptAssemblyContext.LoadFromStream(assemblyStream, pdbStream);
|
_appAssembly = _scriptAssemblyContext.LoadFromStream(assemblyStream, pdbStream);
|
||||||
|
|
||||||
Debug.Assert(_appAssembly != null);
|
Debug.Assert(_appAssembly != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static WeakReference? _weakAssemblyRef;
|
||||||
|
private static string? _lastAssemblyName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called by the engine to unload the assembly (containing user scripts).
|
/// Called by the engine to unload the assembly containing user scripts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
public static void UnloadAssemblies()
|
public static void UnloadScriptAssembly()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_scriptAssemblyContext?.Unload();
|
Debug.Assert(EntityScriptInstances.Count == 0, "Can not unload assemblies while entities are still active!");
|
||||||
|
|
||||||
|
// TODO: Should we allow calling unload when we never loaded?
|
||||||
|
if (_scriptAssemblyContext == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_weakAssemblyRef = new WeakReference(_scriptAssemblyContext);
|
||||||
|
_lastAssemblyName = _scriptAssemblyContext.Name;
|
||||||
|
Debug.WriteLine($"--- Killing {_lastAssemblyName}...");
|
||||||
|
|
||||||
|
// TODO: we should probably have a way to collect and terminate threads. (like a EngineThread override or something)
|
||||||
|
|
||||||
|
_scriptAssemblyContext.Unload();
|
||||||
_scriptAssemblyContext = null;
|
_scriptAssemblyContext = null;
|
||||||
|
_appAssembly = null;
|
||||||
|
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -137,6 +168,42 @@ internal static unsafe partial class ScriptGlue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits until the old assembly is dead.
|
||||||
|
/// </summary>
|
||||||
|
[UnmanagedCallersOnly]
|
||||||
|
public static void WaitUntilOldAssemblyDead()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_weakAssemblyRef == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int iterations = 0;
|
||||||
|
|
||||||
|
while (_weakAssemblyRef.IsAlive)
|
||||||
|
{
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
|
||||||
|
// We must sleep a bit, otherwise we absolutely wreck the debugger.
|
||||||
|
Thread.Sleep(10);
|
||||||
|
iterations++;
|
||||||
|
}
|
||||||
|
Debug.WriteLine($"--- {_lastAssemblyName} is now dead after {iterations} garbage collections!");
|
||||||
|
|
||||||
|
_weakAssemblyRef = null;
|
||||||
|
_lastAssemblyName = null;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Exception(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits until the old assembly is dead.
|
||||||
|
/// </summary>
|
||||||
private static NativeScriptClassInfo[]? _unsafeClasses;
|
private static NativeScriptClassInfo[]? _unsafeClasses;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 0)]
|
[StructLayout(LayoutKind.Sequential, Pack = 0)]
|
||||||
@@ -272,11 +339,16 @@ internal static unsafe partial class ScriptGlue
|
|||||||
_unsafeClasses = null;
|
_unsafeClasses = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly AsyncLocal<UUID> _currentEntityId = new ();
|
||||||
|
|
||||||
|
internal static UUID CurrentEntityId => _currentEntityId.Value;
|
||||||
|
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
public static void ShowEntityEditor(UUID entityId)
|
public static void ShowEntityEditor(UUID entityId)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_currentEntityId.Value = entityId;
|
||||||
(Entity entity, Type type) = EntityScriptInstances[entityId];
|
(Entity entity, Type type) = EntityScriptInstances[entityId];
|
||||||
EntityEditor.ShowEntityEditor(entity);
|
EntityEditor.ShowEntityEditor(entity);
|
||||||
}
|
}
|
||||||
@@ -284,6 +356,10 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
Log.Exception(e);
|
Log.Exception(e);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_currentEntityId.Value = UUID.Zero;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
@@ -291,6 +367,7 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_currentEntityId.Value = entityId;
|
||||||
(Entity entity, Type type) = EntityScriptInstances[entityId];
|
(Entity entity, Type type) = EntityScriptInstances[entityId];
|
||||||
entity.OnCreate();
|
entity.OnCreate();
|
||||||
}
|
}
|
||||||
@@ -298,6 +375,10 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
Log.Exception(e);
|
Log.Exception(e);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_currentEntityId.Value = UUID.Zero;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
|
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
|
||||||
@@ -305,6 +386,7 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_currentEntityId.Value = entityId;
|
||||||
(Entity entity, Type type) = EntityScriptInstances[entityId];
|
(Entity entity, Type type) = EntityScriptInstances[entityId];
|
||||||
entity.OnUpdate(deltaTime);
|
entity.OnUpdate(deltaTime);
|
||||||
}
|
}
|
||||||
@@ -312,6 +394,10 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
Log.Exception(e);
|
Log.Exception(e);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_currentEntityId.Value = UUID.Zero;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
|
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
|
||||||
@@ -319,6 +405,7 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_currentEntityId.Value = entityId;
|
||||||
(Entity entity, Type type) = EntityScriptInstances[entityId];
|
(Entity entity, Type type) = EntityScriptInstances[entityId];
|
||||||
entity.OnCollisionEnter2D(collision);
|
entity.OnCollisionEnter2D(collision);
|
||||||
}
|
}
|
||||||
@@ -326,6 +413,10 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
Log.Exception(e);
|
Log.Exception(e);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_currentEntityId.Value = UUID.Zero;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
@@ -333,6 +424,7 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_currentEntityId.Value = entityId;
|
||||||
if (EntityScriptInstances.Remove(entityId, out var match) && callDestroy > 0)
|
if (EntityScriptInstances.Remove(entityId, out var match) && callDestroy > 0)
|
||||||
{
|
{
|
||||||
match.Entity.OnDestroy();
|
match.Entity.OnDestroy();
|
||||||
@@ -342,6 +434,10 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
Log.Exception(e);
|
Log.Exception(e);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_currentEntityId.Value = UUID.Zero;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Type? GetTypeFromNativeString(byte* fullTypeName)
|
internal static Type? GetTypeFromNativeString(byte* fullTypeName)
|
||||||
@@ -365,6 +461,7 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_currentEntityId.Value = entityId;
|
||||||
Type? scriptType = GetTypeFromNativeString(scriptClassName);
|
Type? scriptType = GetTypeFromNativeString(scriptClassName);
|
||||||
|
|
||||||
Debug.Assert(scriptType != null, "Script class Type not found.");
|
Debug.Assert(scriptType != null, "Script class Type not found.");
|
||||||
@@ -379,6 +476,10 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
Log.Exception(e);
|
Log.Exception(e);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_currentEntityId.Value = UUID.Zero;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ComponentFunctionPointers
|
struct ComponentFunctionPointers
|
||||||
@@ -459,6 +560,7 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_currentEntityId.Value = entityId;
|
||||||
if (!EntityScriptInstances.TryGetValue(entityId, out var match))
|
if (!EntityScriptInstances.TryGetValue(entityId, out var match))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -468,6 +570,10 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
Log.Exception(e);
|
Log.Exception(e);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_currentEntityId.Value = UUID.Zero;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
@@ -475,6 +581,7 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_currentEntityId.Value = entityId;
|
||||||
if (!EntityScriptInstances.TryGetValue(entityId, out var match))
|
if (!EntityScriptInstances.TryGetValue(entityId, out var match))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -484,6 +591,10 @@ internal static unsafe partial class ScriptGlue
|
|||||||
{
|
{
|
||||||
Log.Exception(e);
|
Log.Exception(e);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_currentEntityId.Value = UUID.Zero;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
@@ -518,6 +629,19 @@ internal static unsafe partial class ScriptGlue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly]
|
||||||
|
public static void EntitySerializer_FinishSerialization(IntPtr engineSerializer)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EntitySerializer.FinishSerialization(engineSerializer);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Exception(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion EntitySerializer
|
#endregion EntitySerializer
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -544,14 +668,16 @@ internal static unsafe partial class ScriptGlue
|
|||||||
public static void Entity_GetScriptInstance(UUID entityId, out Entity? instance)
|
public static void Entity_GetScriptInstance(UUID entityId, out Entity? instance)
|
||||||
{
|
{
|
||||||
// We currently can implement this method here, because we only have C# scripts.
|
// We currently can implement this method here, because we only have C# scripts.
|
||||||
// If we ever need to do something to interop with other script languages, then this would change.
|
// If we ever need to do something to interop with other script languages, then this would probably need to communicate with the engine.
|
||||||
if (EntityScriptInstances.TryGetValue(entityId, out var match))
|
if (EntityScriptInstances.TryGetValue(entityId, out var match))
|
||||||
{
|
{
|
||||||
instance = match.Entity;
|
instance = match.Entity;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
instance = null;
|
instance = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Serialization_SerializeField(IntPtr serializationContext, SerializationType type, string fieldName, object? valueObject, string? fullTypeName)
|
public static void Serialization_SerializeField(IntPtr serializationContext, SerializationType type, string fieldName, object? valueObject, string? fullTypeName)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,8 +33,19 @@ internal static class EntitySerializer
|
|||||||
|
|
||||||
internal static void DestroySerializationContext(IntPtr engineSerializer)
|
internal static void DestroySerializationContext(IntPtr engineSerializer)
|
||||||
{
|
{
|
||||||
SerializationObjects.Remove(engineSerializer);
|
if (SerializationObjects.Remove(engineSerializer, out var serializedObjects))
|
||||||
DeserializationObjects.Remove(engineSerializer);
|
{
|
||||||
|
serializedObjects.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DeserializationObjects.Remove(engineSerializer, out var deserializationObjects))
|
||||||
|
{
|
||||||
|
deserializationObjects.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal static void FinishSerialization(IntPtr engineSerializer)
|
||||||
|
{
|
||||||
|
ClearSerializationContext(engineSerializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Serialize(Entity entity, IntPtr engineObject, IntPtr engineSerializer)
|
internal static void Serialize(Entity entity, IntPtr engineObject, IntPtr engineSerializer)
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ public class SerializedObject
|
|||||||
|
|
||||||
public void Serialize(Entity entity)
|
public void Serialize(Entity entity)
|
||||||
{
|
{
|
||||||
|
_serializedClasses.Add(entity, this);
|
||||||
SerializeInstanceFields(entity);
|
SerializeInstanceFields(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +322,7 @@ public class SerializedObject
|
|||||||
{
|
{
|
||||||
if (fieldType.IsSubclassOf(typeof(EngineObject)))
|
if (fieldType.IsSubclassOf(typeof(EngineObject)))
|
||||||
{
|
{
|
||||||
AddValueTypeField(fieldName, SerializationType.ObjectReference, ((EngineObject?)fieldValue)?.UUID ?? UUID.Zero, fieldValue?.GetType().FullName);
|
AddValueTypeField(fieldName, SerializationType.EngineObjectReference, ((EngineObject?)fieldValue)?.UUID ?? UUID.Zero, fieldValue?.GetType().FullName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -122,7 +122,8 @@ public class ScriptGlueGenerator : IIncrementalGenerator
|
|||||||
None,
|
None,
|
||||||
Ref,
|
Ref,
|
||||||
In,
|
In,
|
||||||
Out
|
Out,
|
||||||
|
Pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (MappedType Type, TypeModifier Modifier) DecodeType(string beefType, Dictionary<string, MappedType> beefTypeToMappedType)
|
private static (MappedType Type, TypeModifier Modifier) DecodeType(string beefType, Dictionary<string, MappedType> beefTypeToMappedType)
|
||||||
@@ -145,6 +146,15 @@ public class ScriptGlueGenerator : IIncrementalGenerator
|
|||||||
beefType = beefType.Substring(3);
|
beefType = beefType.Substring(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (beefTypeToMappedType.TryGetValue(beefType, out var mapping))
|
||||||
|
return (mapping, modifier);
|
||||||
|
|
||||||
|
if (beefType.EndsWith("*"))
|
||||||
|
{
|
||||||
|
modifier = TypeModifier.Pointer;
|
||||||
|
beefType = beefType.Substring(0, beefType.Length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (beefTypeToMappedType.TryGetValue(beefType, out MappedType? mappedType))
|
if (beefTypeToMappedType.TryGetValue(beefType, out MappedType? mappedType))
|
||||||
{
|
{
|
||||||
return (mappedType, modifier);
|
return (mappedType, modifier);
|
||||||
@@ -296,6 +306,14 @@ public class ScriptGlueGenerator : IIncrementalGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
parameterText.Append(parameterType.CSharpTypeName);
|
parameterText.Append(parameterType.CSharpTypeName);
|
||||||
|
|
||||||
|
switch (parameterModifier)
|
||||||
|
{
|
||||||
|
case TypeModifier.Pointer:
|
||||||
|
parameterText.Append("*");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
parameterText.Append(", ");
|
parameterText.Append(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +363,12 @@ public class ScriptGlueGenerator : IIncrementalGenerator
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
parameterText.Append($"{parameterType.CSharpWrapperType} {param.Name}");
|
parameterText.Append($"{parameterType.CSharpWrapperType}");
|
||||||
|
|
||||||
|
if (parameterModifier == TypeModifier.Pointer)
|
||||||
|
parameterText.Append("*");
|
||||||
|
|
||||||
|
parameterText.Append($" {param.Name}");
|
||||||
|
|
||||||
switch (parameterModifier)
|
switch (parameterModifier)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user