dev: 更新代码,并准备LuaMod

This commit is contained in:
ZZY
2025-06-14 14:38:28 +08:00
parent 3fa39fc71e
commit 13450ea09a
20 changed files with 486 additions and 397 deletions

View File

@ -1,31 +1,22 @@
#nullable disable
using Godot;
using Godot.Collections;
using ChineseChess;
public partial class ChessGame : Node2D {
ChessBoard board;
GlobalManager global = GlobalManager.Instance;
ConfirmationDialog dialog;
readonly Logging.Logger logger = Logging.GetLogger("ChessGame");
ChessBoard board = null!;
ConfirmationDialog dialog = null!;
ChessCore Game = null!;
private bool isSession = false;
ChessCore Game;
ChessCore.TurnsSideType sideSelf;
ChessCore.TurnsSideType sideOpposite;
// Called when the node enters the scene tree for the first time.
public override void _Ready() {
// Init.Call();
board = GetNode<ChessBoard>("Chessboard");
// ChessGameAPI api = new();
// api.BindToLua(global.ModManager.lua);
// isSession = global.RPClient.IsOnline();
GetNode<LineEdit>("Control/VBoxContainer/MarginContainer3/HFlowContainer/LineEdit")
.Text = global.GlobalData["player_color"].AsString();
LineEdit turnSideEdit = GetNode<LineEdit>("Control/VBoxContainer/MarginContainer3/HFlowContainer/LineEdit2");
board = GetNode<ChessBoard>("%Chessboard");
GetNode<LineEdit>("%LineEdit_YouAre").Text = global.GlobalData["player_color"].AsString();
LineEdit turnSideEdit = GetNode<LineEdit>("%LineEdit_TurnSide");
turnSideEdit.Text = "red";
// GD.Print("ChessGame uid:", global.RPClient.GetUserId(), " color:",global.GlobalData["player_color"]);
dialog = new ConfirmationDialog {
DialogAutowrap = true,
@ -52,107 +43,32 @@ public partial class ChessGame : Node2D {
int posX = (int)pos.X;
int posY = (int)pos.Y;
Vector.Vector2I vector = new(posX, posY);
if (isSession) {
Game.OnPosClicked(vector, ChessCore.PlayerSideType.Self);
// var res = global.RPClient.SendSessionToAll(global.sessionId, new Dictionary {
// {"type", "mouseClicked"},
// {"X", pos.X},
// {"Y", pos.Y},
// {"id", global.RPClient.GetUserId()}
// });
} else {
Game.OnPosClicked(vector);
}
Game.OnPosClicked(vector);
};
if (isSession) {
GD.Print("ws is connected");
// global.RPClient.OnPRCSessionExit += (cmd, code) => {
// GoHome();
// };
// global.RPClient.OnPRCSessionRecv += (msg) => {
// SessionMsgHandle(msg["msg"].AsGodotDictionary());
// };
}
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta) {
}
// private void SessionMsgHandle(Dictionary msg) {
// GD.PrintErr($"session msg: {msg}");
// switch (msg["type"].AsString()) {
// case "over":
// if (global.RPClient.GetUserId() == msg["id"].AsString()) {
// break;
// }
// dialog.Title = "Opponent Finished";
// dialog.DialogText = "Turn On You\n";
// dialog.Visible = true;
// break;
// case "mouseClicked":
// if (msg["id"].ToString() == global.RPClient.GetUserId()) {
// break;
// }
// Vector2 mouseClicked = new(GD.StrToVar(msg["X"].ToString()).AsInt32(),
// GD.StrToVar(msg["Y"].ToString()).AsInt32());
// Vector.Vector2I vector = new((int)mouseClicked.X, (int)mouseClicked.Y);
// Game.OnPosClicked(vector, ChessCore.PlayerSideType.Opponent);
// break;
// case "undo":
// Game.Undo();
// break;
// case "reInit":
// Game.ReInit();
// break;
// }
// }
private void BtnOver() {
GD.Print($"BtnOver {isSession}");
if (isSession == false) {
return;
}
// global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
// {"type", "over"},
// {"id", global.RPClient.GetUserId()},
// });
logger.Info($"BtnOver");
}
public void GoHome() {
// if (global.RPClient.IsOnline()) {
// global.RPClient.ExitServer();
// }
// TODO ClearServer();
global.GotoScene("res://Main.tscn");
}
public void Undo() {
GD.Print($"Undo {isSession}");
if (isSession) {
// global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
// {"type", "undo"},
// {"id", global.RPClient.GetUserId()},
// });
} else {
if (Game.board.Steps == 0) board.Clear();
Game.Undo();
}
logger.Info($"Undo");
if (Game.board.Steps == 0) board.Clear();
Game.Undo();
}
public void ReInit() {
GD.PrintErr($"ReInit {isSession}");
if (isSession) {
// global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
// {"type", "reInit"},
// });
} else {
Game.ReInit();
board.Clear();
}
// TODO using dialog to confirm
logger.Info($"ReInit");
Game.ReInit();
}
}

View File

@ -1,140 +1,60 @@
#nullable disable
using Godot;
using Godot.Collections;
public partial class GameLobby : Control {
GlobalManager global = GlobalManager.Instance;
Timer timer = null;
readonly Logging.Logger logger = Logging.GetLogger("GameLobby");
Timer timer = null!;
ItemList lists = null;
ConfirmationDialog dialog = null;
string URL;
string userName;
bool isAcceptedPart = false;
ItemList lists = null!;
ConfirmationDialog dialog = null!;
string URL = null!;
string userName = null!;
// Called when the node enters the scene tree for the first time.
public override void _Ready() {
lists = GetNode<ItemList>("MarginContainer/VBoxContainer/MarginContainer3/ItemList");
lists = GetNode<ItemList>("%ItemList_Users");
// TODO using %
dialog = GetNode<ConfirmationDialog>("Dialogs/ConfirmationDialog");
URL = global.ConfigManager.GetValue<string>("server_url");
URL = global.ConfigManager.GetValue<string>("server_addr");
userName = global.ConfigManager.GetValue<string>("user_name");
dialog.MinSize = new Vector2I(400, 200);
dialog.DialogAutowrap = true;
dialog.Canceled += () => {
// if (dialog.Title == "Session Created")
// global.RPClient.SessionAckCreate(dialog.GetMeta("sessionId").ToString(), false);
};
// TODO Cancel to Accept Session Create
};
dialog.Confirmed += () => {
if (dialog.Title == "Session Created") {
// FIXME
isAcceptedPart = true;
// global.RPClient.SessionAckCreate(dialog.GetMeta("sessionId").ToString(), true);
// TODO Create Session
}
};
// global.RPClient.OnOpen += (eventName, args) => {
// global.RPClient.UserInit(userName, "godot chessboard", () => {
// global.RPClient.UserRename(userName);
// return global.RPClient.RegionAdd("server");
// });
// };
// global.RPClient.OnOpen += (eventName, args) => {
// global.RPClient.UserInit(userName, "none", () => {
// GD.PrintErr($"User Init Success");
// return true;
// });
// // global.RPClient.UserRename(userName);
// };
// global.RPClient.RegSessionAckCreateCallback((sessionId, res, reqUserId, reqUserName) => {
// if (reqUserId != null) {
// dialog.Title = "Session Created";
// dialog.SetMeta("reqUserName", reqUserName);
// dialog.SetMeta("reqUserId", reqUserId);
// dialog.SetMeta("sessionId", sessionId);
// // dialog.GetLabel==>Text = $"{sessdata["reqUserName"]}";
// dialog.DialogText = $"username: {reqUserName}\n" +
// $"reqUserId: {reqUserId}\n";
// dialog.Visible = true;
// } else {
// if (res) {
// // TODO FIXME
// global.sessionId = sessionId;
// if (!isAcceptedPart) {
// global.GlobalData["player_color"] = "red";
// } else {
// global.GlobalData["player_color"] = "black";
// }
// // GD.PrintErr("sessionId: ", reqUserId, "color: ", global.GlobalData["player_color"].AsString());
// global.GotoScene("res://Scenes/ChessGame.tscn");
// } else {
// dialog.Title = "Failed";
// dialog.DialogText = $"session create failed";
// dialog.Visible = true;
// }
// }
// return true;
// });
// timer = new Timer();
// AddChild(timer);
// TODO using new State Shower And Using Heartbeat to calucate the delay time
ColorRect colorRect = GetNode<ColorRect>("MarginContainer/VBoxContainer/MarginContainer2/MarginContainer2/ColorRect");
// timer.Connect("timeout", Callable.From(() => {
// if (global.RPClient.GetIsConnected()) {
// colorRect.Color = Colors.Green;
// } else {
// colorRect.Color = Colors.Red;
// }
// FlushData();
// }));
// timer.Start(1);
Connect();
}
public void Connect() {
GD.Print("Connect");
// if (global.RPClient.GetIsConnected()) {
// return;
// }
// global.RPClient.ExitServer();
// global.RPClient.ConnectToUrlEx(URL);
logger.Info("Connect To Server");
// TODO connet to server
global.SetProcess(true);
}
private void OnItemSelected(int index) {
Dictionary item = lists.GetItemMetadata(index).AsGodotDictionary();
GD.Print($"Item {index} selected, {item}");
string[] strings = { item["id"].ToString() };
// global.RPClient.SessionCreate(strings);
}
Dictionary item = lists.GetItemMetadata(index).AsGodotDictionary();
logger.Debug($"Item {index} selected, {item}");
string[] _ = [item["id"].ToString()];
// TODO new a Session
}
private void FlushData() {
// global.RPClient.RegionInspect("server", (data) => {
// // GD.Print(data);
// lists?.Clear();
// foreach (Dictionary<string, string> user in data) {
// string userId = user["id"].ToString();
// string userName = user["name"].ToString();
// if (userId == global.RPClient.GetUserId()) {
// lists.SetItemDisabled(
// lists.AddItem($"Name: {userName}"),
// true);
// continue;
// }
// var idx = lists.AddItem($"Name: {userName}");
// lists.SetItemMetadata(idx, user);
// lists.SetItemTooltip(idx, $"User ID: {userId}");
// }
// return true;
// });
private void FlushUserData() {
// TODO Flush user data or heartbeat flush
}
private void OnBack() {
// global.RPClient.ExitServer();
global.GotoScene("res://Main.tscn");
// TODO ExitServer();
global.GotoScene("res://Main.tscn");
}
}

