diff --git a/Capy64/Runtime/Libraries/Event.cs b/Capy64/Runtime/Libraries/Event.cs index 2767438..a9690ea 100644 --- a/Capy64/Runtime/Libraries/Event.cs +++ b/Capy64/Runtime/Libraries/Event.cs @@ -14,6 +14,7 @@ // limitations under the License. using Capy64.API; +using Capy64.Runtime.Objects; using KeraLua; using System; @@ -26,6 +27,8 @@ public class Event : IComponent private static Lua UserQueue; + private static bool FrozenTaskAwaiter = false; + private static IGame _game; public Event(IGame game) { @@ -49,15 +52,36 @@ public class Event : IComponent name = "push", function = L_Push, }, + new() + { + name = "setAwaiter", + function = L_SetTaskAwaiter, + }, + new() + { + name = "fulfill", + function = L_Fulfill, + }, + new() + { + name = "reject", + function = L_Reject, + }, + new() + { + name = "newTask", + function = L_NewTask, + }, new(), }; public void LuaInit(Lua L) { PushQueue = 0; - UserQueue = _game.LuaRuntime.Parent.NewThread(); + FrozenTaskAwaiter = false; + L.RequireF("event", OpenLib, false); } @@ -140,4 +164,86 @@ public class Event : IComponent return 0; } + + private static int L_SetTaskAwaiter(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + L.CheckType(1, LuaType.Function); + + if (FrozenTaskAwaiter) + { + L.Error("awaiter is frozen"); + } + + FrozenTaskAwaiter = L.ToBoolean(2); + + L.GetMetaTable(TaskMeta.ObjectType); + L.GetField(-1, "__index"); + + L.Rotate(1, -1); + L.SetField(-2, "await"); + L.Pop(2); + + return 0; + } + + private static int L_Fulfill(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + var task = TaskMeta.CheckTask(L, false); + L.CheckAny(2); + + if(!task.UserTask) + { + L.Error("attempt to fulfill machine task"); + } + + if (task.Status != TaskMeta.TaskStatus.Running) + { + L.Error("attempt to fulfill a finished task"); + } + + task.Fulfill(lk => + { + L.XMove(lk, 1); + }); + + return 0; + } + + private static int L_Reject(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + var task = TaskMeta.CheckTask(L, false); + var error = L.CheckString(2); + + if (!task.UserTask) + { + L.Error("attempt to reject machine task"); + } + + if(task.Status != TaskMeta.TaskStatus.Running) + { + L.Error("attempt to reject a finished task"); + } + + task.Reject(error); + + return 0; + } + + private static int L_NewTask(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + var name = L.OptString(1, "object"); + + var task = TaskMeta.Push(L, name); + task.UserTask = true; + + return 1; + } } diff --git a/Capy64/Runtime/Objects/Task.cs b/Capy64/Runtime/Objects/Task.cs index 27b2587..fdcc175 100644 --- a/Capy64/Runtime/Objects/Task.cs +++ b/Capy64/Runtime/Objects/Task.cs @@ -56,6 +56,7 @@ public class TaskMeta : IComponent public TaskStatus Status { get; set; } = TaskStatus.Running; public string Error { get; private set; } public int DataIndex { get; private set; } = 0; + public bool UserTask { get; set; } = false; public void Fulfill(Action lk) { @@ -191,12 +192,12 @@ public class TaskMeta : IComponent return task; } - private static RuntimeTask ToTask(Lua L, bool gc = false) + public static RuntimeTask ToTask(Lua L, bool gc = false) { return ObjectManager.ToObject(L, 1, gc); } - private static RuntimeTask CheckTask(Lua L, bool gc = false) + public static RuntimeTask CheckTask(Lua L, bool gc = false) { var obj = ObjectManager.CheckObject(L, 1, ObjectType, gc); if (obj is null) @@ -218,6 +219,8 @@ public class TaskMeta : IComponent { var L = Lua.FromIntPtr(state); + L.Warning("Native task awaiter should be avoided", false); + var task = CheckTask(L, false); if (task.Status == TaskStatus.Succeeded)