Classic mode (#10)

This commit is contained in:
Alessandro Proto 2023-03-06 23:01:56 +01:00 committed by GitHub
parent 341ed38897
commit 0e4d3c135a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 892 additions and 135 deletions

View file

@ -35,7 +35,6 @@ term.setBackground(bg)
term.clear() term.clear()
term.setSize(53, 20) term.setSize(53, 20)
gpu.setScale(2)
local w, h = term.getSize() local w, h = term.getSize()
@ -63,13 +62,14 @@ local function drawVendorImage()
local w, h = gpu.getSize() local w, h = gpu.getSize()
local ok, err = pcall(function() local ok, err = pcall(function()
local buffer<close>, width, height = gpu.loadImage("/boot/vendor.bmp") local task<close> = gpu.loadImageAsync("/boot/vendor.bmp")
local buffer<close> = task:await()
local x, y = local x, y =
math.ceil((w / 2) - (width / 2)), math.ceil((w / 2) - (buffer.width / 2)),
math.ceil((h / 2) - (height / 2)) math.ceil((h / 2) - (buffer.height / 2))
gpu.drawBuffer(buffer, x, y, width, height) gpu.drawBuffer(buffer, x, y)
end) end)
if not ok then if not ok then
@ -157,11 +157,6 @@ local function installOS()
promptKey() promptKey()
end end
local function toggleConsole()
local status = getConsole()
setConsole(not status)
end
term.setBlink(false) term.setBlink(false)
local function setupScreen() local function setupScreen()
@ -170,10 +165,6 @@ local function setupScreen()
"Open data folder", "Open data folder",
openDataFolder, openDataFolder,
}, },
{
"Toggle console window",
toggleConsole,
},
{ {
"Install default OS", "Install default OS",
installOS, installOS,

View file

@ -1,4 +1,8 @@
{ {
"EngineMode": 0,
"Window": {
"Scale": 2
},
"HTTP": { "HTTP": {
"Enable": true, "Enable": true,
"Blacklist": [], "Blacklist": [],

View file

@ -20,6 +20,7 @@ using Capy64.Extensions;
using Capy64.Integrations; using Capy64.Integrations;
using Capy64.PluginManager; using Capy64.PluginManager;
using Capy64.Runtime; using Capy64.Runtime;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@ -33,9 +34,29 @@ using static Capy64.Utils;
namespace Capy64; namespace Capy64;
public enum EngineMode
{
Classic,
Free
}
public class Capy64 : Game, IGame 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 public static string AppDataPath
{ {
@ -56,11 +77,12 @@ public class Capy64 : Game, IGame
public static Capy64 Instance { get; private set; } public static Capy64 Instance { get; private set; }
public Capy64 Game => this; public Capy64 Game => this;
public EngineMode EngineMode { get; private set; } = EngineMode.Classic;
public IList<IComponent> NativePlugins { get; private set; } public IList<IComponent> NativePlugins { get; private set; }
public IList<IComponent> Plugins { get; private set; } public IList<IComponent> Plugins { get; private set; }
public int Width { get; set; } = 320; public int Width { get; set; } = DefaultParameters.Width;
public int Height { get; set; } = 240; public int Height { get; set; } = DefaultParameters.Height;
public float Scale { get; set; } = 2f; public float Scale { get; set; } = DefaultParameters.Scale;
public Drawing Drawing { get; private set; } public Drawing Drawing { get; private set; }
public Audio Audio { get; private set; } public Audio Audio { get; private set; }
public LuaState LuaRuntime { get; set; } public LuaState LuaRuntime { get; set; }
@ -83,6 +105,7 @@ public class Capy64 : Game, IGame
private readonly GraphicsDeviceManager _graphics; private readonly GraphicsDeviceManager _graphics;
private IServiceProvider _serviceProvider; private IServiceProvider _serviceProvider;
private ulong _totalTicks = 0; private ulong _totalTicks = 0;
private ulong tickrate = 0;
public Capy64() public Capy64()
{ {
@ -103,6 +126,27 @@ public class Capy64 : Game, IGame
_serviceProvider = serviceProvider; _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) public void UpdateSize(bool resize = true)
{ {
if (resize) if (resize)
@ -157,7 +201,7 @@ public class Capy64 : Game, IGame
private void ResetBorder() private void ResetBorder()
{ {
var size = (int)(Scale * 1.5); var size = (int)(Scale * DefaultParameters.BorderMultiplier);
Borders = new Borders Borders = new Borders
{ {
Top = size, Top = size,
@ -169,8 +213,12 @@ public class Capy64 : Game, IGame
protected override void Initialize() protected override void Initialize()
{ {
var configuration = _serviceProvider.GetService<IConfiguration>();
Window.Title = "Capy64 " + Version; Window.Title = "Capy64 " + Version;
Scale = configuration.GetValue("Window:Scale", DefaultParameters.Scale);
ResetBorder(); ResetBorder();
UpdateSize(); UpdateSize();
@ -179,6 +227,8 @@ public class Capy64 : Game, IGame
InactiveSleepTime = new TimeSpan(0); InactiveSleepTime = new TimeSpan(0);
SetEngineMode(configuration.GetValue<EngineMode>("EngineMode", DefaultParameters.EngineMode));
Audio = new Audio(); Audio = new Audio();
NativePlugins = GetNativePlugins(); NativePlugins = GetNativePlugins();
@ -222,7 +272,8 @@ public class Capy64 : Game, IGame
EventEmitter.RaiseTick(new() EventEmitter.RaiseTick(new()
{ {
GameTime = gameTime, GameTime = gameTime,
TotalTicks = _totalTicks TotalTicks = _totalTicks,
IsActiveTick = _totalTicks % (60 / tickrate) == 0,
}); });
Drawing.End(); Drawing.End();

321
Capy64/Core/ColorPalette.cs Normal file
View file

@ -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,
};
}

View file

@ -22,4 +22,5 @@ public class TickEvent : EventArgs
{ {
public GameTime GameTime { get; set; } public GameTime GameTime { get; set; }
public ulong TotalTicks { get; set; } public ulong TotalTicks { get; set; }
public bool IsActiveTick { get; set; }
} }

View file

@ -26,6 +26,7 @@ namespace Capy64;
public interface IGame public interface IGame
{ {
Capy64 Game { get; } Capy64 Game { get; }
EngineMode EngineMode { get; }
IList<IComponent> NativePlugins { get; } IList<IComponent> NativePlugins { get; }
IList<IComponent> Plugins { get; } IList<IComponent> Plugins { get; }
GameWindow Window { get; } GameWindow Window { get; }

View file

@ -73,7 +73,7 @@ public class Event : IComponent
return nargs; return nargs;
} }
private static int L_Pull(IntPtr state) public static int L_Pull(IntPtr state)
{ {
var L = Lua.FromIntPtr(state); var L = Lua.FromIntPtr(state);

View file

@ -14,13 +14,16 @@
// limitations under the License. // limitations under the License.
using Capy64.API; using Capy64.API;
using Capy64.Core;
using Capy64.Runtime.Objects; using Capy64.Runtime.Objects;
using KeraLua; using KeraLua;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks;
namespace Capy64.Runtime.Libraries; namespace Capy64.Runtime.Libraries;
@ -45,16 +48,6 @@ public class GPU : IComponent
function = L_SetSize, function = L_SetSize,
}, },
new() new()
{
name = "getScale",
function = L_GetScale,
},
new()
{
name = "setScale",
function = L_SetScale,
},
new()
{ {
name = "getPixel", name = "getPixel",
function = L_GetPixel, function = L_GetPixel,
@ -131,8 +124,8 @@ public class GPU : IComponent
}, },
new() new()
{ {
name = "loadImage", name = "loadImageAsync",
function = L_LoadImage, function = L_LoadImageAsync,
}, },
new() new()
{ {
@ -153,6 +146,14 @@ public class GPU : IComponent
l.NewLib(gpuLib); l.NewLib(gpuLib);
return 1; 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) private static int L_GetSize(IntPtr state)
{ {
var L = Lua.FromIntPtr(state); var L = Lua.FromIntPtr(state);
@ -167,6 +168,12 @@ public class GPU : IComponent
{ {
var L = Lua.FromIntPtr(state); var L = Lua.FromIntPtr(state);
if (_game.EngineMode == EngineMode.Classic)
{
L.PushBoolean(false);
return 1;
}
var w = L.CheckInteger(1); var w = L.CheckInteger(1);
var h = L.CheckInteger(2); var h = L.CheckInteger(2);
@ -175,31 +182,11 @@ public class GPU : IComponent
_game.UpdateSize(); _game.UpdateSize();
return 0; L.PushBoolean(true);
}
private static int L_GetScale(IntPtr state)
{
var L = Lua.FromIntPtr(state);
L.PushNumber(_game.Scale);
return 1; 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) private static int L_GetPixel(IntPtr state)
{ {
var L = Lua.FromIntPtr(state); var L = Lua.FromIntPtr(state);
@ -222,7 +209,7 @@ public class GPU : IComponent
var y = (int)L.CheckNumber(2) - 1; var y = (int)L.CheckNumber(2) - 1;
var c = L.CheckInteger(3); 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)); _game.Drawing.Plot(new Point(x, y), new Color(r, g, b));
return 0; return 0;
@ -257,7 +244,7 @@ public class GPU : IComponent
pts.Add(new Point(x, y)); 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)); _game.Drawing.Plot(pts, new(r, g, b));
return 0; return 0;
@ -272,7 +259,7 @@ public class GPU : IComponent
var c = L.CheckInteger(3); var c = L.CheckInteger(3);
var s = (int)L.OptNumber(4, 1); 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); _game.Drawing.DrawPoint(new(x, y), new Color(r, g, b), s);
return 0; return 0;
@ -289,7 +276,7 @@ public class GPU : IComponent
var t = (int)L.OptNumber(5, 1); var t = (int)L.OptNumber(5, 1);
var s = (int)L.OptInteger(6, -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); _game.Drawing.DrawCircle(new(x, y), rad, new Color(r, g, b), t, s);
return 0; return 0;
@ -306,7 +293,7 @@ public class GPU : IComponent
var c = L.CheckInteger(5); var c = L.CheckInteger(5);
var s = (int)L.OptNumber(6, 1); 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); _game.Drawing.DrawLine(new(x1, y1), new(x2, y2), new Color(r, g, b), s);
return 0; return 0;
@ -323,7 +310,7 @@ public class GPU : IComponent
var c = L.CheckInteger(5); var c = L.CheckInteger(5);
var s = (int)L.OptNumber(6, 1); 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); _game.Drawing.DrawRectangle(new(x, y), new(w, h), new Color(r, g, b), s);
return 0; return 0;
@ -359,7 +346,7 @@ public class GPU : IComponent
pts.Add(new(xp, yp)); 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); _game.Drawing.DrawPolygon(new(x, y), pts.ToArray(), new(r, g, b), s);
return 0; return 0;
@ -376,7 +363,7 @@ public class GPU : IComponent
var c = L.CheckInteger(5); var c = L.CheckInteger(5);
var s = (int)L.OptNumber(6, 1); 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); _game.Drawing.DrawEllipse(new(x, y), new(rx, ry), new Color(r, g, b), s);
return 0; return 0;
@ -391,7 +378,7 @@ public class GPU : IComponent
var c = L.CheckInteger(3); var c = L.CheckInteger(3);
var t = L.CheckString(4); 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 try
{ {
_game.Drawing.DrawString(new Vector2(x, y), t, new Color(r, g, b)); _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); _game.Drawing.Canvas.GetData(buffer);
ObjectManager.PushObject(L, buffer); ObjectManager.PushObject(L, buffer);
//L.PushObject(buffer); L.SetMetaTable(GPUBufferMeta.ObjectType);
L.SetMetaTable(GPUBuffer.ObjectType);
return 1; return 1;
} }
@ -436,9 +422,9 @@ public class GPU : IComponent
{ {
var L = Lua.FromIntPtr(state); 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; return 0;
} }
@ -453,7 +439,7 @@ public class GPU : IComponent
var buffer = new uint[width * height]; var buffer = new uint[width * height];
ObjectManager.PushObject(L, buffer); ObjectManager.PushObject(L, buffer);
L.SetMetaTable(GPUBuffer.ObjectType); L.SetMetaTable(GPUBufferMeta.ObjectType);
return 1; return 1;
} }
@ -462,30 +448,23 @@ public class GPU : IComponent
{ {
var L = Lua.FromIntPtr(state); 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 x = (int)L.CheckInteger(2) - 1;
var y = (int)L.CheckInteger(3) - 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) _game.Drawing.DrawBuffer(buffer.Buffer, new()
{
L.Error("width and height do not match buffer size");
}
_game.Drawing.DrawBuffer(buffer, new()
{ {
X = x, X = x,
Y = y, Y = y,
Width = w, Width = buffer.Width,
Height = h, Height = buffer.Height,
}); });
return 0; return 0;
} }
private static int L_LoadImage(IntPtr state) private static int L_LoadImageAsync(IntPtr state)
{ {
var L = Lua.FromIntPtr(state); var L = Lua.FromIntPtr(state);
@ -499,6 +478,8 @@ public class GPU : IComponent
return 0; return 0;
} }
var task = TaskMeta.Push(L, GPUBufferMeta.ObjectType);
Texture2D texture; Texture2D texture;
try try
{ {
@ -506,21 +487,52 @@ public class GPU : IComponent
} }
catch (Exception e) catch (Exception e)
{ {
L.Error(e.Message); task.Reject(e.Message);
return 0; return 1;
} }
var data = new uint[texture.Width * texture.Height]; var data = new uint[texture.Width * texture.Height];
texture.GetData(data); texture.GetData(data);
ObjectManager.PushObject(L, data); Task.Run(() =>
L.SetMetaTable(GPUBuffer.ObjectType); {
L.PushInteger(texture.Width); if (_game.EngineMode == EngineMode.Classic)
L.PushInteger(texture.Height); {
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) private static int L_Clear(IntPtr state)
@ -529,7 +541,7 @@ public class GPU : IComponent
var c = L.OptInteger(1, 0x000000); 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)); _game.Drawing.Clear(new Color(r, g, b));
return 0; return 0;

View file

@ -72,6 +72,11 @@ public class Machine : IComponent
name = "getClipboard", name = "getClipboard",
function = L_GetClipboard, function = L_GetClipboard,
}, },
new()
{
name = "task",
function = L_Task,
},
new(), new(),
}; };
@ -87,6 +92,18 @@ public class Machine : IComponent
return 1; 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 _) private static int L_Shutdown(IntPtr _)
{ {
RuntimeManager.Shutdown(); RuntimeManager.Shutdown();

View file

@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
using Capy64.API; using Capy64.API;
using Capy64.Core;
using Capy64.Eventing.Events; using Capy64.Eventing.Events;
using KeraLua; using KeraLua;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
@ -174,6 +175,13 @@ internal class Term : IComponent
return 1; 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) public static void UpdateSize(bool resize = true)
{ {
Array.Resize(ref CharGrid, Width * Height); Array.Resize(ref CharGrid, Width * Height);
@ -388,6 +396,12 @@ internal class Term : IComponent
{ {
var L = Lua.FromIntPtr(state); var L = Lua.FromIntPtr(state);
if(_game.EngineMode == EngineMode.Classic)
{
L.PushBoolean(false);
return 1;
}
var w = (int)L.CheckNumber(1); var w = (int)L.CheckNumber(1);
var h = (int)L.CheckNumber(2); var h = (int)L.CheckNumber(2);
@ -402,7 +416,8 @@ internal class Term : IComponent
SetSize(w, h); SetSize(w, h);
return 0; L.PushBoolean(true);
return 1;
} }
private static int L_GetForegroundColor(IntPtr state) private static int L_GetForegroundColor(IntPtr state)
@ -427,12 +442,14 @@ internal class Term : IComponent
r = (byte)L.CheckNumber(1); r = (byte)L.CheckNumber(1);
g = (byte)L.CheckNumber(2); g = (byte)L.CheckNumber(2);
b = (byte)L.CheckNumber(3); b = (byte)L.CheckNumber(3);
UnpackRGB(ColorPalette.GetColor(r, g, b), out r, out g, out b);
} }
// packed RGB value // packed RGB value
else if (argsn == 1) else if (argsn == 1)
{ {
var c = (uint)L.CheckInteger(1); var c = (uint)L.CheckInteger(1);
UnpackRGB(c, out r, out g, out b); GetColor(c, out r, out g, out b);
} }
else else
{ {
@ -467,12 +484,13 @@ internal class Term : IComponent
r = (byte)L.CheckNumber(1); r = (byte)L.CheckNumber(1);
g = (byte)L.CheckNumber(2); g = (byte)L.CheckNumber(2);
b = (byte)L.CheckNumber(3); b = (byte)L.CheckNumber(3);
UnpackRGB(ColorPalette.GetColor(r, g, b), out r, out g, out b);
} }
// packed RGB value // packed RGB value
else if (argsn == 1) else if (argsn == 1)
{ {
var c = (uint)L.CheckInteger(1); var c = (uint)L.CheckInteger(1);
UnpackRGB(c, out r, out g, out b); GetColor(c, out r, out g, out b);
} }
else else
{ {

View file

@ -14,15 +14,23 @@
// limitations under the License. // limitations under the License.
using Capy64.API; using Capy64.API;
using Capy64.Core;
using KeraLua; using KeraLua;
using System; using System;
namespace Capy64.Runtime.Objects; namespace Capy64.Runtime.Objects;
public class GPUBuffer : IComponent public class GPUBufferMeta : IComponent
{ {
public const string ObjectType = "GPUBuffer"; 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[] private static LuaRegister[] MetaMethods = new LuaRegister[]
{ {
new() new()
@ -59,29 +67,43 @@ public class GPUBuffer : IComponent
new(), new(),
}; };
private static IGame _game;
public GPUBufferMeta(IGame game)
{
_game = game;
}
public void LuaInit(Lua L) public void LuaInit(Lua L)
{ {
CreateMeta(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) public static void CreateMeta(Lua L)
{ {
L.NewMetaTable(ObjectType); L.NewMetaTable(ObjectType);
L.SetFuncs(MetaMethods, 0); 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<uint[]>(L, 1, gc); return ObjectManager.ToObject<GPUBuffer>(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<uint[]>(L, 1, ObjectType, gc); var obj = ObjectManager.CheckObject<GPUBuffer>(L, 1, ObjectType, gc);
if (obj is null) if (obj.Buffer is null)
{ {
L.Error("attempt to use a closed buffer"); L.Error("attempt to use a closed buffer");
return null; return default;
} }
return obj; return obj;
} }
@ -94,19 +116,27 @@ public class GPUBuffer : IComponent
if (!L.IsInteger(2)) 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; return 1;
} }
var key = L.ToInteger(2); var key = L.ToInteger(2);
if (key < 0 || key >= buffer.Length) if (key < 0 || key >= buffer.Buffer.Length)
{ {
L.PushNil(); L.PushNil();
return 1; return 1;
} }
var value = buffer[key]; var value = buffer.Buffer[key];
// ABGR to RGB // ABGR to RGB
value = value =
@ -131,7 +161,7 @@ public class GPUBuffer : IComponent
var key = L.ToInteger(2); var key = L.ToInteger(2);
if (key < 0 || key >= buffer.Length) if (key < 0 || key >= buffer.Buffer.Length)
{ {
return 0; return 0;
} }
@ -142,6 +172,7 @@ public class GPUBuffer : IComponent
} }
var value = (uint)L.ToInteger(3); var value = (uint)L.ToInteger(3);
value = GetColor(value);
// RGB to ABGR // RGB to ABGR
value = value =
@ -151,7 +182,7 @@ public class GPUBuffer : IComponent
0xFF_00_00_00U; 0xFF_00_00_00U;
buffer[key] = value; buffer.Buffer[key] = value;
return 0; return 0;
} }
@ -171,7 +202,7 @@ public class GPUBuffer : IComponent
var buffer = CheckBuffer(L, false); var buffer = CheckBuffer(L, false);
L.PushInteger(buffer.LongLength); L.PushInteger(buffer.Buffer.LongLength);
return 1; return 1;
} }
@ -180,7 +211,7 @@ public class GPUBuffer : IComponent
{ {
var L = Lua.FromIntPtr(state); var L = Lua.FromIntPtr(state);
var buffer = ToBuffer(L); var buffer = ToBuffer(L);
if (buffer is not null) if (buffer.Buffer is not null)
{ {
L.PushString("GPUBuffer ({0:X})", (ulong)&buffer); L.PushString("GPUBuffer ({0:X})", (ulong)&buffer);
} }

View file

@ -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>(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<Lua> 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<RuntimeTask>(L, 1, gc);
}
private static RuntimeTask CheckTask(Lua L, bool gc = false)
{
var obj = ObjectManager.CheckObject<RuntimeTask>(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<Lua> 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;
}
}

View file

@ -171,11 +171,11 @@ public class WebSocketClient : IComponent
var buffer = ToObject(L); var buffer = ToObject(L);
if (buffer is not null) if (buffer is not null)
{ {
L.PushString("GPUBuffer ({0:X})", (ulong)&buffer); L.PushString("WebSocket ({0:X})", (ulong)&buffer);
} }
else else
{ {
L.PushString("GPUBuffer (closed)"); L.PushString("WebSocket (closed)");
} }
return 1; return 1;
} }

View file

@ -123,12 +123,6 @@ internal class RuntimeManager : IComponent
luaState.Thread.PushCFunction(L_Exit); luaState.Thread.PushCFunction(L_Exit);
luaState.Thread.SetGlobal("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"); var status = luaState.Thread.LoadFile("Assets/bios.lua");
if (status != LuaStatus.OK) if (status != LuaStatus.OK)
{ {
@ -219,26 +213,6 @@ internal class RuntimeManager : IComponent
return 0; 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) private void OnInit(object sender, EventArgs e)
{ {
Start(); Start();
@ -246,6 +220,7 @@ internal class RuntimeManager : IComponent
private void OnTick(object sender, TickEvent e) private void OnTick(object sender, TickEvent e)
{ {
Resume(); if (e.IsActiveTick)
Resume();
} }
} }