From 76f3c79b8b4045a6f4a3844b6a65d5bc44dc64a6 Mon Sep 17 00:00:00 2001
From: Joseph Walton-Rivers <joseph@walton-rivers.uk>
Date: Mon, 17 Oct 2022 11:41:57 +0100
Subject: [PATCH] lua integration test

---
 CMakeLists.txt                                | 20 ++--
 Config.cmake.in                               |  1 +
 demo/CMakeLists.txt                           |  5 +-
 demo/data/rollball.lua                        |  1 +
 demo/data/rollball.yml                        |  2 +
 demo/demo/main.cpp                            |  2 +
 demo/demo/rollball.cpp                        | 20 ++++
 demo/include/rollball.hpp                     |  2 +
 include/fggl/script/engine.hpp                | 53 ++++++++++
 integrations/lua/CMakeLists.txt               | 19 ++++
 .../lua/include/fggl/script/lua/engine.hpp    | 67 +++++++++++++
 .../lua/include/fggl/script/lua/module.hpp    | 46 +++++++++
 integrations/lua/src/engine.cpp               | 98 +++++++++++++++++++
 integrations/lua/src/module.cpp               | 35 +++++++
 14 files changed, 364 insertions(+), 7 deletions(-)
 create mode 100644 demo/data/rollball.lua
 create mode 100644 include/fggl/script/engine.hpp
 create mode 100644 integrations/lua/CMakeLists.txt
 create mode 100644 integrations/lua/include/fggl/script/lua/engine.hpp
 create mode 100644 integrations/lua/include/fggl/script/lua/module.hpp
 create mode 100644 integrations/lua/src/engine.cpp
 create mode 100644 integrations/lua/src/module.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6e7c22b..396a756 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -63,11 +63,22 @@ if (FGGL_DOCS)
 	endif()
 endif()
 
+##
+# Optional/Extra modules
+##
+
+# 3rd party integrations
+add_subdirectory( integrations/bullet )
+add_subdirectory( integrations/lua )
+
+# Tools
+add_subdirectory( tools/pack )
+
 # Demo project
 if (FGGL_EXAMPLES)
-  add_subdirectory(demo)
-  target_compile_options( demo PRIVATE -Wall -Wextra -Wodr -Wdouble-promotion -fno-strict-aliasing -fno-strict-overflow )
-  set_property(TARGET demo PROPERTY INTERPROCEDURAL_OPTIMIZATION True)
+	add_subdirectory(demo)
+	target_compile_options( demo PRIVATE -Wall -Wextra -Wodr -Wdouble-promotion -fno-strict-aliasing -fno-strict-overflow )
+	set_property(TARGET demo PROPERTY INTERPROCEDURAL_OPTIMIZATION True)
 endif()
 
 ##
@@ -127,6 +138,3 @@ export(EXPORT "${PROJECT_NAME}Targets"
     NAMESPACE ${namespace}::
 )
 
-# 3rd party integrations
-add_subdirectory( integrations/bullet )
-add_subdirectory( tools/pack )
diff --git a/Config.cmake.in b/Config.cmake.in
index fe72022..7d3cb0a 100644
--- a/Config.cmake.in
+++ b/Config.cmake.in
@@ -3,6 +3,7 @@
 include(CMakeFindDependencyMacro)
 find_dependency(glm)
 find_dependency(fmt)
+find_dependency(Lua)
 find_dependency(spdlog)
 find_dependency(Freetype)
 find_dependency(OpenAL CONFIG)
diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt
index 37e86da..facfd04 100644
--- a/demo/CMakeLists.txt
+++ b/demo/CMakeLists.txt
@@ -11,7 +11,6 @@ add_executable(demo
         demo/robot/programmer.cpp
 )
 
