Модульная система
Модули позволяют расширять функционал скриптов, регистрируя C# функции, классы и значения для использования в скриптовом коде.
Основы
Что такое модуль?
Модуль — это набор функций и классов, которые становятся доступными в скриптах. Например, модуль logging предоставляет функции для логирования:
-- В Lua скрипте
logging.info("Игра запущена")
logging.error("Произошла ошибка")
Встроенные модули
LoggingModule
Модуль для логирования из скриптов:
using IgdrasilEngine.Engine.Scripting.Modules;
var logger = /* ваш ILogger */;
var module = new LoggingModule<LuaModuleInterface>(logger);
engine.RegisterModule(module);
Доступные функции в скриптах:
logging.info("Информационное сообщение")
logging.debug("Отладочное сообщение")
logging.warning("Предупреждение")
logging.error("Ошибка")
logging.fatal("Критическая ошибка")
logging.wtf("Что-то пошло не так")
Создание собственных модулей
Базовый модуль
using IgdrasilEngine.Engine.Scripting;
public class MathModule : Module<LuaModuleInterface>
{
public override string Name => "math_ext";
public override void Initialize(LuaModuleInterface moduleInterface)
{
// Регистрация функций
moduleInterface.RegisterFunction("square", (double x) => x * x);
moduleInterface.RegisterFunction("cube", (double x) => x * x * x);
// Регистрация значений
moduleInterface.RegisterValue("PI", Math.PI);
moduleInterface.RegisterValue("E", Math.E);
}
}
// Использование
engine.RegisterModule(new MathModule());
В Lua:
print(math_ext.square(5)) -- 25
print(math_ext.cube(3)) -- 27
print(math_ext.PI) -- 3.14159...
Модуль с классами
public class Player
{
public string Name { get; set; }
public int Health { get; set; }
public void Attack(string target)
{
Console.WriteLine($"{Name} атакует {target}!");
}
}
public class GameModule : Module<LuaModuleInterface>
{
public override string Name => "game";
public override void Initialize(LuaModuleInterface moduleInterface)
{
// Регистрация класса
moduleInterface.RegisterClass("Player", typeof(Player));
// Регистрация фабричной функции
moduleInterface.RegisterFunction("createPlayer", (string name, int health) =>
new Player { Name = name, Health = health }
);
}
}
В Lua:
local player = game.createPlayer("Hero", 100)
player.Name = "Super Hero"
player:Attack("Dragon")
Модуль с перечислениями
public enum GameState
{
Menu,
Playing,
Paused,
GameOver
}
public class StateModule : Module<LuaModuleInterface>
{
public override string Name => "states";
public override void Initialize(LuaModuleInterface moduleInterface)
{
// Регистрация перечисления
moduleInterface.RegisterEnum("GameState", typeof(GameState));
}
}
В Lua:
local currentState = states.GameState.Playing
Жизненный цикл модуля
Расширенная структура модуля
public class GameLoopModule : Module<LuaModuleInterface>
{
public override string Name => "gameloop";
public override string Version => "1.0.0";
public override string? Description => "Модуль игрового цикла";
public override int InitializationOrder => 10; // Меньше = раньше инициализируется
private float _totalTime = 0;
public override void Initialize(LuaModuleInterface moduleInterface)
{
moduleInterface.RegisterFunction("getTotalTime", () => _totalTime);
moduleInterface.RegisterFunction("reset", () => _totalTime = 0);
}
public override void OnInitialized()
{
Console.WriteLine("GameLoopModule инициализирован");
}
public override void Update(float deltaTime)
{
_totalTime += deltaTime;
}
public override void Shutdown()
{
Console.WriteLine($"GameLoopModule завершен после {_totalTime}s");
}
public override ModuleCompatibilityResult CheckCompatibility()
{
// Проверка совместимости
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
return ModuleCompatibilityResult.Incompatible("Работает только на Windows");
return ModuleCompatibilityResult.Compatible;
}
}
Использование Update
// В игровом цикле
var engine = new LuaEngine();
engine.RegisterModule(new GameLoopModule());
// Каждый кадр
while (isRunning)
{
float deltaTime = CalculateDeltaTime();
engine.Update(deltaTime);
// Выполнение скриптов
engine.Execute("print(gameloop.getTotalTime())");
}
Атрибут ScriptExposed
Для явного экспорта методов используйте атрибут ScriptExposedAttribute:
using IgdrasilEngine.Engine.Attributes;
public class AudioManager
{
[ScriptExposed("playSound")]
public void PlaySound(string soundName)
{
// Воспроизведение звука
}
[ScriptExposed("stopAll", Description = "Останавливает все звуки")]
public void StopAllSounds()
{
// Остановка всех звуков
}
// Этот метод не будет экспортирован
internal void InternalMethod() { }
}
Валидация типов
Используйте ITypeValidator для проверки типов:
public class SafeModule : Module<LuaModuleInterface>
{
public override string Name => "safe";
public override void Initialize(LuaModuleInterface moduleInterface)
{
var validator = moduleInterface.GetTypeValidator();
Action<string> myFunction = (text) => Console.WriteLine(text);
// Проверка перед регистрацией
var validationResult = validator?.ValidateMethod(myFunction);
if (validationResult?.IsValid == true)
{
moduleInterface.RegisterFunction("print", myFunction);
}
else
{
Console.WriteLine($"Ошибка валидации: {validationResult?.ErrorMessage}");
}
}
}
Best Practices
- Именование: Используйте короткие, понятные имена для модулей
- Группировка: Объединяйте связанные функции в один модуль
- Версионирование: Указывайте версию для совместимости
- Документация: Добавляйте описания через атрибуты
- Производительность: Избегайте тяжелых операций в
Update() - Cleanup: Освобождайте ресурсы в
Shutdown()
Примеры модулей
Модуль работы с файлами
public class FileModule : Module<LuaModuleInterface>
{
public override string Name => "file";
public override void Initialize(LuaModuleInterface moduleInterface)
{
moduleInterface.RegisterFunction("read", (string path) =>
File.ReadAllText(path));
moduleInterface.RegisterFunction("write", (string path, string content) =>
File.WriteAllText(path, content));
moduleInterface.RegisterFunction("exists", (string path) =>
File.Exists(path));
}
}
Модуль HTTP-запросов
public class HttpModule : Module<LuaModuleInterface>
{
private readonly HttpClient _client = new();
public override string Name => "http";
public override void Initialize(LuaModuleInterface moduleInterface)
{
moduleInterface.RegisterFunction("get", async (string url) =>
{
var response = await _client.GetAsync(url);
return await response.Content.ReadAsStringAsync();
});
}
public override void Shutdown()
{
_client.Dispose();
}
}