10#include <unordered_set>
22 : _scriptPath(scriptPath), _world(nullptr), _bindingsInitialized(false) {
23 _lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::math, sol::lib::table,
26 LOG_INFO(
"LuaEngine initialized with script path: " + scriptPath);
27 LOG_WARNING(
"Lua bindings not yet initialized. Call setWorld() before executing scripts.");
31 if (world ==
nullptr) {
32 LOG_ERROR(
"Attempted to set null world in LuaEngine");
33 throw std::invalid_argument(
"World cannot be nullptr");
41 LOG_INFO(
"Lua bindings initialized successfully");
43 LOG_WARNING(
"World updated in LuaEngine - bindings already initialized");
63 namespace fs = std::filesystem;
68 const fs::path candidate = base / scriptPath;
70 auto tryLoad = [&](
const fs::path &p) ->
bool {
76 const sol::load_result loadResult =
_lua.load_file(p.string());
77 if (!loadResult.valid()) {
78 const sol::error err = loadResult;
79 LOG_ERROR(
"Lua error loading " + p.string() +
": " + std::string(err.what()));
83 const sol::protected_function scriptFunc = loadResult;
86 sol::protected_function_result result = scriptFunc();
87 if (!result.valid()) {
88 const sol::error err = result;
89 LOG_ERROR(
"Lua error executing " + p.string() +
": " + std::string(err.what()));
95 sol::table scriptTable =
_lua.create_table();
96 sol::table globals =
_lua.globals();
99 if (globals[
"onUpdate"].valid()) {
100 scriptTable[
"onUpdate"] = globals[
"onUpdate"];
102 if (globals[
"onInit"].valid()) {
103 scriptTable[
"onInit"] = globals[
"onInit"];
105 if (globals[
"onDestroy"].valid()) {
106 scriptTable[
"onDestroy"] = globals[
"onDestroy"];
108 if (globals[
"onGameStart"].valid()) {
109 scriptTable[
"onGameStart"] = globals[
"onGameStart"];
113 LOG_INFO(
"Loaded Lua script: " + scriptPath +
" (" + p.string() +
")");
115 }
catch (
const sol::error &e) {
116 LOG_ERROR(
"Lua error loading " + p.string() +
": " + std::string(e.what()));
122 if (tryLoad(candidate)) {
129 fs::path cwd = fs::current_path();
130 for (
int up = 0; up < 6; ++up) {
131 fs::path probeRoot = cwd;
132 for (
int i = 0; i < up; ++i) {
133 probeRoot = probeRoot.parent_path();
135 fs::path probe = probeRoot /
"server" /
"Scripting" /
"scripts" / scriptPath;
136 if (tryLoad(probe)) {
142 static std::unordered_set<std::string> loggedMissing;
143 if (!loggedMissing.contains(scriptPath)) {
144 loggedMissing.insert(scriptPath);
145 LOG_ERROR(
"Lua script not found: " + candidate.string());
147 LOG_ERROR(
" - scriptPath: " + scriptPath);
148 LOG_ERROR(
" - basePath: " + base.string());
154 std::lock_guard<std::recursive_mutex> lock(
_luaMutex);
157 LOG_ERROR(
"LuaEngine not properly initialized. Call setWorld() first.");
172 sol::optional<sol::function> onGameStartOpt = script[
"onGameStart"];
174 if (!onGameStartOpt) {
175 LOG_WARNING(
"Script " + scriptPath +
" has no onGameStart function");
179 sol::protected_function onGameStart = onGameStartOpt.value();
181 sol::protected_function_result result = onGameStart(entity);
182 if (!result.valid()) {
183 sol::error err = result;
184 LOG_ERROR(
"Lua runtime error in " + scriptPath +
": " + std::string(err.what()));
186 }
catch (
const sol::error &e) {
187 LOG_ERROR(
"Lua runtime error in " + scriptPath +
": " + std::string(e.what()));
188 }
catch (
const std::exception &e) {
189 LOG_ERROR(
"C++ exception in executeOnGameStart: " + std::string(e.what()));
195 std::lock_guard<std::recursive_mutex> lock(
_luaMutex);
198 LOG_ERROR(
"LuaEngine not properly initialized. Call setWorld() first.");
203 bool isWaveManager = (scriptPath.find(
"wave_manager") != std::string::npos);
215 sol::optional<sol::function> onUpdateOpt = script[
"onUpdate"];
218 LOG_WARNING(
"Script " + scriptPath +
" has no onUpdate function");
222 sol::protected_function onUpdate = onUpdateOpt.value();
223 sol::protected_function_result result = onUpdate(entity, deltaTime);
224 if (!result.valid()) {
225 sol::error err = result;
226 LOG_ERROR(
"Lua runtime error in " + scriptPath +
": " + std::string(err.what()));
228 }
catch (
const sol::error &e) {
229 LOG_ERROR(
"Lua runtime error in " + scriptPath +
": " + std::string(e.what()));
230 }
catch (
const std::exception &e) {
231 LOG_ERROR(
"C++ exception in executeUpdate: " + std::string(e.what()));
250 if (!script.valid()) {
251 LOG_ERROR(
"Cached script table is invalid for: " + scriptPath);
256 sol::optional<sol::function> onUpdateOpt = script[
"onUpdate"];
259 LOG_WARNING(
"Script " + scriptPath +
" has no onUpdate function");
263 sol::protected_function onUpdate = onUpdateOpt.value();
266 if (!onUpdate.valid()) {
267 LOG_ERROR(
"onUpdate function is invalid for: " + scriptPath);
271 sol::protected_function_result result = onUpdate(entity, deltaTime);
272 if (!result.valid()) {
273 sol::error err = result;
274 LOG_ERROR(
"Lua runtime error in " + scriptPath +
": " + std::string(err.what()));
276 }
catch (
const sol::error &e) {
277 LOG_ERROR(
"Lua runtime error in " + scriptPath +
": " + std::string(e.what()));
278 }
catch (
const std::exception &e) {
279 LOG_ERROR(
"C++ exception in executeUpdate: " + std::string(e.what()));
283 template <
typename... Args>
286 std::lock_guard<std::recursive_mutex> lock(
_luaMutex);
289 LOG_ERROR(
"LuaEngine not properly initialized. Call setWorld() first.");
301 sol::optional<sol::function> func = script[functionName];
304 func.value()(std::forward<Args>(args)...);
306 LOG_WARNING(
"Function " + functionName +
" not found in " + scriptPath);
308 }
catch (
const sol::error &e) {
309 LOG_ERROR(
"Lua error calling " + functionName +
": " + std::string(e.what()));
324 LOG_DEBUG(
"No game start callbacks registered");
329 " game start callback(s) for room: " + roomId);
333 sol::protected_function_result result = callback(roomId);
334 if (!result.valid()) {
335 sol::error err = result;
336 LOG_ERROR(
"Lua error in game start callback: " + std::string(err.what()));
338 }
catch (
const sol::error &e) {
339 LOG_ERROR(
"Lua exception in game start callback: " + std::string(e.what()));
340 }
catch (
const std::exception &e) {
341 LOG_ERROR(
"C++ exception in game start callback: " + std::string(e.what()));
347 std::lock_guard<std::recursive_mutex> lock(
_luaMutex);
351 LOG_DEBUG(
"Cleaning up script cache for entity " + std::to_string(entityId));
High-level ECS manager providing clean server-side API.
High-level entity wrapper providing fluent interface.
void executeOnGameStart(const std::string &scriptPath, ecs::wrapper::Entity entity)
Execute onGameStart function for an entity's script.
void registerGameStartCallback(sol::function callback)
Register a Lua callback to be called when the game starts.
void cleanupEntity(uint32_t entityId)
Clean up script cache for a destroyed entity.
void executeUpdate(const std::string &scriptPath, ecs::wrapper::Entity entity, float deltaTime)
Execute onUpdate function for an entity's script.
LuaEngine(const std::string &scriptPath="server/Scripting/scripts/")
Constructor with scripts directory path.
std::recursive_mutex _luaMutex
void setWorld(ecs::wrapper::ECSWorld *world)
Set the ECS world for entity operations.
std::unordered_map< uint32_t, std::unordered_map< std::string, sol::table > > _entityScriptCache
void callFunction(const std::string &scriptPath, const std::string &functionName, Args &&...args)
Call a specific Lua function.
bool loadScript(const std::string &scriptPath)
Load and cache a Lua script.
ecs::wrapper::ECSWorld * _world
std::unordered_map< std::string, sol::table > _scriptCache
std::vector< sol::function > _gameStartCallbacks
void initializeBindings()
void fireGameStartCallbacks(const std::string &roomId)
Fire all registered game start callbacks.
bool _bindingsInitialized
void bindEntity(sol::state &lua, ecs::wrapper::ECSWorld *world, ComponentBindingHelper &helper)
Bind Entity wrapper class and operations to Lua.
void bindWorld(sol::state &lua, ecs::wrapper::ECSWorld *world)
Bind ECS world operations to Lua.
void bindServerGame(sol::state &lua, ecs::wrapper::ECSWorld *world, LuaEngine *engine)
Bind server-specific game logic functions to Lua.
ComponentBindingHelper & bindComponents(sol::state &lua, ecs::wrapper::ECSWorld *world)
Bind ECS component types to Lua.