-
 target_include_directories(demo
         PRIVATE
             ${CMAKE_CURRENT_SOURCE_DIR}/include
@@ -19,11 +18,15 @@ target_include_directories(demo
 
 target_link_libraries( demo fggl )
 #target_link_libraries(demo fggl fgglbt)
+target_link_libraries( demo fggl-lua )
 
 find_package(spdlog)
 target_link_libraries(demo spdlog::spdlog)
 #target_include_directories(FgglDemo PUBLIC ${PROJECT_BINARY_DIR})
 
+#find_package(Lua)
+#target_link_libraries(demo ${LUA_LIBRARIES})
+
 # resources
 file(GLOB_RECURSE data data/*)
 file(COPY ${data} DESTINATION data )
diff --git a/demo/data/rollball.lua b/demo/data/rollball.lua
new file mode 100644
index 0000000..1221bc1
--- /dev/null
+++ b/demo/data/rollball.lua
@@ -0,0 +1 @@
+print("File has been loaded!")
\ No newline at end of file
diff --git a/demo/data/rollball.yml b/demo/data/rollball.yml
index 966d1f8..f9895a6 100644
--- a/demo/data/rollball.yml
+++ b/demo/data/rollball.yml
@@ -120,3 +120,5 @@ scene:
     components:
       Transform:
         origin: [6, -0.5, 15]
+scripts:
+  - "rollball.lua"
\ No newline at end of file
diff --git a/demo/demo/main.cpp b/demo/demo/main.cpp
index da2ae94..37b95f6 100644
--- a/demo/demo/main.cpp
+++ b/demo/demo/main.cpp
@@ -41,6 +41,7 @@
 #include "fggl/scenes/menu.hpp"
 #include "fggl/modules/manager.hpp"
 #include "fggl/assets/module.hpp"
+#include "fggl/script/lua/module.hpp"
 
 #include "GameScene.h"
 #include "rollball.hpp"
@@ -83,6 +84,7 @@ int main(int argc, const char* argv[]) {
 	moduleManager.use<fggl::display::GLFW>();
 	moduleManager.use<fggl::assets::AssetFolders>();
 	moduleManager.use<fggl::entity::ECS>();
+	moduleManager.use<fggl::script::Lua>();
 
 	#ifdef FGGL_MODULE_BULLET
 		moduleManager.use<fggl::phys::Bullet3>();
diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp
index cccdac0..39fb33b 100644
--- a/demo/demo/rollball.cpp
+++ b/demo/demo/rollball.cpp
@@ -125,6 +125,10 @@ namespace demo {
 		auto* entFactory = m_owner.service<fggl::entity::EntityFactory>();
 		m_phys = physService->create(&world(), entFactory);
 
+		auto* scriptProvider = m_owner.service<fggl::script::ScriptProvider>();
+		m_scripts = scriptProvider->create();
+
+		// asset loader
 		auto* assetLoader = m_owner.service<fggl::assets::Loader>();
 		assetLoader->load("rollball.yml", fggl::entity::PROTOTYPE_ASSET);
 
@@ -145,9 +149,20 @@ namespace demo {
 
 		// create a 20x20 grid
 		setup_environment(world(), m_owner.service<fggl::entity::EntityFactory>(), WORLD_SIZE, state);
+
+		// activate scripts
+		if ( m_scripts != nullptr ) {
+			m_scripts->onActivate();
+			m_scripts->load("rollball.lua");
+		}
 	}
 
 	void RollBall::deactivate() {
+		// deactivate scripts
+		if ( m_scripts != nullptr ) {
+			m_scripts->onActivate();
+		}
+
 		// we need to clean up physics
 		if ( m_phys != nullptr ) {
 			delete m_phys;
@@ -235,6 +250,11 @@ namespace demo {
 				spinCubes(world, deltaTime);
 				state.time += deltaTime;
 		}
+
+		// tick scripts
+		if ( m_scripts != nullptr ) {
+			m_scripts->onUpdate();
+		}
 	}
 
 	void RollBall::closestPickup(fggl::entity::EntityManager &world) {
diff --git a/demo/include/rollball.hpp b/demo/include/rollball.hpp
index fc23e5c..a470bfc 100644
--- a/demo/include/rollball.hpp
+++ b/demo/include/rollball.hpp
@@ -27,6 +27,7 @@
 
 #include "fggl/scenes/game.hpp"
 #include "fggl/phys/service.hpp"
+#include "fggl/script/engine.hpp"
 
 namespace demo {
 
@@ -64,6 +65,7 @@ namespace demo {
 			constexpr static fggl::math::vec3 HINT_COLOUR{0.5f, 0.0f, 0.0f};
 			RollState state;
 			fggl::phys::PhysicsEngine* m_phys;
+			fggl::script::ScriptEngine* m_scripts;
 			fggl::math::vec3 cameraOffset = {-15.0F, 15.0F, 0.0F};
 
 			void closestPickup(fggl::entity::EntityManager& world);
diff --git a/include/fggl/script/engine.hpp b/include/fggl/script/engine.hpp
new file mode 100644
index 0000000..f81d7f1
--- /dev/null
+++ b/include/fggl/script/engine.hpp
@@ -0,0 +1,53 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 15/10/22.
+//
+
+#ifndef FGGL_SCRIPT_ENGINE_H
+#define FGGL_SCRIPT_ENGINE_H
+
+#include "fggl/modules/module.hpp"
+
+namespace fggl::script {
+
+	class ScriptEngine {
+		public:
+			virtual ~ScriptEngine() = default;
+
+			// TODO use protected virtual pattern
+
+			// scene callbacks
+			virtual void onActivate() = 0;
+			virtual void onDeactivate() = 0;
+			virtual void onUpdate() = 0;
+
+			// trigger callback
+			virtual void onEvent(const std::string& name) = 0;
+
+			// run code in engine
+			virtual bool run(const char* script) = 0;
+			virtual bool load(const char* filename) = 0;
+	};
+
+	class ScriptProvider {
+		public:
+			constexpr static const modules::ModuleService service = modules::make_service("fggl::script::service");
+			virtual ScriptEngine* create() = 0;
+	};
+
+}
+
+#endif //FGGL_SCRIPT_ENGINE_H
diff --git a/integrations/lua/CMakeLists.txt b/integrations/lua/CMakeLists.txt
new file mode 100644
index 0000000..e889372
--- /dev/null
+++ b/integrations/lua/CMakeLists.txt
@@ -0,0 +1,19 @@
+add_library(fggl-lua)
+
+find_package(Lua REQUIRED)
+target_link_libraries(fggl-lua PUBLIC ${LUA_LIBRARIES})
+
+# Link to FGGL
+target_link_libraries(fggl-lua PUBLIC fggl)
+
+# sources and include directories
+target_sources(fggl-lua
+    PRIVATE
+        src/engine.cpp
+        src/module.cpp
+)
+target_include_directories(fggl-lua
+    PUBLIC
+        $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
+        $<INSTALL_INTERFACE:include>
+)
diff --git a/integrations/lua/include/fggl/script/lua/engine.hpp b/integrations/lua/include/fggl/script/lua/engine.hpp
new file mode 100644
index 0000000..0442c74
--- /dev/null
+++ b/integrations/lua/include/fggl/script/lua/engine.hpp
@@ -0,0 +1,67 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 15/10/22.
+//
+
+#ifndef FGGL_INTEGRATIONS_LUA_SCRIPT_LUA_ENGINE_HPP
+#define FGGL_INTEGRATIONS_LUA_SCRIPT_LUA_ENGINE_HPP
+
+#include "fggl/script/engine.hpp"
+#include "fggl/data/storage.hpp"
+
+extern "C" {
+	#include <lua.h>
+	#include <lauxlib.h>
+	#include <lualib.h>
+};
+
+namespace fggl::script::lua {
+
+	class LuaScriptEngine : public ScriptEngine {
+		public:
+			LuaScriptEngine(data::Storage* storage);
+			virtual ~LuaScriptEngine();
+
+			void onActivate() override;
+			void onDeactivate() override;
+			void onUpdate() override;
+
+			void onEvent(const std::string& name) override;
+
+			// running scripts
+			bool run(const char* script) override;
+			bool load(const char* filename) override;
+
+		private:
+			data::Storage* m_storage;
+			lua_State* m_state;
+			void release();
+	};
+
+	class LuaScriptProvider : public ScriptProvider {
+		public:
+			LuaScriptProvider(data::Storage* storage);
+
+			virtual ~LuaScriptProvider() = default;
+			LuaScriptEngine* create() override;
+
+		private:
+			data::Storage* m_storage;
+	};
+
+}
+
+#endif //FGGL_INTEGRATIONS_LUA_SCRIPT_LUA_ENGINE_HPP
diff --git a/integrations/lua/include/fggl/script/lua/module.hpp b/integrations/lua/include/fggl/script/lua/module.hpp
new file mode 100644
index 0000000..5f9bd96
--- /dev/null
+++ b/integrations/lua/include/fggl/script/lua/module.hpp
@@ -0,0 +1,46 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 15/10/22.
+//
+
+#ifndef FGGL_INTEGRATIONS_LUA_SCRIPT_LUA_MODULE_HPP
+#define FGGL_INTEGRATIONS_LUA_SCRIPT_LUA_MODULE_HPP
+
+#include "fggl/modules/module.hpp"
+#include "fggl/entity/module.hpp"
+#include "fggl/script/engine.hpp"
+#include "fggl/data/module.hpp"
+
+namespace fggl::script::lua {
+	struct Lua {
+		constexpr static const char* name = "fggl::script::lua";
+		constexpr static const std::array<modules::ModuleService, 1> provides = {
+			script::ScriptProvider::service
+		};
+		constexpr static const std::array<modules::ModuleService, 1> depends = {
+			data::SERVICE_STORAGE
+		};
+		static bool factory(modules::ModuleService name, modules::Services& serviceManager);
+	};
+
+} // namespace fggl::script::lua
+
+namespace fggl::script {
+	using Lua = lua::Lua;
+
+} // namespace fggl::script
+
+#endif //FGGL_INTEGRATIONS_LUA_SCRIPT_LUA_MODULE_HPP
diff --git a/integrations/lua/src/engine.cpp b/integrations/lua/src/engine.cpp
new file mode 100644
index 0000000..984b588
--- /dev/null
+++ b/integrations/lua/src/engine.cpp
@@ -0,0 +1,98 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 15/10/22.
+//
+
+#include "fggl/script/lua/engine.hpp"
+#include "fggl/debug/logging.hpp"
+
+#include <cassert>
+
+namespace fggl::script::lua {
+
+	LuaScriptProvider::LuaScriptProvider(data::Storage *storage) : m_storage(storage) {
+	}
+
+	LuaScriptEngine *LuaScriptProvider::create() {
+		return new LuaScriptEngine(m_storage);
+	}
+
+	LuaScriptEngine::LuaScriptEngine(data::Storage* storage) : m_state(luaL_newstate()), m_storage(storage) {
+		luaL_openlibs(m_state);
+	}
+
+	LuaScriptEngine::~LuaScriptEngine() {
+		release();
+	}
+
+	void LuaScriptEngine::release() {
+		if ( m_state != nullptr ) {
+			lua_close(m_state);
+			m_state = nullptr;
+		}
+	}
+
+	void LuaScriptEngine::onActivate() {
+		lua_getglobal(m_state, "print");
+		lua_pushstring(m_state, "LUA activate triggered");
+		lua_call(m_state, 1, 0);
+	}
+
+	void LuaScriptEngine::onDeactivate() {
+		lua_getglobal(m_state, "print");
+		lua_pushstring(m_state, "LUA deactivate triggered");
+		lua_call(m_state, 1, 0);
+	}
+
+	void LuaScriptEngine::onUpdate() {
+		lua_getglobal(m_state, "print");
+		lua_pushstring(m_state, "LUA update triggered");
+		lua_call(m_state, 1, 0);
+	}
+
+	void LuaScriptEngine::onEvent(const std::string &name) {
+		lua_getglobal(m_state, "print");
+		lua_pushstring(m_state, "LUA event triggered");
+		lua_call(m_state, 1, 0);
+	}
+
+	bool LuaScriptEngine::run(const char *script) {
+		auto result = luaL_dostring(m_state, script);
+		if ( !result ) {
+			fggl::debug::warning("lua error: {}", lua_tostring(m_state, -1));
+		}
+		return result;
+	}
+
+	bool LuaScriptEngine::load(const char *filename) {
+		assert( filename != nullptr);
+		auto path = m_storage->resolvePath(data::StorageType::Data, filename);
+
+		if ( !std::filesystem::exists(path) ) {
+			fggl::debug::warning("lua error: file does not exist: {}", path.c_str());
+			return false;
+		}
+
+		// load the file ( OK = 0 = false because reasons...)
+		auto result = !luaL_dofile(m_state, path.c_str());
+		if ( !result ) {
+			fggl::debug::warning("lua error: {}", lua_tostring(m_state, -1));
+			return false;
+		}
+		return true;
+	}
+
+}
\ No newline at end of file
diff --git a/integrations/lua/src/module.cpp b/integrations/lua/src/module.cpp
new file mode 100644
index 0000000..af4b1e3
--- /dev/null
+++ b/integrations/lua/src/module.cpp
@@ -0,0 +1,35 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 15/10/22.
+//
+
+#include "fggl/script/lua/module.hpp"
+#include "fggl/script/lua/engine.hpp"
+
+namespace fggl::script::lua {
+
+	bool Lua::factory(modules::ModuleService service, modules::Services &serviceManager) {
+
+		if ( service == ScriptProvider::service ) {
+			auto storageService = serviceManager.get<data::Storage>();
+			serviceManager.bind<ScriptProvider,LuaScriptProvider>(storageService);
+			return true;
+		}
+
+		return false;
+	}
+
+}
\ No newline at end of file
-- 
GitLab