diff --git a/GS2Engine.UnitTests/Objects/Drawing.cs b/GS2Engine.UnitTests/Objects/Drawing.cs deleted file mode 100644 index 8e8559a..0000000 --- a/GS2Engine.UnitTests/Objects/Drawing.cs +++ /dev/null @@ -1,40 +0,0 @@ -using GS2Engine.Extensions; -using GS2Engine.Models; - -namespace GS2Engine.UnitTests.Objects; - -public class Drawing : VariableCollection -{ - private string? _image; - - public Drawing( - string? image, - int x, - int y, - int? cropx = null, - int? cropy = null, - int? width = null, - int? height = null, - float scale = 1f, - float rotation = 0f - ) - { - _image = image; - Rotation = rotation; - } - - public double Rotation - { - get => GetVariable("rotation").GetValue(); - private set => AddOrUpdate("rotation", value.ToStackEntry()); - } - public ImgVis Layer { get; private set; } = ImgVis.DrawOverPlayer; - - public void ShowImg(string? image, int x, int y) - { - _image = image; - } - - public void ChangeImgVis(ImgVis imgVis) => Layer = imgVis; - -} \ No newline at end of file diff --git a/GS2Engine.UnitTests/ScriptMachineTests.cs b/GS2Engine.UnitTests/ScriptMachineTests.cs deleted file mode 100644 index 657df44..0000000 --- a/GS2Engine.UnitTests/ScriptMachineTests.cs +++ /dev/null @@ -1,308 +0,0 @@ -using System.Collections.Concurrent; -using System.Reflection; -using GS2Engine.Enums; -using GS2Engine.Extensions; -using GS2Engine.GS2.Script; -using GS2Engine.Models; -using GS2Engine.UnitTests.Objects; -using static GS2Engine.GS2.Script.Script; - -namespace GS2Engine.UnitTests; - -public class ScriptMachineTests -{ - private int _calledTimes; - private readonly Dictionary _receivedStrings = new(); - - public ScriptMachineTests() - { - Command echoCommand = delegate (ScriptMachine machine, IStackEntry[]? args) - { - if (args?.Length > 0) - { - _receivedStrings[_calledTimes] = machine.GetEntry(args[0]).GetValue()?.ToString() ?? ""; - - Console.WriteLine(_receivedStrings[_calledTimes]); - - _calledTimes++; - } - - return 0.ToStackEntry(); - }; - - GlobalFunctions.AddOrUpdate( - "echo", - echoCommand, - (_, _) => echoCommand - ); - } - - private static Script InitializeScript(string scriptText) - { - var response = GS2Compiler.Interface.CompileCode( - scriptText, - "weapon", - "testScript" - ); - - if (response.Success) - { - // Arrange - return new("testScript", response.ByteCode); - } - - throw new("Script failure"); - } - - [Fact] - public void When_for_loop_with_8_loops_Then_echo_is_called_8_times() - { - //Arrange - _receivedStrings.Clear(); - _calledTimes = 0; - const string scriptText = - """ - //#CLIENTSIDE - function onCreated() { - for(this.i=0;this.i<8;this.i++) { - echo(((this.i==6)?"test2":"test") @ "_text_" @ this.i); - } - } - """; - var script = InitializeScript(scriptText); - - //Act - _ = script.Call("onCreated"); - - //Assert - Assert.Equal(8, _calledTimes); - Assert.Equal("test_text_3", _receivedStrings[3]); - Assert.Equal("test2_text_6", _receivedStrings[6]); - } - - [Fact] - public async Task When_for_loop_with_items_Then_properly_for_loop_through_items() - { - //Arrange - _receivedStrings.Clear(); - _calledTimes = 0; - const string scriptText = - """ - //#CLIENTSIDE - function onCreated() { - temp.i = 0; - temp.sounds = { - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - "text_" @ temp.i++, - }; - - for (temp.sound : temp.sounds) { - echo(temp.sound); - } - - return "done!"; - } - """; - var script = InitializeScript(scriptText); - - //Act - var result = (await script.Call("onCreated")).GetValue(); - - //Assert - Assert.Equal("done!", result?.ToString()); - Assert.Equal(15, _calledTimes); - Assert.Equal("text_11", _receivedStrings[3]); - Assert.Equal("text_0", _receivedStrings[14]); - } - - [Fact] - public async Task When_for_loop_with_images_Then_properly_for_loop_through_images() - { - //Arrange - _receivedStrings.Clear(); - _calledTimes = 0; - - ConcurrentDictionary Drawings = new(); - - Command showimgCommand = delegate(ScriptMachine machine, IStackEntry[]? args) - { - if (!(args?.Length > 3)) return 0.ToStackEntry(); - try - { - var index = (int)machine.GetEntry(args[0]).GetValue(); - string? image = machine.GetEntry(args[1]).GetValue() ?? string.Empty; - - var x = (int)machine.GetEntry(args[2]).GetValue(); - var y = (int)machine.GetEntry(args[3]).GetValue(); - if (Drawings.TryGetValue(index, out var value)) - { - value?.ShowImg(image, x, y); - } - else - { - value = new(image, x, y); - Drawings.AddOrUpdate(index, value, (_, _) => value); - } - } - catch (Exception e) - { - //_logger.LogDebug(e.Message); - } - - return 0.ToStackEntry(); - }; - - GlobalFunctions.AddOrUpdate( - "showimg", - showimgCommand, - (_, _) => showimgCommand - ); - - Command findimgCommand = delegate(ScriptMachine machine, IStackEntry[]? args) - { - if (!(args?.Length > 0)) return 0.ToStackEntry(); - try - { - var index = (int)machine.GetEntry(args[0]).GetValue(); - - if (Drawings.TryGetValue(index, out var value)) - { - return value!.ToStackEntry(); - } - } - catch (Exception e) - { - //_logger.LogDebug(e.Message); - } - - return 0.ToStackEntry(); - }; - - GlobalFunctions.AddOrUpdate( - "findimg", - findimgCommand, - (_, _) => findimgCommand - ); - - Command getimgwidth = delegate(ScriptMachine machine, IStackEntry[]? args) - { - if (!(args?.Length > 0)) return 0.ToStackEntry(); - try - { - var image = machine.GetEntry(args[0]).GetValue(); - - Console.WriteLine(image); - - return 1!.ToStackEntry(); - - } - catch (Exception e) - { - //_logger.LogDebug(e.Message); - } - - return 0.ToStackEntry(); - }; - - GlobalFunctions.AddOrUpdate( - "getimgwidth", - getimgwidth, - (_, _) => getimgwidth - ); - const string scriptText = - """ - //#CLIENTSIDE - function onCreated() { - temp.images = { - "eye_platdownload.gif", - "sign1.gif", - "eye_giantbomb.png", - "eye_platloading.gif", - "sen_piano.png", - "sen_tileset_0.png", - "sen_tileset_1.png", - "sen_tileset_2.png", - "sen_tileset_3.png", - "sen_tileset_4.png", - "sen_tileset_5.png", - "sen_tileset_6.png", - "sen_tileset_7.png", - "bluelampani2.gif", - "koni_bomber_vpieces.gif", - //"pics1.png", - //"eye_bomber_choc.png", - "eye_bomber_pcur.png", - "eye_bomber_pgui.png", - "eye_bomber_poni.png", - "eye_bomber_pqui.png", - "eye_bombsprites-body.png", - "eye_bombsprites-dec0.png", - "eye_bombsprites-dec1.png", - "eye_bombsprites-dec2.png", - "eye_bombsprites-dec3.png", - "eye_bombsprites-dec4.png", - "eye_bombsprites-dec5.png", - "eye_bombsprites-dec6.png", - "eye_bombsprites-dec7.png", - "eye_bombsprites-dec8.png", - "eye_bombsprites-fire.png", - "eye_bombsprites-fuse.png", - "cadavrezcog2.png", - "koni_vase.png", - "eye_p1a.png", - "eye_p1b.png", - "eye_p1c.png", - "eye_p1d.png", - "eye_p1e.png", - "eye_p1f.png", - "eye_p1g.png", - "eye_p1h.png", - "eye_p1i.png", - "eye_p1j.png", - "eye_p1k.png", - "eye_p1l.png", - "eye_bomber_coin.png", - "eye_bomber_coinbag.png", - "eye_bomber_notice2.png", - "eye_bomber_notice.png", - "bmb_pics1.png" - }; - - this.tokenscount = temp.images.size(); - echo(this.tokenscount); - for(this.img=0; this.img < this.tokenscount; this.img++) { - echo(temp.images[this.img]); - if(this.img>2) echo("DrawBar()"); - while(getimgwidth(temp.images[this.img])==0) sleep(0.01); - hideimg(500); - if(this.img%300==0) sleep(0.01); - echo(temp.images[this.img]); - } - } - """; - var script = InitializeScript(scriptText); - - //Act - var result = (await script.Call("onCreated")).GetValue(); - - //Assert - Assert.Equal(0, result); - Assert.Equal(148, _calledTimes); - Assert.Equal("sign1.gif", _receivedStrings[3]); - Assert.Equal("bmb_pics1.png", _receivedStrings[147]); - } - -} \ No newline at end of file diff --git a/GS2Engine/Models/GuiControl.cs b/GS2Engine/Models/GuiControl.cs deleted file mode 100644 index df44bd9..0000000 --- a/GS2Engine/Models/GuiControl.cs +++ /dev/null @@ -1,328 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using GS2Engine.Enums; -using GS2Engine.Extensions; -using GS2Engine.GS2.Script; - -namespace GS2Engine.Models; - -[SuppressMessage("ReSharper", "MemberCanBeProtected.Global")] -public class GuiControl : VariableCollection, IGuiControl, IDisposable -{ - protected readonly string Id; - protected readonly Script? Script; - - public GuiControl(string id, Script? script) - { - Console.WriteLine($"Creating control with ID {id}"); - Id = id; - Script = script; - Script.GlobalVariables.AddOrUpdate(id.ToLower(), this.ToStackEntry()); - - Active = true; - CanMove = true; - CanResize = true; - ClipMove = false; - ClipChildren = false; - X = -1; - Y = -1; - Width = -1; - Height = -1; - AddOrUpdate( - "addcontrol", - new Script.Command( - (m, a) => - { - if (!(a?.Length > 0)) return 0.ToStackEntry(); - - var control = m.GetEntry(a[0], StackEntryType.Variable).GetValue(); - if (control != null) - AddControl(control); - - return 0.ToStackEntry(); - } - ).ToStackEntry() - ); - - SetCallback("extent", SetExtentCallback); - GetCallback("extent", GetExtentCallback); - SetCallback("clientextent", SetClientExtentCallback); - GetCallback("clientextent", GetClientExtentCallback); - SetCallback("position", PositionCallback); - } - - public bool Active - { - get => GetVariable("active").GetValue(); - private set => AddOrUpdate("active", value.ToStackEntry()); - } - - public bool Awake => GetVariable("awake").GetValue(); - - public bool CanMove - { - get => GetVariable("canmove").GetValue(); - set => AddOrUpdate("canmove", value.ToStackEntry()); - } - - public bool CanResize - { - get => GetVariable("canresize").GetValue(); - set => AddOrUpdate("canresize", value.ToStackEntry()); - } - - public bool ClipChildren - { - get => GetVariable("clipchildren").GetValue(); - set => AddOrUpdate("clipchildren", value.ToStackEntry()); - } - - public bool ClipMove - { - get => GetVariable("clipmove").GetValue(); - set => AddOrUpdate("clipmove", value.ToStackEntry()); - } - - public bool ClipToBounds - { - get => GetVariable("cliptobounds").GetValue(); - set => AddOrUpdate("cliptobounds", value.ToStackEntry()); - } - - public HashSet Controls { get; } = []; - - public int Cursor - { - get => GetVariable("cursor").GetValue(); - set => AddOrUpdate("cursor", value.ToStackEntry()); - } - - public bool Editing - { - get => GetVariable("editing").GetValue(); - set => AddOrUpdate("editing", value.ToStackEntry()); - } - - public string Extent - { - get => GetVariable("extent").GetValue() ?? string.Empty; - set => AddOrUpdate("extent", value.ToStackEntry()); - } - - public bool Flickering - { - get => GetVariable("flickering").GetValue(); - set => AddOrUpdate("flickering", value.ToStackEntry()); - } - - public int FlickerTime - { - get => GetVariable("flickertime").GetValue(); - set => AddOrUpdate("flickertime", value.ToStackEntry()); - } - - public string Hint - { - get => GetVariable("hint").GetValue() ?? string.Empty; - set => AddOrUpdate("hint", value.ToStackEntry()); - } - public string HorizSizing - { - get => GetVariable("horizsizing").GetValue() ?? string.Empty; - set => AddOrUpdate("horizsizing", value.ToStackEntry()); - } - public int layer { get; } - public string MinExtent - { - get => GetVariable("minextent").GetValue() ?? string.Empty; - set => AddOrUpdate("minextent", value.ToStackEntry()); - } - public string MinSize - { - get => GetVariable("minsize").GetValue() ?? string.Empty; - set => AddOrUpdate("minsize", value.ToStackEntry()); - } - public string position - { - get => GetVariable("position").GetValue() ?? string.Empty; - set => AddOrUpdate("position", value.ToStackEntry()); - } - public IGuiControl? profile - { - get => GetVariable("profile").GetValue(); - set => AddOrUpdate("profile", value.ToStackEntry()); - } - - public bool ResizeHeight - { - get => GetVariable("resizeheight").GetValue(); - set => AddOrUpdate("resizeheight", value.ToStackEntry()); - } - - public bool ResizeWidth - { - get => GetVariable("resizewidth").GetValue(); - set => AddOrUpdate("resizewidth", value.ToStackEntry()); - } - - public int scrolllinex { get; set; } - public int scrollliney { get; set; } - - public bool ShowHint - { - get => GetVariable("showhint").GetValue(); - set => AddOrUpdate("showhint", value.ToStackEntry()); - } - - public bool UseOwnProfile - { - get => GetVariable("useownprofile").GetValue(); - set => AddOrUpdate("useownprofile", value.ToStackEntry()); - } - - public string vertsizing - { - get => GetVariable("vertsizing").GetValue() ?? string.Empty; - set => AddOrUpdate("vertsizing", value.ToStackEntry()); - } - - public bool Visible - { - get => GetVariable("visible").GetValue(); - set => AddOrUpdate("visible", value.ToStackEntry()); - } - - public int Height - { - get => (int)GetVariable("height").GetValue(); - set => AddOrUpdate("height", value.ToStackEntry()); - } - - public int Width - { - get => (int)GetVariable("width").GetValue(); - set => AddOrUpdate("width", value.ToStackEntry()); - } - - public int X - { - get => (int)GetVariable("x").GetValue(); - set => AddOrUpdate("x", value.ToStackEntry()); - } - - public int Y - { - get => (int)GetVariable("y").GetValue(); - set => AddOrUpdate("y", value.ToStackEntry()); - } - - public void Dispose() - { - Active = false; - } - - public void Destroy() - { - //lock (controls) - { - foreach (var control in Controls) control?.Destroy(); - Controls.Clear(); - } - Dispose(); - } - - public IGuiControl? parent - { - get => GetVariable("parent").GetValue(); - set => AddOrUpdate("parent", value.ToStackEntry()); - } - - public void AddControl(IGuiControl? obj) - { - if (obj == null) return; - obj.parent = this; - lock (Controls) - { - Controls.Add(obj); - } - } - - // ReSharper disable once UnusedMember.Global - protected void CallAction() => Script?.Call($"{Id}.onAction", []).ConfigureAwait(false).GetAwaiter().GetResult(); - - - public virtual void Draw() - { - //lock (controls) - { - foreach (var control in Controls) control?.Draw(); - } - } - - protected void SetClientExtentCallback(object? posVar) - { - switch (posVar) - { - case List var: - Width = (int)(double)(var[0] ?? "-1"); - Height = (int)(double)(var[1] ?? "-1"); - break; - case TString posVarString: - { - string? positionString = posVarString; - if (positionString?.Length <= 0 || positionString == null) return; - string[] p = positionString.Split(' '); - - if (double.TryParse(p[0], out var p0)) Width = (int)p0; - if (double.TryParse(p[1], out var p1)) Height = (int)p1; - break; - } - } - } - private object? GetClientExtentCallback() => $"{Width} {Height}"; - - protected void SetExtentCallback(object? posVar) - { - switch (posVar) - { - case List var: - Width = (int)(double)(var[0] ?? "-1"); - Height = (int)(double)(var[1] ?? "-1"); - break; - case TString posVarString: - { - var positionString = posVarString.ToString(); - if (positionString.Length <= 0) return; - var p = positionString.Split(' '); - - if (double.TryParse(p[0], out var p0)) Width = (int)p0; - if (double.TryParse(p[1], out var p1)) Height = (int)p1; - break; - } - } - } - - private object? GetExtentCallback() => $"{Width} {Height}"; - - protected void PositionCallback(object? posVar) - { - switch (posVar) - { - case List var: - X = (int)(double)(var[0] ?? "-1"); - Y = (int)(double)(var[1] ?? "-1"); - break; - case TString posVarString: - { - string? positionString = posVarString; - if (positionString?.Length <= 0 || positionString == null) return; - string[] p = positionString.Split(' '); - - if (double.TryParse(p[0], out var p0)) X = (int)p0; - if (double.TryParse(p[1], out var p1)) Y = (int)p1; - break; - } - } - } -} \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index 560d4fc..f501987 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -43,7 +43,7 @@ def buildStepDocker() { def split_job_name = env.JOB_NAME.split(/\/{1}/); def fixed_job_name = split_job_name[1].replace('%2F',' '); - def customImage = docker.image("mcr.microsoft.com/dotnet/sdk:9.0"); + def customImage = docker.image("mcr.microsoft.com/dotnet/sdk:10.0"); customImage.pull(); try { @@ -83,7 +83,7 @@ def buildStepDocker() { stage("Building NuGet Package") { customImage.inside("-u 0") { sh("chmod 777 -R ."); - sh("dotnet pack GS2Engine/GS2Engine.csproj -c Release ${VER}"); + sh("dotnet pack Preagonal.Scripting.GS2Engine/Preagonal.Scripting.GS2Engine.csproj -c Release ${VER}"); sh("chmod 777 -R ."); } } @@ -116,7 +116,7 @@ def buildStepDocker() { ) withCredentials([string(credentialsId: 'PREAGONAL_GS2ENGINE_CODECOV_TOKEN', variable: 'CODECOV_TOKEN')]) { - sh("curl -s https://codecov.io/bash > codecov && chmod +x codecov && ./codecov -f \"Testing/unit_tests.xml\" -t ${env.CODECOV_TOKEN} && ./codecov -f \"GS2Engine.UnitTests/coverage.opencover.xml\" -t ${env.CODECOV_TOKEN}") + sh("curl -s https://codecov.io/bash > codecov && chmod +x codecov && ./codecov -f \"Testing/unit_tests.xml\" -t ${env.CODECOV_TOKEN} && ./codecov -f \"Preagonal.Scripting.GS2Engine.UnitTests/coverage.opencover.xml\" -t ${env.CODECOV_TOKEN}") } stage("Xunit") { @@ -124,7 +124,7 @@ def buildStepDocker() { testTimeMargin: '3000', thresholdMode: 1, thresholds: [ - skipped(failureThreshold: '0'), + skipped(failureThreshold: '1000'), failed(failureThreshold: '0') ], tools: [MSTest( @@ -144,11 +144,11 @@ def buildStepDocker() { stage("Pushing NuGet") { customImage.inside("-u 0") { withCredentials([string(credentialsId: 'PREAGONAL_GITHUB_TOKEN', variable: 'GITHUB_TOKEN')]) { - sh("dotnet nuget push -s https://nuget.pkg.github.com/Preagonal/index.json -k ${env.GITHUB_TOKEN} GS2Engine/bin/Release/*.nupkg;chmod 777 -R ."); + sh("dotnet nuget push --skip-duplicate -s https://nuget.pkg.github.com/Preagonal/index.json -k ${env.GITHUB_TOKEN} Preagonal.Scripting.GS2Engine/bin/Release/*.nupkg;chmod 777 -R ."); discordSend description: "NuGet Successful", footer: "", link: env.BUILD_URL, result: currentBuild.currentResult, title: "[${split_job_name[0]}] Artifact Successful: ${fixed_job_name} #${env.BUILD_NUMBER}", webhookURL: env.GS2EMU_WEBHOOK; } withCredentials([string(credentialsId: 'PREAGONAL_NUGET_TOKEN', variable: 'NUGET_TOKEN')]) { - sh("dotnet nuget push -s https://api.nuget.org/v3/index.json -k ${env.NUGET_TOKEN} GS2Engine/bin/Release/*.nupkg;chmod 777 -R ."); + sh("dotnet nuget push --skip-duplicate -s https://api.nuget.org/v3/index.json -k ${env.NUGET_TOKEN} Preagonal.Scripting.GS2Engine/bin/Release/*.nupkg;chmod 777 -R ."); discordSend description: "NuGet Successful", footer: "", link: env.BUILD_URL, result: currentBuild.currentResult, title: "[${split_job_name[0]}] Artifact Successful: ${fixed_job_name} #${env.BUILD_NUMBER}", webhookURL: env.GS2EMU_WEBHOOK; } } @@ -224,13 +224,13 @@ node('master') { ).trim(); env.JSON_RESPONSE = sh( - script: "curl -L -X POST -H \"Accept: application/vnd.github+json\" -H \"Authorization: Bearer ${env.GITHUB_TOKEN}\" -H \"X-GitHub-Api-Version: 2022-11-28\" https://api.github.com/repos/preagonal/gs2engine/git/tags -d '{\"tag\":\"${tagName}\",\"message\":\"${env.COMMIT_MSG}\",\"object\":\"${env.GIT_COMMIT}\",\"type\":\"tree\",\"tagger\":{\"name\":\"preagonal-pipeline[bot]\",\"email\":\"119898225+preagonal-pipeline[bot]@users.noreply.github.com\",\"date\":\"${iso8601Date}\"}}'", + script: "curl -L -X POST -H \"Accept: application/vnd.github+json\" -H \"Authorization: Bearer ${env.GITHUB_TOKEN}\" -H \"X-GitHub-Api-Version: 2022-11-28\" https://api.github.com/repos/preagonal/Preagonal.Scripting.gs2engine/git/tags -d '{\"tag\":\"${tagName}\",\"message\":\"${env.COMMIT_MSG}\",\"object\":\"${env.GIT_COMMIT}\",\"type\":\"tree\",\"tagger\":{\"name\":\"preagonal-pipeline[bot]\",\"email\":\"119898225+preagonal-pipeline[bot]@users.noreply.github.com\",\"date\":\"${iso8601Date}\"}}'", returnStdout: true ); def response = readJSON(text: env.JSON_RESPONSE); sh( - script: "curl -L -X POST -H \"Accept: application/vnd.github+json\" -H \"Authorization: Bearer ${env.GITHUB_TOKEN}\" -H \"X-GitHub-Api-Version: 2022-11-28\" https://api.github.com/repos/preagonal/gs2engine/git/refs -d '{\"ref\": \"refs/tags/${tagName}\", \"sha\": \"${response.sha}\"}'", + script: "curl -L -X POST -H \"Accept: application/vnd.github+json\" -H \"Authorization: Bearer ${env.GITHUB_TOKEN}\" -H \"X-GitHub-Api-Version: 2022-11-28\" https://api.github.com/repos/preagonal/Preagonal.Scripting.gs2engine/git/refs -d '{\"ref\": \"refs/tags/${tagName}\", \"sha\": \"${response.sha}\"}'", returnStdout: true ); } @@ -250,8 +250,8 @@ node('master') { if (env.TAG_NAME) { def DESC = sh(returnStdout: true, script: 'cat RELEASE_DESCRIPTION.txt'); - discordSend(description: "${DESC}", customUsername: "OpenGraal", customAvatarUrl: "https://pbs.twimg.com/profile_images/1895028712/13460_106738052711614_100001262603030_51047_4149060_n_400x400.jpg", footer: "OpenGraal Team", link: "https://github.com/Preagonal/GS2Engine/pkgs/nuget/GS2Engine", result: "SUCCESS", title: "GS2Engine v${env.TAG_NAME} NuGet Package", webhookURL: env.GS2EMU_RELEASE_WEBHOOK); + discordSend(description: "${DESC}", customUsername: "Preagonal Team", customAvatarUrl: "http://avatars.githubusercontent.com/u/115027903?s=400&u=32e1a4e73e367fc86404530293ecf05b60bb1132&v=4", footer: "Preagonal Team", link: "https://www.nuget.org/packages/Preagonal.Scripting.GS2Engine/${env.TAG_NAME}", result: "SUCCESS", title: "GS2Engine v${env.TAG_NAME} NuGet Package", webhookURL: env.GS2EMU_RELEASE_WEBHOOK); } sh("rm -rf ./*"); -} +} \ No newline at end of file diff --git a/GS2Engine.TestApp/Dockerfile b/Preagonal.Scripting.GS2Engine.TestApp/Dockerfile similarity index 82% rename from GS2Engine.TestApp/Dockerfile rename to Preagonal.Scripting.GS2Engine.TestApp/Dockerfile index 9641014..00ae3ad 100644 --- a/GS2Engine.TestApp/Dockerfile +++ b/Preagonal.Scripting.GS2Engine.TestApp/Dockerfile @@ -1,7 +1,7 @@ -FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base +FROM mcr.microsoft.com/dotnet/runtime:10.0 AS base WORKDIR /app -FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /src COPY ["GS2Engine.TestApp/GS2Engine.TestApp.csproj", "GS2Engine.TestApp/"] RUN dotnet restore "GS2Engine.TestApp/GS2Engine.TestApp.csproj" diff --git a/GS2Engine.TestApp/Objects/Drawing.cs b/Preagonal.Scripting.GS2Engine.TestApp/Objects/Drawing.cs similarity index 90% rename from GS2Engine.TestApp/Objects/Drawing.cs rename to Preagonal.Scripting.GS2Engine.TestApp/Objects/Drawing.cs index e7155c2..42cbbe0 100644 --- a/GS2Engine.TestApp/Objects/Drawing.cs +++ b/Preagonal.Scripting.GS2Engine.TestApp/Objects/Drawing.cs @@ -1,7 +1,7 @@ -using GS2Engine.Extensions; -using GS2Engine.Models; +using Preagonal.Scripting.GS2Engine.Extensions; +using Preagonal.Scripting.GS2Engine.Models; -namespace GS2Engine.TestApp.Objects; +namespace Preagonal.Scripting.GS2Engine.TestApp.Objects; public class Drawing : VariableCollection { diff --git a/GS2Engine.TestApp/GS2Engine.TestApp.csproj b/Preagonal.Scripting.GS2Engine.TestApp/Preagonal.Scripting.GS2Engine.TestApp.csproj similarity index 67% rename from GS2Engine.TestApp/GS2Engine.TestApp.csproj rename to Preagonal.Scripting.GS2Engine.TestApp/Preagonal.Scripting.GS2Engine.TestApp.csproj index 19e2264..f54d10c 100644 --- a/GS2Engine.TestApp/GS2Engine.TestApp.csproj +++ b/Preagonal.Scripting.GS2Engine.TestApp/Preagonal.Scripting.GS2Engine.TestApp.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 enable enable Linux @@ -17,18 +17,14 @@ - + - - - - - ..\..\..\..\..\.nuget\packages\xunit.assert\2.8.1\lib\net6.0\xunit.assert.dll - + + diff --git a/GS2Engine.TestApp/Program.cs b/Preagonal.Scripting.GS2Engine.TestApp/Program.cs similarity index 95% rename from GS2Engine.TestApp/Program.cs rename to Preagonal.Scripting.GS2Engine.TestApp/Program.cs index e4bd978..2e77b50 100644 --- a/GS2Engine.TestApp/Program.cs +++ b/Preagonal.Scripting.GS2Engine.TestApp/Program.cs @@ -1,13 +1,10 @@ using System.Collections.Concurrent; using System.Reflection; -using GS2Engine.Extensions; -using GS2Engine.GS2.Script; -using GS2Engine.Models; -using GS2Engine.TestApp.Objects; +using Preagonal.Scripting.GS2Engine.GS2.Script; +using Preagonal.Scripting.GS2Engine.TestApp.Objects; using Xunit; -using static GS2Engine.GS2.Script.Script; -namespace GS2Engine.TestApp; +namespace Preagonal.Scripting.GS2Engine.TestApp; internal static class Program { @@ -31,6 +28,8 @@ private static async Task Main(string[] args) var calledTimes = 0; var receivedStrings = new Dictionary(); var path = $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}{Path.DirectorySeparatorChar}scripts/test.gs2bc"; + + /* Command echoCommand = delegate (ScriptMachine machine, IStackEntry[]? args) { if (!(args?.Length > 0)) return 0.ToStackEntry(); @@ -49,9 +48,11 @@ private static async Task Main(string[] args) echoCommand, (_, _) => echoCommand ); + */ ConcurrentDictionary Drawings = new(); + /* Command showimgCommand = delegate(ScriptMachine machine, IStackEntry[]? args) { if (!(args?.Length > 3)) return 0.ToStackEntry(); @@ -140,6 +141,7 @@ private static async Task Main(string[] args) getimgwidth, (_, _) => getimgwidth ); + */ const string scriptText = """ diff --git a/Preagonal.Scripting.GS2Engine.UnitTests/Objects/Drawing.cs b/Preagonal.Scripting.GS2Engine.UnitTests/Objects/Drawing.cs new file mode 100644 index 0000000..10c2c66 --- /dev/null +++ b/Preagonal.Scripting.GS2Engine.UnitTests/Objects/Drawing.cs @@ -0,0 +1,57 @@ +using Preagonal.Scripting.GS2Engine.Models; + +namespace Preagonal.Scripting.GS2Engine.UnitTests.Objects; + +public class Drawing : ScriptVariable +{ + public new static readonly DrawingProperties PropertiesInstance = []; + public override IScriptProperties Properties => PropertiesInstance; + private protected string? _image = ""; + + public Drawing( + string? image, + int x, + int y, + int? cropx = null, + int? cropy = null, + int? width = null, + int? height = null, + float zoom = 1f, + float rotation = 0f + ) + { + _image = image; + //Position = new(x, y); + + //if (cropx != null && cropy != null && width != null && height != null) + // Source = new(cropx.Value, cropy.Value, width.Value, height.Value); + + Rotation = rotation; + Zoom = zoom; + Hidden = false; + } + + //public virtual Texture2D? Image => TextureSystem.GetInstance().GetImage(_image); + //public Vector2 Position { get; private protected set; } + //public Rectangle? Source { get; private set; } + public bool Hidden { get; private protected set; } + //public Color Color { get; private protected set; } = Color.White; + + public double Rotation { get; set; } + public ImgVis Layer { get; private set; } = ImgVis.DrawOverPlayer; + + public void ShowImg(string? image, int x, int y) + { + _image = image; + //Position = new(x, y); + Hidden = false; + } + + public void ChangeImgVis(ImgVis imgVis) => Layer = imgVis; + //public void ChangeImgPart(int x, int y, int w, int h) => Source = new Rectangle(x, y, w, h); + public void ChangeImgZoom(double zoom) => Zoom = (float)zoom; + //public void ChangeImgColors(int r, int g, int b, int a) => Color = new(r, g, b, a); + public void Hide() => Hidden = true; + public void Show() => Hidden = false; + public float Zoom { get; private set; } = 1.00f; +} \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine.UnitTests/Objects/DrawingProperties.cs b/Preagonal.Scripting.GS2Engine.UnitTests/Objects/DrawingProperties.cs new file mode 100644 index 0000000..a4d2232 --- /dev/null +++ b/Preagonal.Scripting.GS2Engine.UnitTests/Objects/DrawingProperties.cs @@ -0,0 +1,44 @@ +using Preagonal.Scripting.GS2Engine.Models; + +namespace Preagonal.Scripting.GS2Engine.UnitTests.Objects; + +public class DrawingProperties : ScriptProperties +{ + public DrawingProperties() : base(typeof(ScriptVariable)) + { + AddProperties( + this, + new() + { + { "rotation", "", drawing => drawing.Rotation, (drawing, rotation) => drawing.Rotation = rotation }, + } + ); + + /* + AddFunctions( + this, + new() + { + { + "settimer", + "", + (control, o2) => + { + var value = o2.FirstOrDefault()?.GetValue(); + switch (value) + { + case double timeout: + control.SetTimer(timeout); + break; + } + + return 0; + } + }, + } + ); + */ + + Compile(); + } +} \ No newline at end of file diff --git a/GS2Engine.UnitTests/Objects/ImgVis.cs b/Preagonal.Scripting.GS2Engine.UnitTests/Objects/ImgVis.cs similarity index 65% rename from GS2Engine.UnitTests/Objects/ImgVis.cs rename to Preagonal.Scripting.GS2Engine.UnitTests/Objects/ImgVis.cs index 4ff33e8..0765710 100644 --- a/GS2Engine.UnitTests/Objects/ImgVis.cs +++ b/Preagonal.Scripting.GS2Engine.UnitTests/Objects/ImgVis.cs @@ -1,4 +1,4 @@ -namespace GS2Engine.UnitTests.Objects; +namespace Preagonal.Scripting.GS2Engine.UnitTests.Objects; public enum ImgVis { diff --git a/GS2Engine.UnitTests/GS2Engine.UnitTests.csproj b/Preagonal.Scripting.GS2Engine.UnitTests/Preagonal.Scripting.GS2Engine.UnitTests.csproj similarity index 73% rename from GS2Engine.UnitTests/GS2Engine.UnitTests.csproj rename to Preagonal.Scripting.GS2Engine.UnitTests/Preagonal.Scripting.GS2Engine.UnitTests.csproj index d7f0a6c..b842609 100644 --- a/GS2Engine.UnitTests/GS2Engine.UnitTests.csproj +++ b/Preagonal.Scripting.GS2Engine.UnitTests/Preagonal.Scripting.GS2Engine.UnitTests.csproj @@ -1,36 +1,37 @@ - net9.0 enable enable false default + + net9.0;net10.0;net8.0 - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/Preagonal.Scripting.GS2Engine.UnitTests/ScriptMachineTests.cs b/Preagonal.Scripting.GS2Engine.UnitTests/ScriptMachineTests.cs new file mode 100644 index 0000000..b84bd98 --- /dev/null +++ b/Preagonal.Scripting.GS2Engine.UnitTests/ScriptMachineTests.cs @@ -0,0 +1,598 @@ +using System.Collections.Concurrent; +using Preagonal.Scripting.GS2Engine.Extensions; +using Preagonal.Scripting.GS2Engine.GS2.Script; +using Preagonal.Scripting.GS2Engine.Models; +using Preagonal.Scripting.GS2Engine.UnitTests.Objects; +using Xunit.Abstractions; +using static Preagonal.Scripting.GS2Engine.GS2.Script.Script; + +namespace Preagonal.Scripting.GS2Engine.UnitTests; + +public class ScriptMachineTests +{ + private int _calledTimes; + private readonly Dictionary _receivedStrings = new(); + + public ScriptMachineTests(ITestOutputHelper testOutputHelper) + { + Tools.SetDebugFuncWrite(testOutputHelper.WriteLine); + Tools.SetDebugFuncWriteLine(testOutputHelper.WriteLine); + Tools.DEBUG_ON = true; + + ConcurrentDictionary drawings = new(); + ScriptProperties.AddProperties( + null, + new() + { + { "screenwidth", "The width of the game screen", _ => 1024 }, + { "screenheight", "The height of the game screen", _ => 1024 }, + } + ); + + ScriptProperties.AddFunctions( + null, + new() + { + { + "echo", + "", + EchoCallback + }, + { + "showimg", + "", + (_, args) => + { + if (!(args?.Length > 3)) return 0; + try + { + var index = (int)args[0]!.GetValue(); + string? image = args[1]?.GetValue() ?? string.Empty; + + var x = (int)args[2]!.GetValue(); + var y = (int)args[3]!.GetValue(); + if (drawings.TryGetValue(index, out var value)) + { + value.ShowImg(image, x, y); + } + else + { + value = new(image, x, y); + drawings.AddOrUpdate(index, value, (_, _) => value); + } + } + catch (Exception) + { + //_logger.LogDebug(e.Message); + } + + return 0; + } + }, + { + "findimg", + "", + (_, args) => + { + if (!(args?.Length > 0)) return null; + try + { + var index = (int)args[0]!.GetValue(); + + if (drawings.TryGetValue(index, out var value)) + { + return value; + } + } + catch (Exception) + { + //_logger.LogDebug(e.Message); + } + + return null; + } + }, + { + "getimgwidth", + "", + (_, args) => + { + if (!(args?.Length > 0)) return 0; + try + { + var image = args[0]!.GetValue(); + + if (image != null) Console.WriteLine(image); + + return 1; + + } + catch (Exception) + { + //_logger.LogDebug(e.Message); + } + + return 0; + } + }, + } + ); + + foreach (var property in GlobalProperties.Where(x => !x.Value.Compiled)) + { + property.Value.Compile(); + } + } + + private int EchoCallback(ScriptMachineTests _, IStackEntry[] args) + { + _receivedStrings[_calledTimes] = args[0]?.GetValue()?.ToString()??""; + + Console.WriteLine(_receivedStrings[_calledTimes]); + + _calledTimes++; + + return 0; + } + + private static Script CompileScript(string scriptText) + { + var response = GS2Compiler.Interface.CompileCode( + scriptText, + "weapon", + "testScript" + ); + + if (response.Success) + { + // Arrange + return new("testScript", response.ByteCode); + } + + throw new($"Script failure: {response.ErrMsg}"); + } + + private static Script InitializePrebakedScript(string fileName) => new(fileName); + + [Fact] + public void When_script_is_faulty_Then_exception_is_thrown() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() + } + """; + + + //Act + var result = Assert.Throws(() => CompileScript(scriptText));; + + //Assert + Assert.Equal("Script failure: malformed input at line 3: \t\t\t}\n", result.Message); + } + + private static void RegisterGlobalObject(string name, ScriptVariable collection) => GlobalObjects[name] = collection; + + + [Fact] + public async Task When_calling_built_in_sin_Then_correct_sin_value_is_returned() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + var expectedSin = new List { 1, 0, -1, 0 }; + var sin = new List(); + const string scriptText = + """ + //#CLIENTSIDE + function test(dir) { + temp.angle = (pi/2 * (dir+1)); + + return sin(temp.angle); + } + """; + var script = CompileScript(scriptText); + + //Act + for (var i = 0; i < 4; i++) sin.Add((await script.Call("test", i)).GetValue()); + + //Assert + for (var i = 0; i < 4; i++) Assert.Equal(expectedSin[i], sin[i]); + } + + [Fact] + public async Task When_calling_built_in_cos_Then_correct_cos_value_is_returned() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + var expectedCos = new List { 0, -1, 0, 1 }; + var cos = new List(); + const string scriptText = + """ + //#CLIENTSIDE + function test(dir) { + temp.angle = (pi/2) * (dir+1); + + return cos(temp.angle); + } + """; + var script = CompileScript(scriptText); + + //Act + for (var i = 0; i < 4; i++) cos.Add((await script.Call("test", i)).GetValue()); + + //Assert + for (var i = 0; i < 4; i++) Assert.Equal(expectedCos[i], cos[i]); + } + + [Fact] + public async Task Given_temp_var_When_returning_without_temp_prefix_Then_temp_var_should_be_returned() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() { + temp.var = "test"; + + temp.var2 = var; + + return var2; + } + """; + var script = CompileScript(scriptText); + + //Act + var result = await script.Call("onCreated"); + + //Assert + Assert.Equal("test", result.GetValue()!); + } + + + [Fact] + public async Task Given_function_in_script2_When_calling_public_function_in_script1_Then_value_should_be_returned() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + const string scriptText1 = + """ + //#CLIENTSIDE + public function PubFun() { + temp.var = "PubFun"; + + temp.var2 = var; + + return var2; + } + """; + var script1 = CompileScript(scriptText1); + const string scriptText2 = + """ + //#CLIENTSIDE + function onCreated() { + return (@"script1").PubFun(); + } + """; + var script2 = CompileScript(scriptText2); + + GlobalVariables["script1"] = script1.ToStackEntry(); + + //Act + var result = await script2.Call("onCreated"); + + //Assert + Assert.Equal("PubFun", result.GetValue()!.ToString()); + } + + [Fact] + public async Task Given_this_var3_When_returning_without_this_prefix_Then_0_should_be_returned() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() { + this.var3 = "test2"; + + return var3; + } + """; + var script = CompileScript(scriptText); + + //Act + var result = await script.Call("onCreated"); + + //Assert + Assert.Equal(0d, result.GetValue()!); + } + + [Fact] + public async Task Given_temp_var_When_creating_new_array_object_Then_result_should_be_empty_array_object() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + GlobalVariables.Clear(); + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() { + temp.var = new[2]; + + return temp.var == {0,0}; + } + """; + var script = CompileScript(scriptText); + + //Act + var result = await script.Call("onCreated"); + + //Assert + Assert.Equal(1.0d, result.GetValue()!); + } + + [Fact(Skip = "Waiting for fix in GS2Compiler")] + public async Task Given_temp_update_When_comparing_multiple_or_and_one_variable_is_updated_Then_result_should_be_true() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + GlobalVariables.Clear(); + GlobalObjects.Clear(); + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() { + temp.var1 = 30.5; + temp.var2 = 30; + temp.var3 = 2; + temp.var4 = 0; + temp.var5 = "myvar"; + this.oldData = {var1,var2,var3,var4,var5}; + var3 = -1; + temp.update = var1 != this.oldData[0] || + var2 != this.oldData[1] || + (var3 == -1 && this.oldData[2] >=0); + return temp.update; + } + """; + var script = CompileScript(scriptText); + + //Act + var result = await script.Call("onCreated"); + + //Assert + Assert.True(Convert.ToBoolean(result.GetValue()!)); + } + + [Fact(Skip = "Waiting for fix in GS2Compiler")] + public async Task Given_temp_var_When_at_comparing_Then_result_should_be_true() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + GlobalVariables.Clear(); + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() { + temp.var = {true,true}; + echo(@var); + echo(""@{1,1}); + + return @var == @{1,1}; + } + """; + var script = CompileScript(scriptText); + + //Act + var result = await script.Call("onCreated"); + + //Assert + Assert.Equal(true, result.GetValue()!); + } + + [Fact] + public async Task Given_temp_var3_is_array_object_When_comparing_values_with_another_array_object_with_identical_values_Then_result_should_be_true() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() { + temp.var3 = {1,"asd",3}; + + return temp.var3 == {1,"asd",3}; + } + """; + var script = CompileScript(scriptText); + + //Act + var result = await script.Call("onCreated"); + + //Assert + Assert.Equal(1.0d, result.GetValue()!); + } + + [Fact(Skip = "fix later")] + public async Task When_for_loop_with_items_Then_properly_for_loop_through_items() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() { + temp.i = 0; + temp.sounds = { + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + "text_" @ temp.i++, + }; + + for (temp.sound : temp.sounds) { + echo(temp.sound); + } + + return "done!"; + } + """; + var script = CompileScript(scriptText); + + //Act + var result = (await script.Call("onCreated")).GetValue(); + + //Assert + Assert.Equal("done!", result?.ToString()); + Assert.Equal(15, _calledTimes); + Assert.Equal("text_11", _receivedStrings[3]); + Assert.Equal("text_0", _receivedStrings[14]); + } + + [Fact(Skip = "fix later")] + public async Task When_for_loop_with_8_loops_Then_echo_is_called_8_times() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() { + for(this.i=0;this.i<8;this.i++) { + echo(((this.i==6)?"test2":"test") @ "_text_" @ this.i); + } + } + """; + var script = CompileScript(scriptText); + + //Act + _ = await script.Call("onCreated"); + + //Assert + Assert.Equal("test_text_3", _receivedStrings[3]); + Assert.Equal("test2_text_6", _receivedStrings[6]); + } + + [Fact(Skip = "fix later")] + public async Task When_for_loop_with_8_loops_Then_echo_is_called_8_times_() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() { + test = new GuiControl("test"); + with(test) { + width = 12; + }; + + echo(test.width); + } + """; + var script = CompileScript(scriptText); + + //Act + _ = await script.Call("onCreated"); + + //Assert + Assert.Equal("12", _receivedStrings[0]); + } + + [Fact(Skip = "fix later")] + public async Task When_for_loop_with_8_loops_Then_echo_is_called_8_times__() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() { + echo(""@screenwidth); + } + """; + var script = CompileScript(scriptText); + + //Act + _ = await script.Call("onCreated"); + + //Assert + Assert.Equal("1024", _receivedStrings[0]); + } + + [Fact(Skip = "fix later")] + public async Task When_for_loop_with_8_loops_Then_echo_is_called_8_times___() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() { + echo(1.01+screenwidth); + } + """; + var script = CompileScript(scriptText); + + //Act + _ = await script.Call("onCreated"); + + //Assert + Assert.Equal("1025.01", _receivedStrings[0]); + } + + [Fact(Skip = "fix later")] + public async Task When_for_loop_with_8_loops_Then_echo_is_called_8_times____() + { + //Arrange + _receivedStrings.Clear(); + _calledTimes = 0; + const string scriptText = + """ + //#CLIENTSIDE + function onCreated() { + showimg(1402, "cog2.png", 85, 60); + findimg(1402).rotation = 234; + temp.rot2 = findimg(1402).rotation; + echo(temp.rot2); + } + """; + var script = CompileScript(scriptText); + + //Act + _ = await script.Call("onCreated"); + + //Assert + Assert.Equal("234", _receivedStrings[0]); + } +} \ No newline at end of file diff --git a/GS2Engine.UnitTests/StackEntryExtensionsTests.cs b/Preagonal.Scripting.GS2Engine.UnitTests/StackEntryExtensionsTests.cs similarity index 93% rename from GS2Engine.UnitTests/StackEntryExtensionsTests.cs rename to Preagonal.Scripting.GS2Engine.UnitTests/StackEntryExtensionsTests.cs index 5831817..7397505 100644 --- a/GS2Engine.UnitTests/StackEntryExtensionsTests.cs +++ b/Preagonal.Scripting.GS2Engine.UnitTests/StackEntryExtensionsTests.cs @@ -1,9 +1,9 @@ -using GS2Engine.Enums; -using GS2Engine.Extensions; -using GS2Engine.GS2.Script; -using GS2Engine.Models; +using Preagonal.Scripting.GS2Engine.Enums; +using Preagonal.Scripting.GS2Engine.Extensions; +using Preagonal.Scripting.GS2Engine.GS2.Script; +using Preagonal.Scripting.GS2Engine.Models; -namespace GS2Engine.UnitTests; +namespace Preagonal.Scripting.GS2Engine.UnitTests; public class StackEntryExtensionsTests { @@ -204,13 +204,14 @@ public void When_input_is_bool_Then_return_StackEntry_with_type_bool_and_value_t { //Arrange const bool val = true; + const double expected = 1.0d; //Act var test = val.ToStackEntry(); //Assert Assert.Equal(StackEntryType.Boolean, test.Type); - Assert.Equal(typeof(bool), test.GetValue()?.GetType()); - Assert.Equal(val, test.GetValue()); + Assert.Equal(typeof(double), test.GetValue()?.GetType()); + Assert.Equal(expected, test.GetValue()); } } \ No newline at end of file diff --git a/GS2Engine.UnitTests/StackExtensionsTests.cs b/Preagonal.Scripting.GS2Engine.UnitTests/StackExtensionsTests.cs similarity index 70% rename from GS2Engine.UnitTests/StackExtensionsTests.cs rename to Preagonal.Scripting.GS2Engine.UnitTests/StackExtensionsTests.cs index 02b60ca..d2efa5b 100644 --- a/GS2Engine.UnitTests/StackExtensionsTests.cs +++ b/Preagonal.Scripting.GS2Engine.UnitTests/StackExtensionsTests.cs @@ -1,7 +1,7 @@ -using GS2Engine.Extensions; -using GS2Engine.Models; +using Preagonal.Scripting.GS2Engine.Extensions; +using Preagonal.Scripting.GS2Engine.Models; -namespace GS2Engine.UnitTests; +namespace Preagonal.Scripting.GS2Engine.UnitTests; public class StackExtensionsTests { diff --git a/GS2Engine.UnitTests/TStringTests.cs b/Preagonal.Scripting.GS2Engine.UnitTests/TStringTests.cs similarity index 65% rename from GS2Engine.UnitTests/TStringTests.cs rename to Preagonal.Scripting.GS2Engine.UnitTests/TStringTests.cs index cbc205e..4e6c19b 100644 --- a/GS2Engine.UnitTests/TStringTests.cs +++ b/Preagonal.Scripting.GS2Engine.UnitTests/TStringTests.cs @@ -1,4 +1,4 @@ -namespace GS2Engine.UnitTests; +namespace Preagonal.Scripting.GS2Engine.UnitTests; public class TStringTests { @@ -23,7 +23,7 @@ public void When_input_is_none_Then_return_TString_with_empty_byte_array_as_buff TString stringy = new(); //Assert - Assert.Equal(stringy.buffer, []); + Assert.Empty(stringy.buffer); } [Fact] @@ -55,6 +55,32 @@ public void When_adding_tstring_Then_value_is_correct() Assert.Equal("asd123@", string1.ToLower().ToString()); } + [Fact] + public void When_removing_from_start_of_tstring_Then_value_is_correct() + { + //Arrange + TString string1 = "ASD123"; + + //Act + string1.removeStart(3); + + //Assert + Assert.Equal("123", string1.ToString()); + } + + [Fact] + public void When_using_equals_Then_return_is_correct() + { + //Arrange + TString string1 = "ASD123"; + + //Act + //Assert + Assert.False(string1.Equals("ASD123")); + Assert.True(string1.Equals((TString)"ASD123")); + Assert.False(string1.Equals((TString)"ASD122")); + } + [Fact] public void Given_equal_operator_When_comparing_tstring_Then_value_is_true() { @@ -67,7 +93,6 @@ public void Given_equal_operator_When_comparing_tstring_Then_value_is_true() Assert.True(string1 == string2); } - [Fact] public void Given_equal_operator_When_comparing_tstring_Then_value_is_false() { @@ -80,6 +105,30 @@ public void Given_equal_operator_When_comparing_tstring_Then_value_is_false() Assert.False(string1 == string2); } + [Fact] + public void Given_equal_operator_When_comparing_tstring_with_null_Then_value_is_false() + { + //Arrange + TString string1 = "ASD"; + TString? string2 = null; + + //Act + //Assert + Assert.False(string1 == string2); + } + + [Fact] + public void Given_equal_operator_When_comparing_tstring_with_null_again_Then_value_is_false() + { + //Arrange + TString? string1 = null; + TString? string2 = "ASD"; + + //Act + //Assert + Assert.False(string1 == string2); + } + [Fact] public void Given_not_equal_operator_When_comparing_tstring_Then_value_is_false() { @@ -117,6 +166,22 @@ public void Given_empty_When_tstring_reading_integer_Then_value_is_zero() Assert.Equal(0, result); } + + + [Fact] + public void Given_string_When_getting_hashcode_Then_value_is_always_the_same() + { + //Arrange + TString string1 = "asd123"; + + //Act + var result1 = string1.GetHashCode(); + var result2 = string1.GetHashCode(); + + //Assert + Assert.Equal(result1, result2); + } + [Fact] public void Given_value_When_tstring_starts_with_value_Then_result_is_true() { diff --git a/GS2Engine.UnitTests/Usings.cs b/Preagonal.Scripting.GS2Engine.UnitTests/Usings.cs similarity index 100% rename from GS2Engine.UnitTests/Usings.cs rename to Preagonal.Scripting.GS2Engine.UnitTests/Usings.cs diff --git a/GS2Engine.UnitTests/scripts/prebaked/.gitkeep b/Preagonal.Scripting.GS2Engine.UnitTests/scripts/prebaked/.gitkeep similarity index 100% rename from GS2Engine.UnitTests/scripts/prebaked/.gitkeep rename to Preagonal.Scripting.GS2Engine.UnitTests/scripts/prebaked/.gitkeep diff --git a/GS2Engine.sln b/Preagonal.Scripting.GS2Engine.sln similarity index 72% rename from GS2Engine.sln rename to Preagonal.Scripting.GS2Engine.sln index 60539d6..24be594 100644 --- a/GS2Engine.sln +++ b/Preagonal.Scripting.GS2Engine.sln @@ -1,10 +1,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GS2Engine", "GS2Engine\GS2Engine.csproj", "{E53F10CD-016C-4ABF-8CAD-04BC9922BA5F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Preagonal.Scripting.GS2Engine", "Preagonal.Scripting.GS2Engine\Preagonal.Scripting.GS2Engine.csproj", "{E53F10CD-016C-4ABF-8CAD-04BC9922BA5F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GS2Engine.TestApp", "GS2Engine.TestApp\GS2Engine.TestApp.csproj", "{88A8D75A-3BB8-42FF-BA24-516040873CFA}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Preagonal.Scripting.GS2Engine.TestApp", "Preagonal.Scripting.GS2Engine.TestApp\Preagonal.Scripting.GS2Engine.TestApp.csproj", "{88A8D75A-3BB8-42FF-BA24-516040873CFA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GS2Engine.UnitTests", "GS2Engine.UnitTests\GS2Engine.UnitTests.csproj", "{93FD9556-014B-4D9E-B8F1-D8C4A7731550}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Preagonal.Scripting.GS2Engine.UnitTests", "Preagonal.Scripting.GS2Engine.UnitTests\Preagonal.Scripting.GS2Engine.UnitTests.csproj", "{93FD9556-014B-4D9E-B8F1-D8C4A7731550}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{ABC120E7-56C6-4964-9635-0C1B142DB227}" ProjectSection(SolutionItems) = preProject diff --git a/Preagonal.Scripting.GS2Engine.sln.DotSettings b/Preagonal.Scripting.GS2Engine.sln.DotSettings new file mode 100644 index 0000000..55ffe91 --- /dev/null +++ b/Preagonal.Scripting.GS2Engine.sln.DotSettings @@ -0,0 +1,24 @@ + + False + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True \ No newline at end of file diff --git a/GS2Engine/Enums/ScriptType.cs b/Preagonal.Scripting.GS2Engine/Enums/ScriptType.cs similarity index 64% rename from GS2Engine/Enums/ScriptType.cs rename to Preagonal.Scripting.GS2Engine/Enums/ScriptType.cs index 9a7f3e3..c7a3f9d 100644 --- a/GS2Engine/Enums/ScriptType.cs +++ b/Preagonal.Scripting.GS2Engine/Enums/ScriptType.cs @@ -1,4 +1,4 @@ -namespace GS2Engine.Enums; +namespace Preagonal.Scripting.GS2Engine.Enums; public enum ScriptType { diff --git a/GS2Engine/Enums/StackEntryType.cs b/Preagonal.Scripting.GS2Engine/Enums/StackEntryType.cs similarity index 65% rename from GS2Engine/Enums/StackEntryType.cs rename to Preagonal.Scripting.GS2Engine/Enums/StackEntryType.cs index 49ceba5..ca5944d 100644 --- a/GS2Engine/Enums/StackEntryType.cs +++ b/Preagonal.Scripting.GS2Engine/Enums/StackEntryType.cs @@ -1,4 +1,4 @@ -namespace GS2Engine.Enums; +namespace Preagonal.Scripting.GS2Engine.Enums; public enum StackEntryType { @@ -11,4 +11,5 @@ public enum StackEntryType Player, Function, Script, + ScriptProperty, } \ No newline at end of file diff --git a/GS2Engine/Exceptions/ScriptException.cs b/Preagonal.Scripting.GS2Engine/Exceptions/ScriptException.cs similarity index 70% rename from GS2Engine/Exceptions/ScriptException.cs rename to Preagonal.Scripting.GS2Engine/Exceptions/ScriptException.cs index 59282fc..ea22d0d 100644 --- a/GS2Engine/Exceptions/ScriptException.cs +++ b/Preagonal.Scripting.GS2Engine/Exceptions/ScriptException.cs @@ -1,6 +1,6 @@ using System; -namespace GS2Engine.Exceptions; +namespace Preagonal.Scripting.GS2Engine.Exceptions; public class ScriptException : Exception { diff --git a/GS2Engine/Extensions/StackEntryExtensions.cs b/Preagonal.Scripting.GS2Engine/Extensions/StackEntryExtensions.cs similarity index 79% rename from GS2Engine/Extensions/StackEntryExtensions.cs rename to Preagonal.Scripting.GS2Engine/Extensions/StackEntryExtensions.cs index 308bec3..cd80c44 100644 --- a/GS2Engine/Extensions/StackEntryExtensions.cs +++ b/Preagonal.Scripting.GS2Engine/Extensions/StackEntryExtensions.cs @@ -1,16 +1,16 @@ using System; using System.Collections.Generic; using System.Linq; -using GS2Engine.Enums; -using GS2Engine.GS2.Script; -using GS2Engine.Models; +using Preagonal.Scripting.GS2Engine.Enums; +using Preagonal.Scripting.GS2Engine.GS2.Script; +using Preagonal.Scripting.GS2Engine.Models; -namespace GS2Engine.Extensions; +namespace Preagonal.Scripting.GS2Engine.Extensions; public static class StackEntryExtensions { - public static StackEntry ToStackEntry(this object? stackObject, bool isVariable = false) => - new(isVariable ? StackEntryType.Variable : GetStackEntryType(stackObject), FixStackValue(stackObject)); + public static StackEntry ToStackEntry(this object? stackObject, bool isVariable = false, object? parent = null) => + new(isVariable ? StackEntryType.Variable : GetStackEntryType(stackObject), FixStackValue(stackObject), parent); private static object? FixStackValue(object? stackObject) { @@ -22,14 +22,15 @@ public static StackEntry ToStackEntry(this object? stackObject, bool isVariable double d => d, float f => (double)f, decimal o => (double)o, - bool b => b, + bool b => b?1.0d:0.0d, _ => stackObject, }; } private static StackEntryType GetStackEntryType(object? stackObject) { - switch (Type.GetTypeCode(stackObject?.GetType())) + var stackType = stackObject?.GetType(); + switch (Type.GetTypeCode(stackType)) { case TypeCode.Boolean: return StackEntryType.Boolean; @@ -50,13 +51,16 @@ private static StackEntryType GetStackEntryType(object? stackObject) return StackEntryType.String; default: { - var stackType = stackObject?.GetType(); if (stackType == typeof(TString)) return StackEntryType.String; if (stackType == typeof(Script.Command)) return StackEntryType.Function; + if (stackType is { IsGenericType: true } && stackType.GetGenericTypeDefinition() == typeof(ScriptProperty<>)) + return StackEntryType.ScriptProperty; + + if (stackType == typeof(Script)) return StackEntryType.Script; @@ -74,7 +78,7 @@ private static StackEntryType GetStackEntryType(object? stackObject) stackType.GetGenericTypeDefinition().IsAssignableFrom(typeof(List<>))) return StackEntryType.Array; - throw new ArgumentOutOfRangeException(nameof(stackObject)); + throw new ArgumentOutOfRangeException(nameof(stackType), $"StackType: {stackType}"); } } } diff --git a/GS2Engine/Extensions/StackExtensions.cs b/Preagonal.Scripting.GS2Engine/Extensions/StackExtensions.cs similarity index 83% rename from GS2Engine/Extensions/StackExtensions.cs rename to Preagonal.Scripting.GS2Engine/Extensions/StackExtensions.cs index 9ab9425..4fe7c0a 100644 --- a/GS2Engine/Extensions/StackExtensions.cs +++ b/Preagonal.Scripting.GS2Engine/Extensions/StackExtensions.cs @@ -2,7 +2,7 @@ using System.Diagnostics.Contracts; using System.Linq; -namespace GS2Engine.Extensions; +namespace Preagonal.Scripting.GS2Engine.Extensions; public static class StackExtensions { diff --git a/GS2Engine/GS2/ByteCode/BytecodeSegment.cs b/Preagonal.Scripting.GS2Engine/GS2/ByteCode/BytecodeSegment.cs similarity index 67% rename from GS2Engine/GS2/ByteCode/BytecodeSegment.cs rename to Preagonal.Scripting.GS2Engine/GS2/ByteCode/BytecodeSegment.cs index c99d283..0f31030 100644 --- a/GS2Engine/GS2/ByteCode/BytecodeSegment.cs +++ b/Preagonal.Scripting.GS2Engine/GS2/ByteCode/BytecodeSegment.cs @@ -1,4 +1,4 @@ -namespace GS2Engine.GS2.ByteCode; +namespace Preagonal.Scripting.GS2Engine.GS2.ByteCode; public enum BytecodeSegment { diff --git a/GS2Engine/GS2/ByteCode/Extensions.cs b/Preagonal.Scripting.GS2Engine/GS2/ByteCode/Extensions.cs similarity index 98% rename from GS2Engine/GS2/ByteCode/Extensions.cs rename to Preagonal.Scripting.GS2Engine/GS2/ByteCode/Extensions.cs index 72bb4e6..fe9dd2d 100644 --- a/GS2Engine/GS2/ByteCode/Extensions.cs +++ b/Preagonal.Scripting.GS2Engine/GS2/ByteCode/Extensions.cs @@ -1,4 +1,4 @@ -namespace GS2Engine.GS2.ByteCode; +namespace Preagonal.Scripting.GS2Engine.GS2.ByteCode; public static class Extensions { diff --git a/GS2Engine/GS2/ByteCode/Opcode.cs b/Preagonal.Scripting.GS2Engine/GS2/ByteCode/Opcode.cs similarity index 98% rename from GS2Engine/GS2/ByteCode/Opcode.cs rename to Preagonal.Scripting.GS2Engine/GS2/ByteCode/Opcode.cs index b39002d..6a8c33d 100644 --- a/GS2Engine/GS2/ByteCode/Opcode.cs +++ b/Preagonal.Scripting.GS2Engine/GS2/ByteCode/Opcode.cs @@ -1,4 +1,4 @@ -namespace GS2Engine.GS2.ByteCode; +namespace Preagonal.Scripting.GS2Engine.GS2.ByteCode; public enum Opcode { diff --git a/GS2Engine/GS2/Script/Script.cs b/Preagonal.Scripting.GS2Engine/GS2/Script/Script.cs similarity index 74% rename from GS2Engine/GS2/Script/Script.cs rename to Preagonal.Scripting.GS2Engine/GS2/Script/Script.cs index 8163e6b..79a9f9d 100644 --- a/GS2Engine/GS2/Script/Script.cs +++ b/Preagonal.Scripting.GS2Engine/GS2/Script/Script.cs @@ -6,32 +6,49 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using GS2Engine.Enums; -using GS2Engine.Extensions; -using GS2Engine.GS2.ByteCode; -using GS2Engine.Models; +using Preagonal.Scripting.GS2Engine.Enums; +using Preagonal.Scripting.GS2Engine.Extensions; +using Preagonal.Scripting.GS2Engine.GS2.ByteCode; +using Preagonal.Scripting.GS2Engine.Models; using static System.IO.File; -namespace GS2Engine.GS2.Script; +namespace Preagonal.Scripting.GS2Engine.GS2.Script; -public class Script : VariableCollection +public class Script : ScriptVariable { - public delegate IStackEntry Command(ScriptMachine machine, IStackEntry[]? args); - - public static readonly VariableCollection GlobalVariables = new(); - public static readonly ConcurrentDictionary GlobalObjects = new(); - public static readonly ConcurrentDictionary GlobalFunctions = new(); - - private readonly List _strings = []; - public readonly Dictionary Functions = new(); - private ScriptCom[] _bytecode = []; - - public readonly VariableCollection? RefObject = null; - private bool _executionEnabled = true; + public delegate IStackEntry Command(ScriptMachine machine, IStackEntry[]? args); + public static readonly Dictionary GlobalProperties = []; + public static readonly ScriptVariable GlobalVariables = new(); + public static readonly ConcurrentDictionary GlobalObjects = new(); + public static readonly ConcurrentDictionary GlobalScripts = new(); + public new static readonly ScriptObjProperties PropertiesInstance = []; + public override IScriptProperties Properties => PropertiesInstance; + private readonly List _strings = []; + public readonly Dictionary Functions = new(); + private ScriptCom[] _bytecode = []; + public readonly ScriptVariable? RefObject = null; + public bool ExecutionEnabled { get; private set; } = true; + public TString File { get; set; } + public ScriptType Type { get; } + private int Gs1Flags { get; set; } + public ScriptMachine Machine { get; } + public DateTime? Timer { get; set; } + public ScriptCom[] Bytecode => _bytecode; + + + + public Script(ScriptType type) + { + Name = string.Empty; + File = string.Empty; + RefObject = this; + Machine = new(this); + Type = type; + } public Script( TString bytecodeFile, - VariableCollection? refObject = null, + ScriptVariable? refObject = null, ScriptType? type = null ) { @@ -49,7 +66,7 @@ public Script( public Script( TString name, byte[] bytecode, - VariableCollection? refObject = null, + ScriptVariable? refObject = null, ScriptType? type = null ) { @@ -64,17 +81,11 @@ public Script( Init(); } - private int BytecodeLength => _bytecode.Length; - - public TString Name { get; set; } - public TString File { get; set; } - public ScriptType Type { get; } - private int Gs1Flags { get; set; } - public ScriptMachine Machine { get; } - private DateTime? Timer { get; set; } - - public ScriptCom[] Bytecode => _bytecode; - public Dictionary ExternalFunctions { get; } = new(); + ~Script() + { + if (GlobalScripts.ContainsKey(GetHashCode().ToString())) + GlobalScripts.TryRemove(GetHashCode().ToString(), out _); + } public void UpdateFromFile(string scriptFile) { @@ -94,24 +105,14 @@ public void UpdateFromByteCode(TString name, byte[] byteCode) Init(); } - public void HaltExecution() => _executionEnabled = false; + public void HaltExecution() => ExecutionEnabled = false; - public void EnableExecution() => _executionEnabled = true; + public void EnableExecution() => ExecutionEnabled = true; private void Init() { - foreach (var obj in GlobalFunctions) - ExternalFunctions?.Add(obj.Key, obj.Value); - - ExternalFunctions?.Add( - "settimer", - delegate(ScriptMachine machine, IStackEntry[]? args) - { - if (args?.Length > 0 && _executionEnabled) - SetTimer((double)(machine.GetEntry(args[0]).GetValue() ?? 0)); - return 0.ToStackEntry(); - } - ); + if (!GlobalScripts.ContainsKey(GetHashCode().ToString())) + GlobalScripts.AddOrUpdate(GetHashCode().ToString(), this, (s, script) => GlobalScripts[s] = script); EnableExecution(); } @@ -120,7 +121,6 @@ private void Reset() { Machine.Reset(); Functions.Clear(); - ExternalFunctions?.Clear(); _bytecode = []; _strings.Clear(); Clear(); @@ -186,7 +186,7 @@ private void SetStream(TString bytecodeParam) var isPublic = functionName.starts("public."); if (isPublic) functionName.removeStart(7); - addFunction(functionName, pos, isPublic); + AddFunction(functionName, pos, isPublic); Tools.Debug($"Function[{pos}]: {functionName}\n"); } @@ -291,9 +291,9 @@ private void SetStream(TString bytecodeParam) default: { - if (oIndex >= BytecodeLength) Array.Resize(ref _bytecode, oIndex + 0x100); + if (oIndex >= _bytecode.Length) Array.Resize(ref _bytecode, oIndex + 0x100); //BytecodeLength = oIndex + 0x100; - op = Bytecode[oIndex] = new(); + op = _bytecode[oIndex] = new(); op.OpCode = (Opcode)bytecodeByte; ++oIndex; break; @@ -311,7 +311,7 @@ private void SetStream(TString bytecodeParam) Array.Resize(ref _bytecode, oIndex); - onScriptUpdated(); + OnScriptUpdated(); } private static void CheckHeader(TString bytecodeParam) @@ -365,10 +365,10 @@ private static void CheckHeader(TString bytecodeParam) */ } - private void addFunction(TString functionName, int pos, bool isPublic) => + private void AddFunction(TString functionName, int pos, bool isPublic) => Functions.Add(functionName.ToString().ToLower(), new() { BytecodePosition = pos, IsPublic = isPublic }); - private static void onScriptUpdated() + private static void OnScriptUpdated() { //fixBadByteCode(); //checkOnlyFunctions(); @@ -388,7 +388,8 @@ private async Task Execute(string functionName, Stack? catch (Exception e) { Tools.DebugLine(e.Message); - return 0.ToStackEntry(); + throw; + //return 0.ToStackEntry(); } } @@ -400,10 +401,10 @@ public async Task Call(string eventName, params object[]? args) try { - if (args == null) return await Execute(eventName, null).ConfigureAwait(false); + if (args == null) return await Execute(eventName).ConfigureAwait(false); var callStack = new Stack(); - foreach (var variable in args.Reverse()) + foreach (var variable in args.AsEnumerable().Reverse()) { switch (variable) { @@ -462,10 +463,10 @@ public async Task TriggerEvent(string eventName) return 0.ToStackEntry(); } - private void SetTimer(double value) + public void SetTimer(double value) { Timer = DateTime.UtcNow.AddSeconds(value); - try + /*try { if (!ThreadPool.QueueUserWorkItem( delegate @@ -483,7 +484,7 @@ private void SetTimer(double value) catch (Exception e) { Console.WriteLine($"{e.Message}: {e}"); - } + }*/ } private static void DelayedMethodCall(double seconds, Action methodToCall) @@ -492,13 +493,13 @@ private static void DelayedMethodCall(double seconds, Action methodToCall) methodToCall(); } - private async Task OnTriggerEvent(string eventName) + public async Task OnTriggerEvent(string eventName) { Timer = null; await Execute(eventName).ConfigureAwait(false); } - public void AddObjectReference(string objectType, VariableCollection obj) + public void AddObjectReference(string objectType, ScriptVariable obj) { if (GlobalObjects.ContainsKey(objectType)) GlobalObjects[objectType] = obj; diff --git a/GS2Engine/GS2/Script/ScriptCom.cs b/Preagonal.Scripting.GS2Engine/GS2/Script/ScriptCom.cs similarity index 66% rename from GS2Engine/GS2/Script/ScriptCom.cs rename to Preagonal.Scripting.GS2Engine/GS2/Script/ScriptCom.cs index 657a6f4..3c8f526 100644 --- a/GS2Engine/GS2/Script/ScriptCom.cs +++ b/Preagonal.Scripting.GS2Engine/GS2/Script/ScriptCom.cs @@ -1,6 +1,6 @@ -using GS2Engine.GS2.ByteCode; +using Preagonal.Scripting.GS2Engine.GS2.ByteCode; -namespace GS2Engine.GS2.Script; +namespace Preagonal.Scripting.GS2Engine.GS2.Script; public class ScriptCom { diff --git a/GS2Engine/GS2/Script/ScriptMachine.cs b/Preagonal.Scripting.GS2Engine/GS2/Script/ScriptMachine.cs similarity index 72% rename from GS2Engine/GS2/Script/ScriptMachine.cs rename to Preagonal.Scripting.GS2Engine/GS2/Script/ScriptMachine.cs index 41e814d..4a9a71e 100644 --- a/GS2Engine/GS2/Script/ScriptMachine.cs +++ b/Preagonal.Scripting.GS2Engine/GS2/Script/ScriptMachine.cs @@ -4,19 +4,19 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using GS2Engine.Enums; -using GS2Engine.Exceptions; -using GS2Engine.Extensions; -using GS2Engine.GS2.ByteCode; -using GS2Engine.Models; -using static GS2Engine.Enums.StackEntryType; +using Preagonal.Scripting.GS2Engine.Enums; +using Preagonal.Scripting.GS2Engine.Exceptions; +using Preagonal.Scripting.GS2Engine.Extensions; +using Preagonal.Scripting.GS2Engine.GS2.ByteCode; +using Preagonal.Scripting.GS2Engine.Models; +using static Preagonal.Scripting.GS2Engine.Enums.StackEntryType; -namespace GS2Engine.GS2.Script; +namespace Preagonal.Scripting.GS2Engine.GS2.Script; public class ScriptMachine { - private readonly Script _script; - private readonly VariableCollection _tempVariables = new(); + private readonly Script _script; + private readonly ScriptVariable _tempVariables = new(); private delegate IStackEntry OpcodeHandler(ScriptCom op, ref int index); @@ -38,8 +38,6 @@ private void registerOpcodeHandlers() } - private Dictionary Functions => _script.ExternalFunctions; - public async Task Execute(string functionName, Stack? callStack = null) { if (!_script.Functions.TryGetValue(functionName.ToLower(), out var value)) @@ -167,49 +165,71 @@ public async Task Execute(string functionName, Stack? var callEntry = GetEntry(stack.Pop(), returnStackEntryIfNotFound: true); var cmd = GetEntryValue(callEntry, returnStackEntryIfNotFound: true); var parameters = stack.Clone(); + var callParams = new List(); + while (parameters.Count > 0) + { + var parameterEntry = parameters.Pop(); + var parameterEntryValue = GetEntryValue(parameterEntry); + if (parameterEntryValue != null) + callParams.Add(parameterEntryValue.ToStackEntry()); + } + while (stack.Peek()?.Type != ArrayStart) stack.Pop(); stack.Pop(); var opWithHasFunc = false; if (opWith?.Any() ?? false) opWithHasFunc = opWith?.Peek() - ?.GetValue() + ?.GetValue() ?.ContainsVariable(cmd?.ToString()?.ToLower() ?? string.Empty) ?? false; switch (callEntry.Type) { + /* case StackEntryType.String or Variable when opWithHasFunc: var funcRet = opWith?.Peek() - ?.GetValue() + ?.GetValue() ?.GetVariable(cmd?.ToString()?.ToLower() ?? string.Empty) ?.GetValue() - ?.Invoke(this, parameters.ToArray()) ?? + ?.Invoke(this, callParams.ToArray()) ?? 0.ToStackEntry(); stack.Push(funcRet); break; + */ + case StackEntryType.String or Variable when _script.Functions.ContainsKey(cmd?.ToString()?.ToLower() ?? string.Empty): - stack.Push(await Execute(cmd?.ToString()?.ToLower() ?? string.Empty, parameters).ConfigureAwait(false)); + stack.Push(await Execute(cmd?.ToString()?.ToLower() ?? string.Empty, new(callParams)).ConfigureAwait(false)); break; + + /* case StackEntryType.String or Variable when Functions.TryGetValue( cmd?.ToString()?.ToLower() ?? string.Empty, out var command ): - stack.Push(command.Invoke(this, parameters.ToArray())); + stack.Push(command.Invoke(this, callParams.ToArray())); break; + */ + case StackEntryType.String or Variable: stack.Push(0.ToStackEntry()); break; case Function: stack.Push( - (cmd as Script.Command)?.Invoke(this, parameters.ToArray()) ?? 0.ToStackEntry() + (cmd as Script.Command)?.Invoke(this, callParams.ToArray()) ?? 0.ToStackEntry() ); break; - case StackEntryType.Script: - stack.Push( - (cmd as Script.Command)?.Invoke(this, parameters.ToArray()) ?? 0.ToStackEntry() - ); + case ScriptProperty: + var scriptProperty = cmd as IScriptProperty; + var inst = opWith is { Count: > 0 }? opWith.Peek().GetValue() : null; + + if (scriptProperty?.MainType == typeof(Script)) + { + inst = _script; + } + + stack.Push((scriptProperty?.Call(inst!, callParams.ToArray())??0).ToStackEntry()); break; default: stack.Push(0.ToStackEntry()); @@ -220,7 +240,19 @@ out var command IStackEntry ret = 0.ToStackEntry(); if (stack.Count > 0) ret = stack.Pop(); - return GetEntry(ret); + var retVal = GetEntry(ret); + + if (retVal.Type == ScriptProperty && retVal.GetValue() is {} retScriptProperty) + { + object? inst = null; + + inst = retScriptProperty.MainType == typeof(Script) ? _script : retVal.GetParent(); + + retVal = retScriptProperty.Read(inst!).ToStackEntry(); + } + + return retVal; + case Opcode.OP_SLEEP: var sleep = GetEntryValue(stack.Pop()); @@ -238,20 +270,21 @@ out var command */ op.LoopCount += 1; - index = _indexPos; } else { op.Value = index; op.VariableName = null; - index = _indexPos; } + index = _indexPos; + break; case Opcode.OP_JMP: { //index = indexPos; + Tools.DebugLine(""); } break; @@ -268,10 +301,10 @@ out var command stack.Push(new StackEntry(ArrayStart, null)); break; case Opcode.OP_TYPE_TRUE: - stack.Push(true.ToStackEntry()); + stack.Push(1.ToStackEntry()); break; case Opcode.OP_TYPE_FALSE: - stack.Push(false.ToStackEntry()); + stack.Push(0.ToStackEntry()); break; case Opcode.OP_TYPE_NULL: stack.Push(0.ToStackEntry()); @@ -307,10 +340,14 @@ out var command convToFloatVal = (bool)test ? 1 : 0; else if (test?.GetType() == typeof(double)) convToFloatVal = (double)test; + else if (test is IScriptProperty { HasReadMethod: true } sp2) + { + var inst = opWith is { Count: > 0 }? opWith.Peek().GetValue() : null; + convToFloatVal = Convert.ToDouble(sp2.Read(inst!)); + } else convToFloatVal = 0; - stack.Push(convToFloatVal.ToStackEntry()); break; case Opcode.OP_CONV_TO_STRING: @@ -333,10 +370,18 @@ out var command s.Clear(); convToStringObject = convListResult[..^1]; } + if (convToStringObject is bool boolConvToStringObject) { convToStringObject = (Convert.ToInt32(boolConvToStringObject)).ToString(); } + + if (convToStringObject is IScriptProperty { HasReadMethod: true } sp) + { + var inst = opWith is { Count: > 0 }? opWith.Peek().GetValue() : null; + convToStringObject = sp.Read(inst!); + } + stack.Push(convToStringObject?.ToString().ToStackEntry() ?? "".ToStackEntry()); break; case Opcode.OP_MEMBER_ACCESS: @@ -344,6 +389,36 @@ out var command var memberAccessParam = GetEntryValue(stackVal, StackEntryType.String); try { + if (stack.Peek()?.Type == StackEntryType.Array) + { + try + { + var objTest = stack.Pop().GetValue(); + var props = objTest?.Properties; + var prop = props?.FirstOrDefault(x => x.PropertyName.Equals( + (memberAccessParam ?? "").ToString(), + StringComparison.CurrentCultureIgnoreCase + ) + ); + + if (prop != null) + { + stack.Push(prop.ToStackEntry(parent: objTest)); + } + else + { + var scriptObjectMember = objTest?.GetVariable(memberAccessParam ?? ""); + stack.Push(scriptObjectMember ?? 0.ToStackEntry()); + } + } + catch (Exception e) + { + Tools.DebugLine(e.Message); + } + break; + } + + if (stack.Peek()?.Type == StackEntryType.Script) { var scriptStackEntry = stack.Pop(); @@ -368,8 +443,10 @@ out var command } break; } + + var memberAccessEntry = GetEntry(stack.Pop()); - var memberAccessObject = memberAccessEntry.GetValue(); + var memberAccessObject = memberAccessEntry.GetValue(); var member = memberAccessObject?.GetVariable(memberAccessParam ?? ""); stack.Push(member ?? 0.ToStackEntry()); } @@ -385,7 +462,7 @@ out var command stack.Push( (opWith is { Count: not 0 } ? opWith?.Peek() - ?.GetValue() + ?.GetValue() ?.GetVariable(GetEntryValue(convEntry)?.ToLower() ?? string.Empty) : GetEntry(convEntry, Variable))! ); @@ -413,7 +490,7 @@ out var command stack.Push(newArray.ToStackEntry()); break; case Opcode.OP_SETARRAY: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_INLINE_NEW: if (stack.Count > 0) { @@ -425,13 +502,13 @@ out var command } break; case Opcode.OP_MAKEVAR: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_NEW_OBJECT: var newObject = stack.Pop(); var newObjectParam = stack.Pop(); try { - var newObjectRet = (VariableCollection)GetInstance( + var newObjectRet = (ScriptVariable)GetInstance( GetEntryValue(newObject) ?? string.Empty, GetEntryValue(newObjectParam, returnStackEntryIfNotFound: true)?.ToString()! )!; @@ -447,7 +524,7 @@ out var command case Opcode.OP_INLINE_CONDITIONAL: var inlineConditional = stack.Pop(); - if (inlineConditional.GetValue() != false) { + if (inlineConditional.GetValue() != 0) { inlineConditional.SetValue(1.0d); } stack.Push(inlineConditional); @@ -461,19 +538,37 @@ out var command { try { - opWith?.Peek() - ?.GetValue() - ?.AddOrUpdate((variable?.GetValue() ?? "").ToString()?.ToLower() ?? string.Empty, GetEntry(val)); + var objTest = opWith?.Peek()?.GetValue(); + var props = objTest?.Properties; + var prop = props?.FirstOrDefault(x => x.PropertyName.Equals( + (variable?.GetValue() ?? "").ToString(), + StringComparison.CurrentCultureIgnoreCase + ) + ); + + if (prop != null) + prop?.Write(objTest!, GetEntry(val).GetValue()); + else + objTest?.AddOrUpdate( + (variable?.GetValue() ?? "").ToString()?.ToLower() ?? string.Empty, + GetEntry(val) + ); } catch (Exception e) { Tools.DebugLine(e.Message); } } - else if (variable.Type != Variable /*StackEntryType.String or Number val.Type*/) + else if (variable.Type != Variable && variable.Type != ScriptProperty/*StackEntryType.String or Number val.Type*/) { variable.SetValue(GetEntry(val).GetValue()); } + else if (variable.Type == ScriptProperty /*StackEntryType.String or Number val.Type*/) + { + var scriptProp = variable.GetValue(); + var inst = variable.GetParent(); + scriptProp?.Write(inst!, GetEntry(val).GetValue()); + } else if (!_useTemp) { Script.GlobalVariables.AddOrUpdate((variable.GetValue() ?? "").ToString()?.ToLower() ?? string.Empty, GetEntry(val)); @@ -555,9 +650,12 @@ out var command stack.Push(Math.Pow(powB, powA).ToStackEntry()); break; case Opcode.OP_NOT: + var notVar = GetEntryValue(stack.Pop()); + + stack.Push((notVar == 0?true:false).ToStackEntry()); break; case Opcode.OP_UNARYSUB: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_EQ: var eq1 = GetEntryValue(stack.Pop()); var eq2 = GetEntryValue(stack.Pop()); @@ -593,17 +691,35 @@ out var command stack.Push((gteB >= gteA).ToStackEntry()); break; case Opcode.OP_BWO: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_BWA: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_IN_RANGE: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_IN_OBJ: + var inObj = stack.Pop(); + var isIn = stack.Pop(); + if (inObj.Type == StackEntryType.Array) + { + var inObjArray = inObj.GetValue>(); + + var boolVal = inObjArray?.Contains(isIn?.GetValue()); + + if (!boolVal.HasValue) + { + stack.Push(0.0d.ToStackEntry()); + break; + } + + stack.Push((boolVal.Value?1.0d:0.0d).ToStackEntry()); + } + + stack.Push(0.0d.ToStackEntry()); break; case Opcode.OP_OBJ_INDEX: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_TYPE: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_FORMAT: var format = stack.Pop(); var objects = stack.Select(x => GetEntryValue(x)).ToArray(); @@ -612,12 +728,13 @@ out var command stack.Push(formatted.ToStackEntry()); break; case Opcode.OP_INT: + stack.Push(((int)GetEntryValue(stack.Pop())).ToStackEntry()); break; case Opcode.OP_ABS: stack.Push(Math.Abs(GetEntryValue(stack.Pop())).ToStackEntry()); break; case Opcode.OP_RANDOM: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_SIN: var sinVal = Math.Sin(GetEntryValue(stack.Pop())); sinVal = Math.Abs(sinVal) < 0.000001 ? 0.0f : sinVal; @@ -634,17 +751,17 @@ out var command stack.Push(atanVal.ToStackEntry()); break; case Opcode.OP_EXP: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_LOG: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_MIN: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_MAX: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_GETANGLE: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_GETDIR: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_VECX: var vecxDir = (int)stack.Pop().GetValue(); var vecxVal = (vecxDir % 4) switch @@ -666,25 +783,29 @@ out var command stack.Push(vecyVal.ToStackEntry()); break; case Opcode.OP_OBJ_INDICES: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_LINK: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_CHAR: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_TRIM: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_LENGTH: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_POS: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_JOIN: var joinA = GetEntryValue(stack.Pop()); var joinB = GetEntryValue(stack.Pop()); stack.Push($"{joinB}{joinA}".ToStackEntry()); break; case Opcode.OP_OBJ_CHARAT: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_SUBSTR: + var subStrLen = GetEntryValue(stack.Pop()); + var subStrStart = GetEntryValue(stack.Pop()); + var subStr = GetEntryValue(stack.Pop()); + stack.Push(subStr?.ToString().Substring((int)subStrStart, (int)subStrLen).ToStackEntry()??0.ToStackEntry()); break; case Opcode.OP_OBJ_STARTS: var obj = GetEntryValue(stack.Pop()) ?? ""; @@ -694,17 +815,17 @@ out var command ); break; case Opcode.OP_OBJ_ENDS: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_TOKENIZE: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_TRANSLATE: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_POSITIONS: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_SIZE: var objSizeVar = GetEntry(stack.Pop()).GetValue(); - if (objSizeVar is VariableCollection vc) + if (objSizeVar is ScriptVariable vc) stack.Push(vc.GetDictionary().Count.ToStackEntry()); else if (objSizeVar is List ls) stack.Push(ls.Count.ToStackEntry()); @@ -737,7 +858,7 @@ out var command { var arrAssVal = GetEntry(stack.Pop()); var arrAssIndex = GetEntry(stack.Pop()).GetValue(); - var arrAssObj = GetEntry(stack.Pop()).GetValue(); + var arrAssObj = GetEntry(stack.Pop()).GetValue(); arrAssObj?.AddOrUpdate(((int)arrAssIndex).ToString(), arrAssVal); } catch (Exception e) @@ -747,25 +868,25 @@ out var command break; case Opcode.OP_ARRAY_MULTIDIM: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_ARRAY_MULTIDIM_ASSIGN: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_SUBARRAY: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_ADDSTRING: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_DELETESTRING: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_REMOVESTRING: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_REPLACESTRING: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_INSERTSTRING: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_OBJ_CLEAR: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_ARRAY_NEW_MULTIDIM: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_WITH: opWith?.Push(stack.Pop()); break; @@ -805,23 +926,21 @@ out var command break; case Opcode.OP_PLAYER: stack.Push( - Script.GlobalObjects.TryGetValue("player", out var o) - ? o.ToStackEntry() - : 0.ToStackEntry() + (GetEntryValue("player".ToStackEntry(isVariable:true))?.Read(null!)??0).ToStackEntry() ); break; case Opcode.OP_PLAYERO: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_LEVEL: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_TEMP: stack.Push(new StackEntry(StackEntryType.Array, _tempVariables)); _useTemp = true; break; case Opcode.OP_PARAMS: - break; + break;//throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); case Opcode.OP_NUM_OPS: - break; + throw new NotImplementedException($"OP({op.OpCode:D}): {op.OpCode:G}"); } } @@ -858,6 +977,14 @@ public IStackEntry GetEntry(IStackEntry stackEntry, StackEntryType? overrideStac var foundVariable = false; switch (type) { + case Variable when Script.GlobalProperties[nameof(ScriptUniverse)].Any(x => x.PropertyName.Equals(stackEntry.GetValue()?.ToString(), StringComparison.CurrentCultureIgnoreCase)): + retVal = Script.GlobalProperties[nameof(ScriptUniverse)].First(x => x.PropertyName.Equals(stackEntry.GetValue()?.ToString(), StringComparison.CurrentCultureIgnoreCase)).ToStackEntry(); + foundVariable = true; + break; + case Variable when Script.GlobalProperties[nameof(Script)].Any(x => x.PropertyName.Equals(stackEntry.GetValue()?.ToString(), StringComparison.CurrentCultureIgnoreCase)): + retVal = Script.GlobalProperties[nameof(Script)].First(x => x.PropertyName.Equals(stackEntry.GetValue()?.ToString(), StringComparison.CurrentCultureIgnoreCase)).ToStackEntry(); + foundVariable = true; + break; case Variable when _tempVariables.ContainsVariable(stackEntry.GetValue()?.ToString()?.ToLower() ?? string.Empty): _useTemp = false; @@ -886,6 +1013,15 @@ when Script.GlobalVariables.ContainsVariable( foundVariable = true; break; default: + + + if (stackEntry.GetValue() is IScriptProperty scriptProperty && scriptProperty!.HasReadMethod) + { + retVal = scriptProperty.Read(stackEntry.GetParent()!).ToStackEntry(); + foundVariable = true; + } + + break; } diff --git a/GS2Engine/GS2Engine.Native.csproj b/Preagonal.Scripting.GS2Engine/GS2Engine.Native.csproj similarity index 95% rename from GS2Engine/GS2Engine.Native.csproj rename to Preagonal.Scripting.GS2Engine/GS2Engine.Native.csproj index db30910..33c7993 100644 --- a/GS2Engine/GS2Engine.Native.csproj +++ b/Preagonal.Scripting.GS2Engine/GS2Engine.Native.csproj @@ -22,7 +22,7 @@ - + diff --git a/GS2Engine/Models/FunctionParams.cs b/Preagonal.Scripting.GS2Engine/Models/FunctionParams.cs similarity index 71% rename from GS2Engine/Models/FunctionParams.cs rename to Preagonal.Scripting.GS2Engine/Models/FunctionParams.cs index 30cca2e..f88616d 100644 --- a/GS2Engine/Models/FunctionParams.cs +++ b/Preagonal.Scripting.GS2Engine/Models/FunctionParams.cs @@ -1,4 +1,4 @@ -namespace GS2Engine.Models; +namespace Preagonal.Scripting.GS2Engine.Models; public struct FunctionParams { diff --git a/Preagonal.Scripting.GS2Engine/Models/GuiControl.cs b/Preagonal.Scripting.GS2Engine/Models/GuiControl.cs new file mode 100644 index 0000000..be4083c --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/GuiControl.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Preagonal.Scripting.GS2Engine.Extensions; +using Preagonal.Scripting.GS2Engine.GS2.Script; + +namespace Preagonal.Scripting.GS2Engine.Models; + +[SuppressMessage("ReSharper", "MemberCanBeProtected.Global")] +public class GuiControl : ScriptVariable, IGuiControl, IDisposable +{ + public new static readonly GuiControlProperties PropertiesInstance = []; + public override IScriptProperties Properties => PropertiesInstance; + + protected readonly string Id; + protected readonly Script? Script; + + public GuiControl(string id, Script? script) + { + Script.GlobalVariables.AddOrUpdate(id.ToLower(), this.ToStackEntry()); + Console.WriteLine($"Creating control with ID {id}"); + Id = id; + Script = script; + Active = true; + CanMove = true; + CanResize = true; + ClipMove = false; + ClipChildren = false; + HorizSizing = ""; + VertSizing = ""; + MinSize = ""; + MinExtent = ""; + Hint = ""; + X = -1; + Y = -1; + Width = -1; + Height = -1; + } + + public bool Active { get; set; } + public bool Awake { get; set; } + public bool CanMove { get; set; } + public bool CanResize { get; set; } + public bool ClipChildren { get; set; } + public bool ClipMove { get; set; } + public bool ClipToBounds { get; set; } + public int Cursor { get; set; } + public bool Editing { get; set; } + public IGuiControl? Parent { get; set; } + public bool Flickering { get; set; } + public int FlickerTime { get; set; } + public string Hint { get; set; } + public string HorizSizing { get; set; } + public string VertSizing { get; set; } + public int Layer { get; } + public string MinExtent { get; set; } + public string MinSize { get; set; } + public IGuiControl? Profile { get; set; } + public bool ResizeWidth { get; set; } + public bool ResizeHeight { get; set; } + public int ScrollLineX { get; set; } + public int ScrollLineY { get; set; } + public bool ShowHint { get; set; } + public bool UseOwnProfile { get; set; } + public bool Visible { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public int X { get; set; } + public int Y { get; set; } + + public string Extent + { + get => GetExtentCallback(); + set => SetExtentCallback(value); + } + + public string ClientExtent + { + get => GetClientExtentCallback(); + set => SetClientExtentCallback(value); + } + + public string Position + { + get => GetPositionCallback(); + set => SetPositionCallback(value); + } + + public HashSet Controls { get; } = []; + + public void Dispose() => Active = false; + + public void Destroy() + { + //lock (controls) + { + foreach (var control in Controls) control?.Destroy(); + Controls.Clear(); + } + Dispose(); + } + + public void AddControl(IGuiControl? obj) + { + if (obj == null) return; + obj.Parent = this; + lock (Controls) + { + Controls.Add(obj); + } + } + + // ReSharper disable once UnusedMember.Global + protected void CallAction() => Script?.Call($"{Id}.onAction", []).ConfigureAwait(false).GetAwaiter().GetResult(); + + + public virtual void Draw() + { + //lock (controls) + { + foreach (var control in Controls) control?.Draw(); + } + } + + protected void SetClientExtentCallback(object? posVar) + { + switch (posVar) + { + case List var: + Width = (int)(double)(var[0] ?? "-1"); + Height = (int)(double)(var[1] ?? "-1"); + break; + case TString posVarString: + { + string? positionString = posVarString; + if (positionString?.Length <= 0 || positionString == null) return; + var p = positionString.Split(' '); + + if (double.TryParse(p[0], out var p0)) Width = (int)p0; + if (double.TryParse(p[1], out var p1)) Height = (int)p1; + break; + } + } + } + private string GetClientExtentCallback() => $"{Width} {Height}"; + + protected void SetExtentCallback(object? posVar) + { + switch (posVar) + { + case List var: + Width = (int)(double)(var[0] ?? "-1"); + Height = (int)(double)(var[1] ?? "-1"); + break; + case string posVarString: + { + if (posVarString.Length <= 0) return; + var p = posVarString.Split(' '); + + if (double.TryParse(p[0], out var p0)) Width = (int)p0; + if (double.TryParse(p[1], out var p1)) Height = (int)p1; + break; + } + case TString posVarString: + { + var positionString = posVarString.ToString(); + if (positionString.Length <= 0) return; + var p = positionString.Split(' '); + + if (double.TryParse(p[0], out var p0)) Width = (int)p0; + if (double.TryParse(p[1], out var p1)) Height = (int)p1; + break; + } + } + } + + private string GetExtentCallback() => $"{Width} {Height}"; + + private string GetPositionCallback() => $"{X} {Y}"; + + private void SetPositionCallback(object? posVar) + { + switch (posVar) + { + case List var: + X = (int)(double)(var[0] ?? "-1"); + Y = (int)(double)(var[1] ?? "-1"); + break; + case TString posVarString: + { + string? positionString = posVarString; + if (positionString?.Length <= 0 || positionString == null) return; + var p = positionString.Split(' '); + + if (double.TryParse(p[0], out var p0)) X = (int)p0; + if (double.TryParse(p[1], out var p1)) Y = (int)p1; + break; + } + } + } +} \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/GuiControlProperties.cs b/Preagonal.Scripting.GS2Engine/Models/GuiControlProperties.cs new file mode 100644 index 0000000..e2fc620 --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/GuiControlProperties.cs @@ -0,0 +1,69 @@ +using System.Linq; +using Preagonal.Scripting.GS2Engine.Models.Properties; + +namespace Preagonal.Scripting.GS2Engine.Models; + +public class GuiControlProperties : ScriptProperties +{ + public GuiControlProperties() : base(typeof(ScriptVariable)) + { + var propertyDefinitions = new PropertyDefinitions(); + propertyDefinitions.Add("active", "", control => control.Active, (control, active) => control.Active = active); + propertyDefinitions.Add("canmove", "", control => control.CanMove, (control, canMove) => control.CanMove = canMove); + propertyDefinitions.Add("canresize", "", control => control.CanResize, (control, canResize) => control.CanResize = canResize); + propertyDefinitions.Add("clipchildren", "", control => control.ClipChildren, (control, clipChildren) => control.ClipChildren = clipChildren); + propertyDefinitions.Add("clipmove", "", control => control.ClipMove, (control, clipMove) => control.ClipMove = clipMove); + propertyDefinitions.Add("cliptobounds", "", control => control.ClipToBounds, (control, clipToBounds) => control.ClipToBounds = clipToBounds); + propertyDefinitions.Add("cursor", "", control => control.Cursor, (control, cursor) => control.Cursor = cursor); + propertyDefinitions.Add("editing", "", control => control.Editing, (control, editing) => control.Editing = editing); + propertyDefinitions.Add("extent", "", control => control.Extent, (control, extent) => control.Extent = extent); + propertyDefinitions.Add("parent", "", control => control.Parent, (control, parent) => control.Parent = parent); + propertyDefinitions.Add("flickering", "", control => control.Flickering, (control, flickering) => control.Flickering = flickering); + propertyDefinitions.Add("flickertime", "", control => control.FlickerTime, (control, flickerTime) => control.FlickerTime = flickerTime); + propertyDefinitions.Add("hint", "", control => control.Hint, (control, hint) => control.Hint = hint); + propertyDefinitions.Add("horizsizing", "", control => control.HorizSizing, (control, horizSizing) => control.HorizSizing = horizSizing); + propertyDefinitions.Add("vertsizing", "", control => control.VertSizing, (control, vertSizing) => control.VertSizing = vertSizing); + propertyDefinitions.Add("minextent", "", control => control.MinExtent, (control, minExtent) => control.MinExtent = minExtent); + propertyDefinitions.Add("minsize", "", control => control.MinSize, (control, minSize) => control.MinSize = minSize); + propertyDefinitions.Add("position", "", control => control.Position, (control, position) => control.Position = position); + propertyDefinitions.Add("profile", "", control => control.Profile, (control, profile) => control.Profile = profile); + propertyDefinitions.Add("resizewidth", "", control => control.ResizeWidth, (control, resizeWidth) => control.ResizeWidth = resizeWidth); + propertyDefinitions.Add("resizeheight", "", control => control.ResizeHeight, (control, resizeHeight) => control.ResizeHeight = resizeHeight); + propertyDefinitions.Add("scrolllinex", "", control => control.ScrollLineX, (control, scrollLineX) => control.ScrollLineX = scrollLineX); + propertyDefinitions.Add("scrollliney", "", control => control.ScrollLineY, (control, scrollLineY) => control.ScrollLineY = scrollLineY); + propertyDefinitions.Add("showhint", "", control => control.ShowHint, (control, showHint) => control.ShowHint = showHint); + propertyDefinitions.Add("useownprofile", "", control => control.UseOwnProfile, (control, useOwnProfile) => control.UseOwnProfile = useOwnProfile); + propertyDefinitions.Add("visible", "", control => control.Visible, (control, visible) => control.Visible = visible); + propertyDefinitions.Add("width", "", control => control.Width, (control, width) => control.Width = width); + propertyDefinitions.Add("height", "", control => control.Height, (control, height) => control.Height = height); + propertyDefinitions.Add("x", "", control => control.X, (control, x) => control.X = x); + propertyDefinitions.Add("y", "", control => control.Y, (control, y) => control.Y = y); + propertyDefinitions.Add("clientextent", "", control => control.ClientExtent, (control, clientExtent) => control.ClientExtent = clientExtent); + propertyDefinitions.Add("controls", "", control => control.Controls); + propertyDefinitions.Add("awake", "", control => control.Awake); + + AddProperties(this, propertyDefinitions); + + var functionDefinitions = new FunctionDefinitions(); + functionDefinitions.Add("addcontrol", "", + (control, o2) => + { + var control2 = o2.FirstOrDefault(); + switch (control2) + { + case GuiControl newControl: + control.AddControl(newControl); + break; + case IStackEntry newControlStackEntry: + control.AddControl(newControlStackEntry.GetValue()); + break; + } + return control2; + } + ); + + AddFunctions(this, functionDefinitions); + + Compile(); + } +} \ No newline at end of file diff --git a/GS2Engine/Models/IGuiControl.cs b/Preagonal.Scripting.GS2Engine/Models/IGuiControl.cs similarity index 61% rename from GS2Engine/Models/IGuiControl.cs rename to Preagonal.Scripting.GS2Engine/Models/IGuiControl.cs index 9744c15..0279c6a 100644 --- a/GS2Engine/Models/IGuiControl.cs +++ b/Preagonal.Scripting.GS2Engine/Models/IGuiControl.cs @@ -1,8 +1,8 @@ -namespace GS2Engine.Models; +namespace Preagonal.Scripting.GS2Engine.Models; public interface IGuiControl { - public IGuiControl? parent { get; set; } + public IGuiControl? Parent { get; set; } public void Draw(); void Destroy(); public void AddControl(IGuiControl? obj); diff --git a/Preagonal.Scripting.GS2Engine/Models/IScriptProperties.cs b/Preagonal.Scripting.GS2Engine/Models/IScriptProperties.cs new file mode 100644 index 0000000..8aa37a7 --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/IScriptProperties.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Preagonal.Scripting.GS2Engine.Models; + +public interface IScriptProperties : ICollection +{ + public new void Add(IScriptProperty scriptProperty); + void Compile(); + bool Compiled { get; } +} \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/IScriptProperty.cs b/Preagonal.Scripting.GS2Engine/Models/IScriptProperty.cs new file mode 100644 index 0000000..33eecac --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/IScriptProperty.cs @@ -0,0 +1,19 @@ +using System; + +namespace Preagonal.Scripting.GS2Engine.Models; + +public interface IScriptProperty +{ + public ScriptPropertyType ScriptPropertyType { get; } + public string PropertyName { get; } + public Type MainType { get; } + public Type ReturnType { get; } + public IScriptProperties? Properties { get; } + public bool HasWriteMethod { get; } + public bool HasReadMethod { get; } + public bool IsFunction { get; } + object? Read(object instance); + void Write(object instance, object? value); + object? Call(object instance, params IStackEntry[] arguments); + void SetCallback(CallbackDelegate callback); +} \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/IScriptVariable.cs b/Preagonal.Scripting.GS2Engine/Models/IScriptVariable.cs new file mode 100644 index 0000000..1c6b4b3 --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/IScriptVariable.cs @@ -0,0 +1,6 @@ +namespace Preagonal.Scripting.GS2Engine.Models; + +public interface IScriptVariable +{ + public IScriptProperties Properties { get; } +} \ No newline at end of file diff --git a/GS2Engine/Models/IStackEntry.cs b/Preagonal.Scripting.GS2Engine/Models/IStackEntry.cs similarity index 53% rename from GS2Engine/Models/IStackEntry.cs rename to Preagonal.Scripting.GS2Engine/Models/IStackEntry.cs index e2fa892..451b4e0 100644 --- a/GS2Engine/Models/IStackEntry.cs +++ b/Preagonal.Scripting.GS2Engine/Models/IStackEntry.cs @@ -1,14 +1,13 @@ -using GS2Engine.Enums; +using Preagonal.Scripting.GS2Engine.Enums; -namespace GS2Engine.Models; +namespace Preagonal.Scripting.GS2Engine.Models; public interface IStackEntry { public StackEntryType Type { get; } public object? GetValue(); + public object? GetParent(); public T? GetValue(); bool TryGetValue(out object? value); void SetValue(object? getValue, bool skipCallback = false); - void SetCallback(VariableCollection.VariableCollectionSetCallback setCallback); - void GetCallback(VariableCollection.VariableCollectionGetCallback getCallback); } \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/Properties/FunctionDefinition.cs b/Preagonal.Scripting.GS2Engine/Models/Properties/FunctionDefinition.cs new file mode 100644 index 0000000..b086ae7 --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/Properties/FunctionDefinition.cs @@ -0,0 +1,24 @@ +using System; + +namespace Preagonal.Scripting.GS2Engine.Models.Properties; + +public readonly struct FunctionDefinition(string propertyName, string description, PropertyFunctionDelegate? callTyped = null) : IFunctionDefinition +{ + public string PropertyName { get; init; } = propertyName; + public string Description { get; init; } = description; + private PropertyFunctionDelegate? CallTyped { get; init; } = callTyped; + public Type ReturnType => typeof(TRet); + + object? IFunctionDefinition.Call(TInstance instance, params IStackEntry[] value) + { + if (CallTyped is null) return null; + // You can choose how strict you want this cast to be: + // direct cast if you trust callers: + // WriteTyped(instance, (TRet)value!); + + // or a safer conversion path: + var ret = CallTyped(instance, value); + var v = ret != null ? ret : (TRet)Convert.ChangeType(ret!, typeof(TRet)); + return v; + } +} \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/Properties/FunctionDefinitions.cs b/Preagonal.Scripting.GS2Engine/Models/Properties/FunctionDefinitions.cs new file mode 100644 index 0000000..6bdfcd2 --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/Properties/FunctionDefinitions.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Preagonal.Scripting.GS2Engine.Models.Properties; + +public class FunctionDefinitions : List> where TInstance : class +{ + public void Add( + string propertyName, + string description, + PropertyFunctionDelegate? callTyped = null + ) => + Add(new FunctionDefinition(propertyName, description, callTyped)); +} \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/Properties/IFunctionDefinition.cs b/Preagonal.Scripting.GS2Engine/Models/Properties/IFunctionDefinition.cs new file mode 100644 index 0000000..b7ea95f --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/Properties/IFunctionDefinition.cs @@ -0,0 +1,11 @@ +using System; + +namespace Preagonal.Scripting.GS2Engine.Models.Properties; + +public interface IFunctionDefinition +{ + string PropertyName { get; } + string Description { get; } + Type ReturnType { get; } + object? Call(T instance, params IStackEntry[] arguments); +} \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/Properties/IPropertyDefinition.cs b/Preagonal.Scripting.GS2Engine/Models/Properties/IPropertyDefinition.cs new file mode 100644 index 0000000..624557f --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/Properties/IPropertyDefinition.cs @@ -0,0 +1,13 @@ +using System; + +namespace Preagonal.Scripting.GS2Engine.Models.Properties; + +public interface IPropertyDefinition +{ + string PropertyName { get; } + string Description { get; } + Type ReturnType { get; } + PropertyType PropertyType { get; } + object? Read(T instance); + void Write(T instance, object? value); +} \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyDefinition.cs b/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyDefinition.cs new file mode 100644 index 0000000..595883d --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyDefinition.cs @@ -0,0 +1,36 @@ +using System; + +namespace Preagonal.Scripting.GS2Engine.Models.Properties; + +public readonly struct PropertyDefinition(string propertyName, string description, PropertyReadDelegate? readTyped = null, PropertyWriteDelegate? writeTyped = null, PropertyType propertyType = PropertyType.Default) : IPropertyDefinition +{ + public string PropertyName { get; init; } = propertyName; + public string Description { get; init; } = description; + private PropertyReadDelegate? ReadTyped { get; init; } = readTyped; + private PropertyWriteDelegate? WriteTyped { get; init; } = writeTyped; + public PropertyType PropertyType { get; init; } = propertyType; + public Type ReturnType => typeof(TRet); + + object? IPropertyDefinition.Read(TInstance instance) + => ReadTyped is null ? null : ReadTyped(instance); + + void IPropertyDefinition.Write(TInstance instance, object? value) + { + if (WriteTyped is null) return; + // You can choose how strict you want this cast to be: + // direct cast if you trust callers: + //WriteTyped(instance, (TRet)value!); + + + if (value != null && value.GetType() == typeof(TString) && typeof(TRet) == typeof(string)) + value = value.ToString(); + //else + // throw new ArgumentException($"Value is not convertible, {value?.GetType().FullName}", nameof(value)); + + + // or a safer conversion path: + var v = value is TRet t ? t : (TRet)Convert.ChangeType(value!, typeof(TRet)); + WriteTyped(instance, v); + + } +} \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyDefinitions.cs b/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyDefinitions.cs new file mode 100644 index 0000000..c7c7f91 --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyDefinitions.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Preagonal.Scripting.GS2Engine.Models.Properties; + +public class PropertyDefinitions : List> where TInstance : class +{ + public void Add( + string propertyName, + string description, + PropertyReadDelegate? readTyped = null, + PropertyWriteDelegate? writeTyped = null, + PropertyType propertyType = PropertyType.Default + ) => + Add(new PropertyDefinition(propertyName, description, readTyped, writeTyped, propertyType)); +} \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyFunctionDelegate.cs b/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyFunctionDelegate.cs new file mode 100644 index 0000000..a70aa0e --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyFunctionDelegate.cs @@ -0,0 +1,3 @@ +namespace Preagonal.Scripting.GS2Engine.Models.Properties; + +public delegate TRet? PropertyFunctionDelegate(T o, params IStackEntry[] o2); \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyReadDelegate.cs b/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyReadDelegate.cs new file mode 100644 index 0000000..4cc7d1b --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyReadDelegate.cs @@ -0,0 +1,3 @@ +namespace Preagonal.Scripting.GS2Engine.Models.Properties; + +public delegate TRet PropertyReadDelegate(T o); \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyType.cs b/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyType.cs new file mode 100644 index 0000000..e26cadb --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyType.cs @@ -0,0 +1,9 @@ +namespace Preagonal.Scripting.GS2Engine.Models.Properties; + +public enum PropertyType +{ + Default = 0, + AcceptStringProperty = 1, + JoinedClassesProperty = 2, + AnimationProperty = 3, +}; \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyWriteDelegate.cs b/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyWriteDelegate.cs new file mode 100644 index 0000000..043a8b5 --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/Properties/PropertyWriteDelegate.cs @@ -0,0 +1,3 @@ +namespace Preagonal.Scripting.GS2Engine.Models.Properties; + +public delegate void PropertyWriteDelegate(T o, T2 o2); \ No newline at end of file diff --git a/Preagonal.Scripting.GS2Engine/Models/ScriptObjProperties.cs b/Preagonal.Scripting.GS2Engine/Models/ScriptObjProperties.cs new file mode 100644 index 0000000..60053f8 --- /dev/null +++ b/Preagonal.Scripting.GS2Engine/Models/ScriptObjProperties.cs @@ -0,0 +1,43 @@ +using System.Linq; +using Preagonal.Scripting.GS2Engine.GS2.Script; + +namespace Preagonal.Scripting.GS2Engine.Models; + +public class ScriptObjProperties : ScriptProperties