From 0e4d3c135ae93c5d87a13bf699f4340e89de4c91 Mon Sep 17 00:00:00 2001 From: Alessandro Proto Date: Mon, 6 Mar 2023 23:01:56 +0100 Subject: [PATCH] Classic mode (#10) --- Capy64/Assets/bios.lua | 19 +- Capy64/Assets/default.json | 4 + Capy64/Capy64.cs | 63 +++- Capy64/Core/ColorPalette.cs | 321 +++++++++++++++++ Capy64/Eventing/Events/TickEvent.cs | 1 + Capy64/IGame.cs | 1 + Capy64/Runtime/Libraries/Event.cs | 2 +- Capy64/Runtime/Libraries/GPU.cs | 148 ++++---- Capy64/Runtime/Libraries/Machine.cs | 17 + Capy64/Runtime/Libraries/Term.cs | 24 +- .../{GPUBuffer.cs => GPUBufferMeta.cs} | 59 ++- Capy64/Runtime/Objects/Task.cs | 335 ++++++++++++++++++ Capy64/Runtime/Objects/WebSocketClient.cs | 4 +- Capy64/Runtime/RuntimeManager.cs | 29 +- 14 files changed, 892 insertions(+), 135 deletions(-) create mode 100644 Capy64/Core/ColorPalette.cs rename Capy64/Runtime/Objects/{GPUBuffer.cs => GPUBufferMeta.cs} (72%) create mode 100644 Capy64/Runtime/Objects/Task.cs diff --git a/Capy64/Assets/bios.lua b/Capy64/Assets/bios.lua index db51d01..0410836 100644 --- a/Capy64/Assets/bios.lua +++ b/Capy64/Assets/bios.lua @@ -35,7 +35,6 @@ term.setBackground(bg) term.clear() term.setSize(53, 20) -gpu.setScale(2) local w, h = term.getSize() @@ -63,13 +62,14 @@ local function drawVendorImage() local w, h = gpu.getSize() local ok, err = pcall(function() - local buffer, width, height = gpu.loadImage("/boot/vendor.bmp") + local task = gpu.loadImageAsync("/boot/vendor.bmp") + local buffer = task:await() local x, y = - math.ceil((w / 2) - (width / 2)), - math.ceil((h / 2) - (height / 2)) + math.ceil((w / 2) - (buffer.width / 2)), + math.ceil((h / 2) - (buffer.height / 2)) - gpu.drawBuffer(buffer, x, y, width, height) + gpu.drawBuffer(buffer, x, y) end) if not ok then @@ -157,11 +157,6 @@ local function installOS() promptKey() end -local function toggleConsole() - local status = getConsole() - setConsole(not status) -end - term.setBlink(false) local function setupScreen() @@ -170,10 +165,6 @@ local function setupScreen() "Open data folder", openDataFolder, }, - { - "Toggle console window", - toggleConsole, - }, { "Install default OS", installOS, diff --git a/Capy64/Assets/default.json b/Capy64/Assets/default.json index 85dca20..6b04894 100644 --- a/Capy64/Assets/default.json +++ b/Capy64/Assets/default.json @@ -1,4 +1,8 @@ { + "EngineMode": 0, + "Window": { + "Scale": 2 + }, "HTTP": { "Enable": true, "Blacklist": [], diff --git a/Capy64/Capy64.cs b/Capy64/Capy64.cs index c824ae8..4b890ac 100644 --- a/Capy64/Capy64.cs +++ b/Capy64/Capy64.cs @@ -20,6 +20,7 @@ using Capy64.Extensions; using Capy64.Integrations; using Capy64.PluginManager; using Capy64.Runtime; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; @@ -33,9 +34,29 @@ using static Capy64.Utils; namespace Capy64; +public enum EngineMode +{ + Classic, + Free +} + + public class Capy64 : Game, IGame { - public const string Version = "0.0.9-alpha"; + public const string Version = "0.0.10-alpha"; + + public static class DefaultParameters + { + public const int Width = 318; + public const int Height = 240; + public const float Scale = 2f; + public const float BorderMultiplier = 1.5f; + public readonly static EngineMode EngineMode = EngineMode.Classic; + + public const int ClassicTickrate = 20; + public const int FreeTickrate = 60; + } + public static string AppDataPath { @@ -56,11 +77,12 @@ public class Capy64 : Game, IGame public static Capy64 Instance { get; private set; } public Capy64 Game => this; + public EngineMode EngineMode { get; private set; } = EngineMode.Classic; public IList NativePlugins { get; private set; } public IList Plugins { get; private set; } - public int Width { get; set; } = 320; - public int Height { get; set; } = 240; - public float Scale { get; set; } = 2f; + public int Width { get; set; } = DefaultParameters.Width; + public int Height { get; set; } = DefaultParameters.Height; + public float Scale { get; set; } = DefaultParameters.Scale; public Drawing Drawing { get; private set; } public Audio Audio { get; private set; } public LuaState LuaRuntime { get; set; } @@ -83,6 +105,7 @@ public class Capy64 : Game, IGame private readonly GraphicsDeviceManager _graphics; private IServiceProvider _serviceProvider; private ulong _totalTicks = 0; + private ulong tickrate = 0; public Capy64() { @@ -103,6 +126,27 @@ public class Capy64 : Game, IGame _serviceProvider = serviceProvider; } + public void SetEngineMode(EngineMode mode) + { + switch (mode) + { + case EngineMode.Classic: + tickrate = DefaultParameters.ClassicTickrate; + Width = DefaultParameters.Width; + Height = DefaultParameters.Height; + Window.AllowUserResizing = false; + ResetBorder(); + + break; + + case EngineMode.Free: + tickrate = DefaultParameters.FreeTickrate; + Window.AllowUserResizing = true; + break; + } + UpdateSize(true); + } + public void UpdateSize(bool resize = true) { if (resize) @@ -157,7 +201,7 @@ public class Capy64 : Game, IGame private void ResetBorder() { - var size = (int)(Scale * 1.5); + var size = (int)(Scale * DefaultParameters.BorderMultiplier); Borders = new Borders { Top = size, @@ -169,8 +213,12 @@ public class Capy64 : Game, IGame protected override void Initialize() { + var configuration = _serviceProvider.GetService(); + Window.Title = "Capy64 " + Version; + Scale = configuration.GetValue("Window:Scale", DefaultParameters.Scale); + ResetBorder(); UpdateSize(); @@ -179,6 +227,8 @@ public class Capy64 : Game, IGame InactiveSleepTime = new TimeSpan(0); + SetEngineMode(configuration.GetValue("EngineMode", DefaultParameters.EngineMode)); + Audio = new Audio(); NativePlugins = GetNativePlugins(); @@ -222,7 +272,8 @@ public class Capy64 : Game, IGame EventEmitter.RaiseTick(new() { GameTime = gameTime, - TotalTicks = _totalTicks + TotalTicks = _totalTicks, + IsActiveTick = _totalTicks % (60 / tickrate) == 0, }); Drawing.End(); diff --git a/Capy64/Core/ColorPalette.cs b/Capy64/Core/ColorPalette.cs new file mode 100644 index 0000000..9f6ba09 --- /dev/null +++ b/Capy64/Core/ColorPalette.cs @@ -0,0 +1,321 @@ +// This file is part of Capy64 - https://github.com/Ale32bit/Capy64 +// Copyright 2023 Alessandro "AlexDevs" Proto +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Capy64.Core; + +public class ColorPalette +{ + public static int GetIndex(int r, int g, int b) + { + r /= 51; + g /= 51; + b /= 51; + return 16 + (36 * r) + (6 * g) + b; + } + + public static int GetIndex(Color color) + { + return GetIndex(color.R, color.G, color.B); + } + + public static int GetIndex(uint packed) + { + var b = (byte)(packed & 0xff); + var g = (byte)((packed >> 8) & 0xff); + var r = (byte)((packed >> 16) & 0xff); + return GetIndex(r, g, b); + } + + public static uint GetColor(int r, int g, int b) + { + return Palette[GetIndex(r, g, b)]; + } + + public static uint GetColor(Color color) + { + return Palette[GetIndex(color)]; + } + + public static uint GetColor(uint packed) + { + return Palette[GetIndex(packed)]; + } + + public static readonly uint[] Palette = new uint[] { + 0x000000, + 0x800000, + 0x008000, + 0x808000, + 0x000080, + 0x800080, + 0x008080, + 0xC0C0C0, + 0x808080, + 0xFF0000, + 0x00FF00, + 0xFFFF00, + 0x0000FF, + 0xFF00FF, + 0x00FFFF, + 0xFFFFFF, + 0x000000, + 0x00005F, + 0x000087, + 0x0000AF, + 0x0000D7, + 0x0000FF, + 0x005F00, + 0x005F5F, + 0x005F87, + 0x005FAF, + 0x005FD7, + 0x005FFF, + 0x008700, + 0x00875F, + 0x008787, + 0x0087AF, + 0x0087D7, + 0x0087FF, + 0x00AF00, + 0x00AF5F, + 0x00AF87, + 0x00AFAF, + 0x00AFD7, + 0x00AFFF, + 0x00D700, + 0x00D75F, + 0x00D787, + 0x00D7AF, + 0x00D7D7, + 0x00D7FF, + 0x00FF00, + 0x00FF5F, + 0x00FF87, + 0x00FFAF, + 0x00FFD7, + 0x00FFFF, + 0x5F0000, + 0x5F005F, + 0x5F0087, + 0x5F00AF, + 0x5F00D7, + 0x5F00FF, + 0x5F5F00, + 0x5F5F5F, + 0x5F5F87, + 0x5F5FAF, + 0x5F5FD7, + 0x5F5FFF, + 0x5F8700, + 0x5F875F, + 0x5F8787, + 0x5F87AF, + 0x5F87D7, + 0x5F87FF, + 0x5FAF00, + 0x5FAF5F, + 0x5FAF87, + 0x5FAFAF, + 0x5FAFD7, + 0x5FAFFF, + 0x5FD700, + 0x5FD75F, + 0x5FD787, + 0x5FD7AF, + 0x5FD7D7, + 0x5FD7FF, + 0x5FFF00, + 0x5FFF5F, + 0x5FFF87, + 0x5FFFAF, + 0x5FFFD7, + 0x5FFFFF, + 0x870000, + 0x87005F, + 0x870087, + 0x8700AF, + 0x8700D7, + 0x8700FF, + 0x875F00, + 0x875F5F, + 0x875F87, + 0x875FAF, + 0x875FD7, + 0x875FFF, + 0x878700, + 0x87875F, + 0x878787, + 0x8787AF, + 0x8787D7, + 0x8787FF, + 0x87AF00, + 0x87AF5F, + 0x87AF87, + 0x87AFAF, + 0x87AFD7, + 0x87AFFF, + 0x87D700, + 0x87D75F, + 0x87D787, + 0x87D7AF, + 0x87D7D7, + 0x87D7FF, + 0x87FF00, + 0x87FF5F, + 0x87FF87, + 0x87FFAF, + 0x87FFD7, + 0x87FFFF, + 0xAF0000, + 0xAF005F, + 0xAF0087, + 0xAF00AF, + 0xAF00D7, + 0xAF00FF, + 0xAF5F00, + 0xAF5F5F, + 0xAF5F87, + 0xAF5FAF, + 0xAF5FD7, + 0xAF5FFF, + 0xAF8700, + 0xAF875F, + 0xAF8787, + 0xAF87AF, + 0xAF87D7, + 0xAF87FF, + 0xAFAF00, + 0xAFAF5F, + 0xAFAF87, + 0xAFAFAF, + 0xAFAFD7, + 0xAFAFFF, + 0xAFD700, + 0xAFD75F, + 0xAFD787, + 0xAFD7AF, + 0xAFD7D7, + 0xAFD7FF, + 0xAFFF00, + 0xAFFF5F, + 0xAFFF87, + 0xAFFFAF, + 0xAFFFD7, + 0xAFFFFF, + 0xD70000, + 0xD7005F, + 0xD70087, + 0xD700AF, + 0xD700D7, + 0xD700FF, + 0xD75F00, + 0xD75F5F, + 0xD75F87, + 0xD75FAF, + 0xD75FD7, + 0xD75FFF, + 0xD78700, + 0xD7875F, + 0xD78787, + 0xD787AF, + 0xD787D7, + 0xD787FF, + 0xD7AF00, + 0xD7AF5F, + 0xD7AF87, + 0xD7AFAF, + 0xD7AFD7, + 0xD7AFFF, + 0xD7D700, + 0xD7D75F, + 0xD7D787, + 0xD7D7AF, + 0xD7D7D7, + 0xD7D7FF, + 0xD7FF00, + 0xD7FF5F, + 0xD7FF87, + 0xD7FFAF, + 0xD7FFD7, + 0xD7FFFF, + 0xFF0000, + 0xFF005F, + 0xFF0087, + 0xFF00AF, + 0xFF00D7, + 0xFF00FF, + 0xFF5F00, + 0xFF5F5F, + 0xFF5F87, + 0xFF5FAF, + 0xFF5FD7, + 0xFF5FFF, + 0xFF8700, + 0xFF875F, + 0xFF8787, + 0xFF87AF, + 0xFF87D7, + 0xFF87FF, + 0xFFAF00, + 0xFFAF5F, + 0xFFAF87, + 0xFFAFAF, + 0xFFAFD7, + 0xFFAFFF, + 0xFFD700, + 0xFFD75F, + 0xFFD787, + 0xFFD7AF, + 0xFFD7D7, + 0xFFD7FF, + 0xFFFF00, + 0xFFFF5F, + 0xFFFF87, + 0xFFFFAF, + 0xFFFFD7, + 0xFFFFFF, + 0x080808, + 0x121212, + 0x1C1C1C, + 0x262626, + 0x303030, + 0x3A3A3A, + 0x444444, + 0x4E4E4E, + 0x585858, + 0x626262, + 0x6C6C6C, + 0x767676, + 0x808080, + 0x8A8A8A, + 0x949494, + 0x9E9E9E, + 0xA8A8A8, + 0xB2B2B2, + 0xBCBCBC, + 0xC6C6C6, + 0xD0D0D0, + 0xDADADA, + 0xE4E4E4, + 0xEEEEEE, + }; +} diff --git a/Capy64/Eventing/Events/TickEvent.cs b/Capy64/Eventing/Events/TickEvent.cs index a554f76..b51d881 100644 --- a/Capy64/Eventing/Events/TickEvent.cs +++ b/Capy64/Eventing/Events/TickEvent.cs @@ -22,4 +22,5 @@ public class TickEvent : EventArgs { public GameTime GameTime { get; set; } public ulong TotalTicks { get; set; } + public bool IsActiveTick { get; set; } } diff --git a/Capy64/IGame.cs b/Capy64/IGame.cs index 9a63019..c7c878b 100644 --- a/Capy64/IGame.cs +++ b/Capy64/IGame.cs @@ -26,6 +26,7 @@ namespace Capy64; public interface IGame { Capy64 Game { get; } + EngineMode EngineMode { get; } IList NativePlugins { get; } IList Plugins { get; } GameWindow Window { get; } diff --git a/Capy64/Runtime/Libraries/Event.cs b/Capy64/Runtime/Libraries/Event.cs index 02f3514..5cdc888 100644 --- a/Capy64/Runtime/Libraries/Event.cs +++ b/Capy64/Runtime/Libraries/Event.cs @@ -73,7 +73,7 @@ public class Event : IComponent return nargs; } - private static int L_Pull(IntPtr state) + public static int L_Pull(IntPtr state) { var L = Lua.FromIntPtr(state); diff --git a/Capy64/Runtime/Libraries/GPU.cs b/Capy64/Runtime/Libraries/GPU.cs index 2130342..0d6117e 100644 --- a/Capy64/Runtime/Libraries/GPU.cs +++ b/Capy64/Runtime/Libraries/GPU.cs @@ -14,13 +14,16 @@ // limitations under the License. using Capy64.API; +using Capy64.Core; using Capy64.Runtime.Objects; using KeraLua; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; using System; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; namespace Capy64.Runtime.Libraries; @@ -45,16 +48,6 @@ public class GPU : IComponent function = L_SetSize, }, new() - { - name = "getScale", - function = L_GetScale, - }, - new() - { - name = "setScale", - function = L_SetScale, - }, - new() { name = "getPixel", function = L_GetPixel, @@ -131,8 +124,8 @@ public class GPU : IComponent }, new() { - name = "loadImage", - function = L_LoadImage, + name = "loadImageAsync", + function = L_LoadImageAsync, }, new() { @@ -153,6 +146,14 @@ public class GPU : IComponent l.NewLib(gpuLib); return 1; } + + public static void GetColor(uint c, out byte r, out byte g, out byte b) + { + if (_game.EngineMode == EngineMode.Classic) + c = ColorPalette.GetColor(c); + Utils.UnpackRGB(c, out r, out g, out b); + } + private static int L_GetSize(IntPtr state) { var L = Lua.FromIntPtr(state); @@ -167,6 +168,12 @@ public class GPU : IComponent { var L = Lua.FromIntPtr(state); + if (_game.EngineMode == EngineMode.Classic) + { + L.PushBoolean(false); + return 1; + } + var w = L.CheckInteger(1); var h = L.CheckInteger(2); @@ -175,31 +182,11 @@ public class GPU : IComponent _game.UpdateSize(); - return 0; - } - - private static int L_GetScale(IntPtr state) - { - var L = Lua.FromIntPtr(state); - - L.PushNumber(_game.Scale); + L.PushBoolean(true); return 1; } - private static int L_SetScale(IntPtr state) - { - var L = Lua.FromIntPtr(state); - - var s = L.CheckNumber(1); - - _game.Scale = (float)s; - - _game.UpdateSize(); - - return 0; - } - private static int L_GetPixel(IntPtr state) { var L = Lua.FromIntPtr(state); @@ -222,7 +209,7 @@ public class GPU : IComponent var y = (int)L.CheckNumber(2) - 1; var c = L.CheckInteger(3); - Utils.UnpackRGB((uint)c, out var r, out var g, out var b); + GetColor((uint)c, out var r, out var g, out var b); _game.Drawing.Plot(new Point(x, y), new Color(r, g, b)); return 0; @@ -257,7 +244,7 @@ public class GPU : IComponent pts.Add(new Point(x, y)); } - Utils.UnpackRGB((uint)c, out var r, out var g, out var b); + GetColor((uint)c, out var r, out var g, out var b); _game.Drawing.Plot(pts, new(r, g, b)); return 0; @@ -272,7 +259,7 @@ public class GPU : IComponent var c = L.CheckInteger(3); var s = (int)L.OptNumber(4, 1); - Utils.UnpackRGB((uint)c, out var r, out var g, out var b); + GetColor((uint)c, out var r, out var g, out var b); _game.Drawing.DrawPoint(new(x, y), new Color(r, g, b), s); return 0; @@ -289,7 +276,7 @@ public class GPU : IComponent var t = (int)L.OptNumber(5, 1); var s = (int)L.OptInteger(6, -1); - Utils.UnpackRGB((uint)c, out var r, out var g, out var b); + GetColor((uint)c, out var r, out var g, out var b); _game.Drawing.DrawCircle(new(x, y), rad, new Color(r, g, b), t, s); return 0; @@ -306,7 +293,7 @@ public class GPU : IComponent var c = L.CheckInteger(5); var s = (int)L.OptNumber(6, 1); - Utils.UnpackRGB((uint)c, out var r, out var g, out var b); + GetColor((uint)c, out var r, out var g, out var b); _game.Drawing.DrawLine(new(x1, y1), new(x2, y2), new Color(r, g, b), s); return 0; @@ -323,7 +310,7 @@ public class GPU : IComponent var c = L.CheckInteger(5); var s = (int)L.OptNumber(6, 1); - Utils.UnpackRGB((uint)c, out var r, out var g, out var b); + GetColor((uint)c, out var r, out var g, out var b); _game.Drawing.DrawRectangle(new(x, y), new(w, h), new Color(r, g, b), s); return 0; @@ -359,7 +346,7 @@ public class GPU : IComponent pts.Add(new(xp, yp)); } - Utils.UnpackRGB((uint)c, out var r, out var g, out var b); + GetColor((uint)c, out var r, out var g, out var b); _game.Drawing.DrawPolygon(new(x, y), pts.ToArray(), new(r, g, b), s); return 0; @@ -376,7 +363,7 @@ public class GPU : IComponent var c = L.CheckInteger(5); var s = (int)L.OptNumber(6, 1); - Utils.UnpackRGB((uint)c, out var r, out var g, out var b); + GetColor((uint)c, out var r, out var g, out var b); _game.Drawing.DrawEllipse(new(x, y), new(rx, ry), new Color(r, g, b), s); return 0; @@ -391,7 +378,7 @@ public class GPU : IComponent var c = L.CheckInteger(3); var t = L.CheckString(4); - Utils.UnpackRGB((uint)c, out var r, out var g, out var b); + GetColor((uint)c, out var r, out var g, out var b); try { _game.Drawing.DrawString(new Vector2(x, y), t, new Color(r, g, b)); @@ -426,8 +413,7 @@ public class GPU : IComponent _game.Drawing.Canvas.GetData(buffer); ObjectManager.PushObject(L, buffer); - //L.PushObject(buffer); - L.SetMetaTable(GPUBuffer.ObjectType); + L.SetMetaTable(GPUBufferMeta.ObjectType); return 1; } @@ -436,9 +422,9 @@ public class GPU : IComponent { var L = Lua.FromIntPtr(state); - var buffer = GPUBuffer.CheckBuffer(L, false); + var buffer = GPUBufferMeta.CheckBuffer(L, false); - _game.Drawing.Canvas.SetData(buffer); + _game.Drawing.Canvas.SetData(buffer.Buffer); return 0; } @@ -453,7 +439,7 @@ public class GPU : IComponent var buffer = new uint[width * height]; ObjectManager.PushObject(L, buffer); - L.SetMetaTable(GPUBuffer.ObjectType); + L.SetMetaTable(GPUBufferMeta.ObjectType); return 1; } @@ -462,30 +448,23 @@ public class GPU : IComponent { var L = Lua.FromIntPtr(state); - var buffer = GPUBuffer.CheckBuffer(L, false); + var buffer = GPUBufferMeta.CheckBuffer(L, false); var x = (int)L.CheckInteger(2) - 1; var y = (int)L.CheckInteger(3) - 1; - var w = (int)L.CheckInteger(4); - var h = (int)L.CheckInteger(5); - if (w * h != buffer.Length) - { - L.Error("width and height do not match buffer size"); - } - - _game.Drawing.DrawBuffer(buffer, new() + _game.Drawing.DrawBuffer(buffer.Buffer, new() { X = x, Y = y, - Width = w, - Height = h, + Width = buffer.Width, + Height = buffer.Height, }); return 0; } - private static int L_LoadImage(IntPtr state) + private static int L_LoadImageAsync(IntPtr state) { var L = Lua.FromIntPtr(state); @@ -499,6 +478,8 @@ public class GPU : IComponent return 0; } + var task = TaskMeta.Push(L, GPUBufferMeta.ObjectType); + Texture2D texture; try { @@ -506,21 +487,52 @@ public class GPU : IComponent } catch (Exception e) { - L.Error(e.Message); - return 0; + task.Reject(e.Message); + return 1; } var data = new uint[texture.Width * texture.Height]; texture.GetData(data); - ObjectManager.PushObject(L, data); - L.SetMetaTable(GPUBuffer.ObjectType); - L.PushInteger(texture.Width); - L.PushInteger(texture.Height); + Task.Run(() => + { + if (_game.EngineMode == EngineMode.Classic) + { + for (int i = 0; i < data.Length; i++) + { + var value = data[i]; - texture.Dispose(); + // ABGR to RGB + value = + ((value & 0x00_00_00_FFU) << 16) | // move R + (value & 0x00_00_FF_00U) | // move G + ((value & 0x00_FF_00_00U) >> 16); // move B - return 3; + value = ColorPalette.GetColor(value); + + // RGB to ABGR + value = + ((value & 0x00_FF_00_00U) >> 16) | // move R + (value & 0x00_00_FF_00U) | // move G + ((value & 0x00_00_00_FFU) << 16) | // move B + 0xFF_00_00_00U; + + data[i] = value; + } + } + + var buffer = new GPUBufferMeta.GPUBuffer + { + Buffer = data, + Height = texture.Height, + Width = texture.Width, + }; + task.Fulfill(buffer); + + texture.Dispose(); + }); + + return 1; } private static int L_Clear(IntPtr state) @@ -529,7 +541,7 @@ public class GPU : IComponent var c = L.OptInteger(1, 0x000000); - Utils.UnpackRGB((uint)c, out var r, out var g, out var b); + GetColor((uint)c, out var r, out var g, out var b); _game.Drawing.Clear(new Color(r, g, b)); return 0; diff --git a/Capy64/Runtime/Libraries/Machine.cs b/Capy64/Runtime/Libraries/Machine.cs index 6f8341e..210368c 100644 --- a/Capy64/Runtime/Libraries/Machine.cs +++ b/Capy64/Runtime/Libraries/Machine.cs @@ -72,6 +72,11 @@ public class Machine : IComponent name = "getClipboard", function = L_GetClipboard, }, + new() + { + name = "task", + function = L_Task, + }, new(), }; @@ -87,6 +92,18 @@ public class Machine : IComponent return 1; } + private static int L_Task(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + var task = new Objects.TaskMeta.RuntimeTask("test"); + + ObjectManager.PushObject(L, task); + L.SetMetaTable(Objects.TaskMeta.ObjectType); + + return 1; + } + private static int L_Shutdown(IntPtr _) { RuntimeManager.Shutdown(); diff --git a/Capy64/Runtime/Libraries/Term.cs b/Capy64/Runtime/Libraries/Term.cs index 1d42d78..fa2824e 100644 --- a/Capy64/Runtime/Libraries/Term.cs +++ b/Capy64/Runtime/Libraries/Term.cs @@ -14,6 +14,7 @@ // limitations under the License. using Capy64.API; +using Capy64.Core; using Capy64.Eventing.Events; using KeraLua; using Microsoft.Xna.Framework; @@ -174,6 +175,13 @@ internal class Term : IComponent return 1; } + public static void GetColor(uint c, out byte r, out byte g, out byte b) + { + if (_game.EngineMode == EngineMode.Classic) + c = ColorPalette.GetColor(c); + UnpackRGB(c, out r, out g, out b); + } + public static void UpdateSize(bool resize = true) { Array.Resize(ref CharGrid, Width * Height); @@ -388,6 +396,12 @@ internal class Term : IComponent { var L = Lua.FromIntPtr(state); + if(_game.EngineMode == EngineMode.Classic) + { + L.PushBoolean(false); + return 1; + } + var w = (int)L.CheckNumber(1); var h = (int)L.CheckNumber(2); @@ -402,7 +416,8 @@ internal class Term : IComponent SetSize(w, h); - return 0; + L.PushBoolean(true); + return 1; } private static int L_GetForegroundColor(IntPtr state) @@ -427,12 +442,14 @@ internal class Term : IComponent r = (byte)L.CheckNumber(1); g = (byte)L.CheckNumber(2); b = (byte)L.CheckNumber(3); + UnpackRGB(ColorPalette.GetColor(r, g, b), out r, out g, out b); + } // packed RGB value else if (argsn == 1) { var c = (uint)L.CheckInteger(1); - UnpackRGB(c, out r, out g, out b); + GetColor(c, out r, out g, out b); } else { @@ -467,12 +484,13 @@ internal class Term : IComponent r = (byte)L.CheckNumber(1); g = (byte)L.CheckNumber(2); b = (byte)L.CheckNumber(3); + UnpackRGB(ColorPalette.GetColor(r, g, b), out r, out g, out b); } // packed RGB value else if (argsn == 1) { var c = (uint)L.CheckInteger(1); - UnpackRGB(c, out r, out g, out b); + GetColor(c, out r, out g, out b); } else { diff --git a/Capy64/Runtime/Objects/GPUBuffer.cs b/Capy64/Runtime/Objects/GPUBufferMeta.cs similarity index 72% rename from Capy64/Runtime/Objects/GPUBuffer.cs rename to Capy64/Runtime/Objects/GPUBufferMeta.cs index 7b5540f..f4b0dd2 100644 --- a/Capy64/Runtime/Objects/GPUBuffer.cs +++ b/Capy64/Runtime/Objects/GPUBufferMeta.cs @@ -14,15 +14,23 @@ // limitations under the License. using Capy64.API; +using Capy64.Core; using KeraLua; using System; namespace Capy64.Runtime.Objects; -public class GPUBuffer : IComponent +public class GPUBufferMeta : IComponent { public const string ObjectType = "GPUBuffer"; + public struct GPUBuffer + { + public uint[] Buffer { get; set; } + public int Width { get; set; } + public int Height { get; set; } + } + private static LuaRegister[] MetaMethods = new LuaRegister[] { new() @@ -59,29 +67,43 @@ public class GPUBuffer : IComponent new(), }; + private static IGame _game; + public GPUBufferMeta(IGame game) + { + _game = game; + } + public void LuaInit(Lua L) { CreateMeta(L); } + public static uint GetColor(uint color) + { + if (_game.EngineMode == EngineMode.Classic) + return ColorPalette.GetColor(color); + + return color; + } + public static void CreateMeta(Lua L) { L.NewMetaTable(ObjectType); L.SetFuncs(MetaMethods, 0); } - public static uint[] ToBuffer(Lua L, bool gc = false) + public static GPUBuffer ToBuffer(Lua L, bool gc = false) { - return ObjectManager.ToObject(L, 1, gc); + return ObjectManager.ToObject(L, 1, gc); } - public static uint[] CheckBuffer(Lua L, bool gc = false) + public static GPUBuffer CheckBuffer(Lua L, bool gc = false) { - var obj = ObjectManager.CheckObject(L, 1, ObjectType, gc); - if (obj is null) + var obj = ObjectManager.CheckObject(L, 1, ObjectType, gc); + if (obj.Buffer is null) { L.Error("attempt to use a closed buffer"); - return null; + return default; } return obj; } @@ -94,19 +116,27 @@ public class GPUBuffer : IComponent if (!L.IsInteger(2)) { - L.PushNil(); + var vkey = L.ToString(2); + + if (vkey == "width") + L.PushInteger(buffer.Width); + else if (vkey == "height") + L.PushInteger(buffer.Height); + else + L.PushNil(); + return 1; } var key = L.ToInteger(2); - if (key < 0 || key >= buffer.Length) + if (key < 0 || key >= buffer.Buffer.Length) { L.PushNil(); return 1; } - var value = buffer[key]; + var value = buffer.Buffer[key]; // ABGR to RGB value = @@ -131,7 +161,7 @@ public class GPUBuffer : IComponent var key = L.ToInteger(2); - if (key < 0 || key >= buffer.Length) + if (key < 0 || key >= buffer.Buffer.Length) { return 0; } @@ -142,6 +172,7 @@ public class GPUBuffer : IComponent } var value = (uint)L.ToInteger(3); + value = GetColor(value); // RGB to ABGR value = @@ -151,7 +182,7 @@ public class GPUBuffer : IComponent 0xFF_00_00_00U; - buffer[key] = value; + buffer.Buffer[key] = value; return 0; } @@ -171,7 +202,7 @@ public class GPUBuffer : IComponent var buffer = CheckBuffer(L, false); - L.PushInteger(buffer.LongLength); + L.PushInteger(buffer.Buffer.LongLength); return 1; } @@ -180,7 +211,7 @@ public class GPUBuffer : IComponent { var L = Lua.FromIntPtr(state); var buffer = ToBuffer(L); - if (buffer is not null) + if (buffer.Buffer is not null) { L.PushString("GPUBuffer ({0:X})", (ulong)&buffer); } diff --git a/Capy64/Runtime/Objects/Task.cs b/Capy64/Runtime/Objects/Task.cs new file mode 100644 index 0000000..ad87885 --- /dev/null +++ b/Capy64/Runtime/Objects/Task.cs @@ -0,0 +1,335 @@ +// This file is part of Capy64 - https://github.com/Ale32bit/Capy64 +// Copyright 2023 Alessandro "AlexDevs" Proto +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Capy64.API; +using Cyotek.Drawing.BitmapFont; +using KeraLua; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Capy64.Runtime.Objects; + +public class TaskMeta : IComponent +{ + private static IGame _game; + public TaskMeta(IGame game) + { + _game = game; + } + + public const string ObjectType = "Task"; + + public enum TaskStatus + { + Running, + Succeeded, + Failed, + } + public class RuntimeTask + { + public RuntimeTask(string typeName) + { + TypeName = typeName; + } + public Guid Guid { get; set; } = Guid.NewGuid(); + public string TypeName { get; set; } + public TaskStatus Status { get; set; } = TaskStatus.Running; + public object Result { get; private set; } + public string Error { get; private set; } + + public void Fulfill(T obj) + { + Status = TaskStatus.Succeeded; + + Result = obj; + + _game.LuaRuntime.QueueEvent("task_finish", LK => + { + LK.PushString(Guid.ToString()); + + ObjectManager.PushObject(LK, obj); + LK.SetMetaTable(TypeName); + + LK.PushNil(); + + return 3; + }); + } + + public void Fulfill(Action lk) + { + Status = TaskStatus.Succeeded; + + Result = lk; + + _game.LuaRuntime.QueueEvent("task_finish", LK => + { + LK.PushString(Guid.ToString()); + + lk(LK); + + LK.PushNil(); + + return 3; + }); + } + + public void Reject(string error, params object[] args) + { + Status = TaskStatus.Failed; + Error = string.Format(error, args); + + _game.LuaRuntime.QueueEvent("task_finish", LK => + { + LK.PushString(Guid.ToString()); + + LK.PushNil(); + + LK.PushString(Error); + + return 3; + }); + } + } + + private static LuaRegister[] Methods = new LuaRegister[] + { + new() + { + name = "await", + function = L_Await, + }, + new() + { + name = "getID", + function = L_GetID, + }, + new() + { + name = "getType", + function = L_GetType, + }, + new() + { + name = "getStatus", + function = L_GetStatus, + }, + new() + { + name = "getResult", + function = L_GetResult, + }, + new() + { + name = "getError", + function = L_GetResult, + }, + new(), + }; + + private static LuaRegister[] MetaMethods = new LuaRegister[] + { + new() + { + name = "__index", + }, + new() + { + name = "__gc", + function = LM_GC, + }, + new() + { + name = "__close", + function = LM_GC, + }, + new() + { + name = "__tostring", + function = LM_ToString, + }, + + new(), + }; + + public void LuaInit(Lua L) + { + CreateMeta(L); + } + + public static void CreateMeta(Lua L) + { + L.NewMetaTable(ObjectType); + L.SetFuncs(MetaMethods, 0); + L.NewLibTable(Methods); + L.SetFuncs(Methods, 0); + L.SetField(-2, "__index"); + L.Pop(1); + } + + public static RuntimeTask Push(Lua L, string typeName) + { + var task = new RuntimeTask(typeName); + + ObjectManager.PushObject(L, task); + L.SetMetaTable(ObjectType); + + return task; + } + + private static RuntimeTask ToTask(Lua L, bool gc = false) + { + return ObjectManager.ToObject(L, 1, gc); + } + + private static RuntimeTask CheckTask(Lua L, bool gc = false) + { + var obj = ObjectManager.CheckObject(L, 1, ObjectType, gc); + if (obj is null) + { + L.Error("attempt to use a closed file"); + return null; + } + return obj; + } + + private static void WaitForTask(Lua L) + { + L.PushCFunction(Libraries.Event.L_Pull); + L.PushString("task_finish"); + L.CallK(1, 4, 0, LK_Await); + } + + private static int L_Await(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + WaitForTask(L); + + return 0; + } + + private static int LK_Await(IntPtr state, int status, nint ctx) + { + var L = Lua.FromIntPtr(state); + var task = CheckTask(L, false); + var taskId = L.CheckString(3); + + if (task.Guid.ToString() != taskId) + { + WaitForTask(L); + return 0; + } + + return 2; + } + + private static int L_GetID(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + var task = CheckTask(L, false); + + L.PushString(task.Guid.ToString()); + + return 1; + } + + private static int L_GetType(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + var task = CheckTask(L, false); + + L.PushString(task.TypeName); + + return 1; + } + + private static int L_GetStatus(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + var task = CheckTask(L, false); + + L.PushString(task.Status.ToString().ToLower()); + + return 1; + } + + private static int L_GetResult(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + var task = CheckTask(L, false); + + if (task.Status == TaskStatus.Succeeded) + { + if (task.Result is Action lk) + { + // todo: make use of first-generated data + lk(L); + } + else + { + ObjectManager.PushObject(L, task.Result); + L.SetMetaTable(task.TypeName); + } + } + else + { + L.PushNil(); + } + + return 1; + } + + private static int L_GetError(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + var task = CheckTask(L, false); + + if (task.Status == TaskStatus.Failed) + { + L.PushString(task.Error); + } + else + { + L.PushNil(); + } + + return 1; + } + + private static int LM_GC(IntPtr state) + { + return 0; + } + + private static int LM_ToString(IntPtr state) + { + var L = Lua.FromIntPtr(state); + var task = ToTask(L); + L.PushString("Task<{0}>: {1} ({2})", task?.TypeName, task?.Guid, task?.Status); + + return 1; + } +} diff --git a/Capy64/Runtime/Objects/WebSocketClient.cs b/Capy64/Runtime/Objects/WebSocketClient.cs index f8fb5e7..6964460 100644 --- a/Capy64/Runtime/Objects/WebSocketClient.cs +++ b/Capy64/Runtime/Objects/WebSocketClient.cs @@ -171,11 +171,11 @@ public class WebSocketClient : IComponent var buffer = ToObject(L); if (buffer is not null) { - L.PushString("GPUBuffer ({0:X})", (ulong)&buffer); + L.PushString("WebSocket ({0:X})", (ulong)&buffer); } else { - L.PushString("GPUBuffer (closed)"); + L.PushString("WebSocket (closed)"); } return 1; } diff --git a/Capy64/Runtime/RuntimeManager.cs b/Capy64/Runtime/RuntimeManager.cs index 54cfca9..cf150d7 100644 --- a/Capy64/Runtime/RuntimeManager.cs +++ b/Capy64/Runtime/RuntimeManager.cs @@ -123,12 +123,6 @@ internal class RuntimeManager : IComponent luaState.Thread.PushCFunction(L_Exit); luaState.Thread.SetGlobal("exit"); - luaState.Thread.PushCFunction(L_SetConsole); - luaState.Thread.SetGlobal("setConsole"); - - luaState.Thread.PushCFunction(L_GetConsole); - luaState.Thread.SetGlobal("getConsole"); - var status = luaState.Thread.LoadFile("Assets/bios.lua"); if (status != LuaStatus.OK) { @@ -219,26 +213,6 @@ internal class RuntimeManager : IComponent return 0; } - private static int L_SetConsole(IntPtr state) - { - var L = Lua.FromIntPtr(state); - - var status = L.ToBoolean(1); - _game.Window.ToggleConsole(status); - - return 0; - } - - private static int L_GetConsole(IntPtr state) - { - var L = Lua.FromIntPtr(state); - - var status = _game.Window.IsConsoleVisible(); - L.PushBoolean(status); - - return 1; - } - private void OnInit(object sender, EventArgs e) { Start(); @@ -246,6 +220,7 @@ internal class RuntimeManager : IComponent private void OnTick(object sender, TickEvent e) { - Resume(); + if (e.IsActiveTick) + Resume(); } }