View File

@ -3,24 +3,37 @@ using System;
public partial class Mods : Control {
GlobalManager global = GlobalManager.Instance;
public override void _Ready() {
// RunDemo();
ItemList modsItemList = null!;
// ModManager modManager = new();
// modManager.RegistryFunc("OnInit", () => { GD.Print("OnInit"); }, true);
// modManager.RegistryFunc("foo", () => { GD.Print("Hello"); });
// LuaMod mod = new(ProjectSettings.GlobalizePath("res://Protos/test.lua"));
// mod.BindFunc(modManager);
// try {
// modManager.InvokeFunc<Action>("OnInit");
// }
// catch (System.Exception e) {
// GD.PrintErr("OnInit error: ", e.Message, e.StackTrace);
// }
public override void _Ready() {
// TODO
modsItemList = GetNode<ItemList>("VBoxContainer/VScrollBar/ItemList");
FlushModState();
}
private void FlushModState() {
modsItemList.Clear();
foreach (var mod in global.Mods.ModList) {
var item = modsItemList.AddItem(mod.Key);
}
}
private void OnEnableMods(bool enable) {
if (enable) {
global.Mods.LoadAllMods();
}
else {
global.Mods.ClearAllMods();
}
}
private void OnReSearchMods() {
global.Mods.SearchAllMods(true);
}
public void OnOpenModFileDir() {
OS.ShellOpen(global.ConfigManager.GetValue<string>("mods_fpath") ?? "user://Mods/");
string path = global.ConfigManager.GetValue<string>("mods_fpath") ?? "user://mods";
OS.ShellOpen(ProjectSettings.GlobalizePath(path));
}
public void OnBack() {

View File

@ -4,24 +4,21 @@ using System.IO;
public partial class Setting : Control {
private GlobalManager global = GlobalManager.Instance;
private IConfigManager config = GlobalManager.Instance.ConfigManager;
int font_size;
private readonly IConfigManager config = GlobalManager.Instance.ConfigManager;
private int font_size;
private LineEdit fontOut = null!;
// Called when the node enters the scene tree for the first time.
public override void _Ready() {
fontOut = GetNode<LineEdit>("BoxContainer/MarginContainer5/HBoxContainer/FontSize");
fontOut = GetNode<LineEdit>("%FontSize");
font_size = config.GetValue<int>("font_size");
GetNode<HSlider>("BoxContainer/MarginContainer6/FontSizeBar")
GetNode<HSlider>("%FontSizeBar")
.Value = font_size;
fontOut.Text = font_size.ToString();
FillingData();
}
private void FillingData() {
GetNode<LineEdit>("BoxContainer/MarginContainer2/Server/LineEdit")
.Text = config.GetValue<string>("server_url");
GetNode<LineEdit>("BoxContainer/MarginContainer3/Name/LineEdit")
GetNode<LineEdit>("%LineEdit_ServerIP")
.Text = config.GetValue<string>("server_addr");
GetNode<LineEdit>("%LineEdit_UserName")
.Text = config.GetValue<string>("user_name");
}
@ -35,17 +32,14 @@ public partial class Setting : Control {
private void OnSave() {
global.SaveConfig();
// OS.Alert("Saved", "Setting");
}
void OnNameChanged(string Value) {
config.SetValue("user_name", Value);
// config.GetValue<>Flush();
}
private void OnServerUrlChanged(string Value) {
config.SetValue("server_url", Value);
// config.GetValue<>Flush();
}
private void OnFontSizeChanged(float Value) {

View File

@ -2,23 +2,26 @@ using System.Collections.Generic;
using Godot;
public partial class GlobalManager : Node {
private static readonly Logging.Logger logger = Logging.GetLogger("GlobalManager");
public static GlobalManager Instance { get; private set; } = null!;
public static string ConfigPath { get; private set; } = "user://config.json";
private GlobalManager() {
ConfigManager.SetFilePath(ProjectSettings.GlobalizePath(ConfigPath));
ConfigManager.SetDefault(_default_config);
ConfigManager.LoadFromFile();
GD.Print("GameSystem Singleton Initialized");
private GlobalManager() {
ConfigManager.SetFilePath(ProjectSettings.GlobalizePath(ConfigPath));
ConfigManager.SetDefault(_default_config);
ConfigManager.LoadFromFile();
Mods = new GlobalModManager(ConfigManager.GetValue<string>("mods_fpath"));
Mods.SearchAllMods();
}
// TODO Will Change It
public readonly GlobalModManager Mods = null!;
public readonly IConfigManager ConfigManager = new JsonConfigManager();
private static readonly Dictionary<string, object> _default_config = new() {
{"font_size", 20},
{"server_url", "wss://game.zzyxyz.com/"},
{"server_addr", "wss://game.zzyxyz.com/"},
{"user_name", "undefined"},
{"mods_fpath", "user://Mods/"},
{"mods_fpath", "user://mods"},
{"mods_allowed", false},
};
@ -39,23 +42,15 @@ public partial class GlobalManager : Node {
Instance = this;
}
else {
GD.PushError("GlobalManager already exists");
logger.Error("GlobalManager already exists");
QueueFree();
}
if (OS.GetName() == "Android") {
bool ret = OS.RequestPermissions();
GD.Print($"RequestPermissions ret is {ret}");
logger.Warning($"RequestPermissions ret is {ret}");
}
// OS.RequestPermissions();
// RPClient.OnRPCError += (errCode, type, cmd, errMsg) => {
// GD.PrintErr($"errCode {errCode}, type/cmd {type}/{cmd}, errMsg {errMsg}");
// };
// RPClient.OnClose += (eventName, args) => {
// SetProcess(false);
// };
Viewport root = GetTree().Root;
CurrentScene = root.GetChild(root.GetChildCount() - 1);

175
Scripts/Logger.cs Normal file
View File

@ -0,0 +1,175 @@
using Godot;
using System;
using System.Collections.Generic;
using System.Diagnostics;
/// <summary>
/// 仿照 Python logging 的 日志系统
/// </summary>
public static class Logging {
// ========== 日志级别 ==========
public enum Level {
NOSET = 0,
DEBUG = 10,
INFO = 20,
WARNING = 30,
ERROR = 40,
CRITICAL = 50
}
/// <summary>
/// 日志记录器
/// </summary>
public class Logger(string name) {
public string Name { get; } = name;
public Level LogLevel { get; set; } = Level.INFO;
private readonly List<Action<Level, string, string, string>> _handlers = [];
public void AddHandler(Action<Level, string, string, string> handler) =>
_handlers.Add(handler);
// ===== 日志方法 =====
public void Debug(string fmt, params object[] args) =>
Log(Level.DEBUG, fmt, false, args);
public void Info(string fmt, params object[] args) =>
Log(Level.INFO, fmt, false, args);
public void Warning(string fmt, params object[] args) =>
Log(Level.WARNING, fmt, false, args);
public void Error(string fmt, params object[] args) =>
Log(Level.ERROR, fmt, false, args);
public void Critical(string fmt, params object[] args) =>
Log(Level.CRITICAL, fmt, true, args);
public void Exception(string fmt, Exception e, params object[] args) {
// TODO for godot not support /r/n yet
string stackTrace = e.StackTrace?.Replace("\r\n", "\n") ?? "";
string exceptionMessage = $"{fmt}\n{e.Message}\n{stackTrace}";
Log(Level.ERROR, exceptionMessage, false, args);
}
// ===== 核心Log方法 =====
private void Log(Level level, string fmt, bool isExec = false, params object[] args) {
if (level < LogLevel) return;
// 格式化消息
string message = args.Length > 0 ?
string.Format(fmt, args) : fmt;
// 添加执行堆栈信息
string stackInfo = isExec ?
$"\nExecution Stack:\n{System.Environment.StackTrace}" : "";
// 构建前缀
string prefix = $"[{DateTime.Now:HH:mm:ss}] [{level}] [{Name}]";
// 调用所有处理器
foreach (var handler in _handlers) {
handler(level, prefix, message, stackInfo);
}
}
[Conditional("DEBUG")]
public void Assert(bool condition, string fmt, params object[] args) {
if (condition) return;
string message = args.Length > 0 ?
string.Format(fmt, args) : fmt;
Error("Assertion Failed: " + message, true);
if (Debugger.IsAttached)
Debugger.Break();
System.Diagnostics.Debug.Assert(condition, message);
}
}
// ========== 全局管理器 ==========
private static readonly Dictionary<string, Logger> _loggers = [];
private static readonly Logger _root = GetLogger("root");
/// <summary>
/// 获取或创建日志记录器
/// </summary>
public static Logger GetLogger(string name = "root") {
if (!_loggers.TryGetValue(name, out var logger)) {
logger = new Logger(name);
logger.AddHandler(DefaultHandler);
_loggers[name] = logger;
}
return logger;
}
// ========== 优化的默认处理器 ==========
private static void DefaultHandler(Level level, string prefix, string message, string stackInfo) {
// 构建完整日志消息
string fullMessage = $"{prefix} {message}{stackInfo}";
switch (level) {
case Level.DEBUG:
// DEBUG级别使用GD.Print + OS条件
if (OS.IsStdOutVerbose())
GD.Print(fullMessage);
break;
case Level.INFO:
// INFO级别使用GD.Print
GD.Print(fullMessage);
break;
case Level.WARNING:
// WARNING级别使用PrintRich + 黄色
GD.PrintRich($"[color=yellow]{fullMessage}[/color]");
break;
case Level.ERROR:
// ERROR级别使用GD.PrintErr
GD.PrintRich($"[color=red][b]{fullMessage}[/b][/color]");
// GD.PrintErr(fullMessage);
break;
case Level.CRITICAL:
// CRITICAL级别使用GD.PushError
GD.PushError(fullMessage);
break;
}
}
// ========== 快捷操作方式 ==========
public static void Debug(string fmt, params object[] args) =>
_root.Debug(fmt, args);
public static void Info(string fmt, params object[] args) =>
_root.Info(fmt, args);
public static void Warning(string fmt, params object[] args) =>
_root.Warning(fmt, args);
public static void Error(string fmt, params object[] args) =>
_root.Error(fmt, args);
public static void Critical(string fmt, params object[] args) =>
_root.Critical(fmt, args);
// ===== C语言风格的宏断言快捷方式 =====
[Conditional("DEBUG")]
public static void Assert(bool condition, string fmt, params object[] args) =>
_root.Assert(condition, fmt, args);
[Conditional("DEBUG")]
public static void TestPrint() {
GD.Print("GameSystem Singleton Initialized");
GD.PrintErr("This is a print error");
GD.PushWarning("This is a warning");
GD.PushError("This is a error");
Debug("This is a debug");
Info("This is a info");
Warning("This is a warn");
Error("This is a error");
Critical("This is a critical");
Assert(true, "This is a assert");
}
}

1
Scripts/Logger.cs.uid Normal file
View File

@ -0,0 +1 @@
uid://bud682vprk1w6

View File

@ -1,67 +1,70 @@
using Godot;
using NLua;
using System;
using System.Text;
using System.Collections.Generic;
using System.Collections.ObjectModel;
public class GlobalModManager
{
public readonly Lua lua = new();
private readonly string modsPath;
public class GlobalModManager(string modPath = "user://mods") {
public static GlobalModManager Instance { get; } = new();
private readonly Dictionary<string, LuaModManager> _mods = [];
public ReadOnlyDictionary<string, LuaModManager> Mods => new(_mods);
private readonly Dictionary<string, string> _modList = [];
public ReadOnlyDictionary<string, string> ModList => new(_modList);
private readonly string modPath = modPath;
private static readonly Logging.Logger logger = Logging.GetLogger("GlobalMod");
public GlobalModManager(string modsPath = "user://Mods/") {
this.modsPath = modsPath;
lua.State.Encoding = Encoding.UTF8;
// lua.State.SetHook(KeraLua.LuaHookMask.Count, 1000000);
InitSandBox();
// RegisterAPI();
}
public void LoadAllMods()
{
if (!DirAccess.DirExistsAbsolute(modsPath))
public void SearchAllMods(bool isFlush = false) {
if (isFlush) {
_modList.Clear();
}
if (!DirAccess.DirExistsAbsolute(modPath))
return;
using var dir = DirAccess.Open(modsPath);
using var dir = DirAccess.Open(modPath);
if (dir == null) return;
dir.ListDirBegin();
string fileName = dir.GetNext();
while (!string.IsNullOrEmpty(fileName))
{
if (fileName.EndsWith(".lua"))
{
LoadMod(fileName);
while (!string.IsNullOrEmpty(fileName)) {
string fullPath = $"{modPath}/{fileName}";
const string entryFileName = "main.lua";
if (dir.CurrentIsDir() && !fileName.StartsWith('.') &&
FileAccess.FileExists($"{fullPath}/{entryFileName}")) {
_modList.Add($"{fullPath}/{entryFileName}", fullPath);
}
fileName = dir.GetNext();
}
}
private void InitSandBox() {
}
private void LoadMod(string filePath)
{
try
{
string luaCode = LoadFile(filePath);
lua.DoString(luaCode);
GD.Print($"成功加载Mod: {filePath}");
public void LoadAllMods(bool isFlush = false) {
if (isFlush) {
_mods.Clear();
}
catch (Exception e)
{
GD.PrintErr($"加载Mod {filePath} 失败: {e.Message}");
foreach (var mod in _modList) {
try {
LoadMod(mod.Key, mod.Value);
}
catch (System.Exception e) {
logger.Exception("Failed to load mod {0}", e, mod.Key);
}
}
}
private static void SaveFile(string path, string content)
{
public void ClearAllMods() {
_mods.Clear();
}
private void LoadMod(string filePath, string modPath) {
string _filePath = ProjectSettings.GlobalizePath(filePath);
string _modPath = ProjectSettings.GlobalizePath(modPath);
_mods.Add(filePath, new LuaModManager(_filePath, _modPath));
}
private static void SaveFile(string path, string content) {
using var file = FileAccess.Open(path, FileAccess.ModeFlags.Write);
file.StoreString(content);
}
private static string LoadFile(string path)
{
private static string LoadFile(string path) {
using var file = FileAccess.Open(path, FileAccess.ModeFlags.Read);
string content = file.GetAsText();
return content;

View File

@ -1,19 +1,19 @@
using System;
using System.IO;
using System.Linq;
using Godot;
using NLua;
class LuaMod : IDisposable {
public class LuaModManager : IDisposable {
private readonly Lua lua = new();
private static readonly Logging.Logger logger = Logging.GetLogger("LuaMod");
public LuaMod(string path, string? package_path = null) {
public LuaModManager(string path, string? package_path = null) {
lua.LoadCLRPackage();
lua.State.Encoding = System.Text.Encoding.UTF8;
lua["package.path"] = package_path ?? Path.GetDirectoryName(path) + "/?.lua;";
lua.RegisterFunction("print", new Action<object[]>(GD.Print).Method);
// Action bar = () => { GD.Print("bar"); };
// lua.RegisterFunction("bar", bar.Target, bar.Method);
lua.RegisterFunction("print", new Action<object[]>(Godot.GD.Print).Method);
logger.Info("Loading Lua Mod: " + path);
lua.DoFile(path);
}
@ -36,5 +36,9 @@ class LuaMod : IDisposable {
lua?.Dispose();
GC.SuppressFinalize(this);
}
public static void WriteComment(Delegate @delegate) {
// TODO
}
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
class ModManager {
public class ModManager {
private readonly Dictionary<string, Delegate> _api = [];
private readonly Dictionary<string, Delegate> _hooks = [];
public delegate bool OnCallback(params object[] args);