Skip to content

Commit a5a5bea

Browse files
committed
feat(scripting): add system and startupsystem to lua
1 parent eb40463 commit a5a5bea

12 files changed

Lines changed: 234 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727
- Support for MaskTrait types in JSON (de)serializer (#1507, **@RiscadoA**).
2828
- Render timers in metrics panel (#1565, **@tomas7770**).
2929
- Custom module loader for lua scripts (#1517, **@mkuritsu**).
30+
- Added possibility of creating basic systems in lua scripts (#1518, **@mkuritsu**)
3031

3132
### Changed
3233

bindings/lua/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ message("# Building cubos::bindings::lua samples " ${CUBOS_LUA_SAMPLES})
1010
# ----------------------- Define lua bindings source files -----------------------
1111

1212
set(LUA_BINDINGS_SOURCE
13+
"src/cubos.cpp"
1314
"src/plugin.cpp"
15+
"src/systems.cpp"
1416
)
1517

1618
# ------------------------ Configure lua bindings target -------------------------

bindings/lua/samples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ endfunction()
5353
# Add samples
5454
make_sample(DIR "hello_world" ASSETS)
5555
make_sample(DIR "modules" ASSETS)
56+
make_sample(DIR "systems" ASSETS)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <cubos/bindings/lua/plugin.hpp>
2+
3+
#include <cubos/engine/prelude.hpp>
4+
#include <cubos/engine/settings/plugin.hpp>
5+
6+
using namespace cubos::engine;
7+
using namespace cubos::bindings::lua;
8+
9+
int main(int argc, char** argv)
10+
{
11+
Cubos cubos{argc, argv};
12+
13+
cubos.plugin(settingsPlugin);
14+
cubos.plugin(luaBindingsPlugin);
15+
16+
cubos.startupSystem("set ShouldQuit to false").call([](ShouldQuit& quit) { quit.value = false; });
17+
18+
cubos.startupSystem("configure Assets").before(settingsTag).call([](Settings& settings) {
19+
settings.setString("scripts.lua.app.osPath", APP_SCRIPTS_PATH);
20+
});
21+
22+
cubos.run();
23+
return 0;
24+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
local counter = 0
2+
3+
cubos.startupSystem("print startup"):call(function()
4+
print("Startup lua system called!")
5+
end)
6+
7+
cubos.system("inc counter"):call(function()
8+
counter = counter + 1
9+
if counter == 1000000 then
10+
print("Counter reached 1000000!")
11+
counter = 0
12+
end
13+
end)

bindings/lua/src/cubos.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#include <string_view>
2+
3+
#include <cubos/core/reflection/external/string_view.hpp>
4+
#include <cubos/core/tel/logging.hpp>
5+
6+
#include <cubos/engine/prelude.hpp>
7+
8+
#include "cubos.hpp"
9+
#include "systems.hpp"
10+
11+
using cubos::engine::Cubos;
12+
13+
static void pushFunction(lua_State* state, std::string_view name, lua_CFunction fn)
14+
{
15+
lua_pushcfunction(state, fn);
16+
lua_setfield(state, -2, name.data());
17+
}
18+
19+
void cubos::bindings::lua::injectCubos(lua_State* state, Cubos* cubos)
20+
{
21+
lua_pushstring(state, "cubos");
22+
lua_pushlightuserdata(state, cubos);
23+
lua_settable(state, LUA_REGISTRYINDEX);
24+
25+
lua_newtable(state);
26+
pushFunction(state, "startupSystem", startupSystem);
27+
pushFunction(state, "system", system);
28+
lua_setglobal(state, "cubos");
29+
}
30+
31+
Cubos* cubos::bindings::lua::getCubos(lua_State* state)
32+
{
33+
lua_pushstring(state, "cubos");
34+
lua_gettable(state, LUA_REGISTRYINDEX);
35+
return static_cast<Cubos*>(lua_touserdata(state, -1));
36+
}

bindings/lua/src/cubos.hpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/// @dir
2+
/// @brief @ref lua-bindings-plugin plugin directory.
3+
4+
/// @file
5+
/// @brief Base cubos lua api.
6+
/// @ingroup lua-bindings-plugin
7+
#pragma once
8+
9+
#include <lua.hpp>
10+
11+
#include <cubos/engine/prelude.hpp>
12+
13+
namespace cubos::bindings::lua
14+
{
15+
/// @brief Injects the cubos table into the lua VM.
16+
/// @param state The current lua state.
17+
/// @param cubos The @ref cubos::engine::Cubos instance to be stored.
18+
/// @ingroup lua-bindings-plugin
19+
void injectCubos(lua_State* state, cubos::engine::Cubos* cubos);
20+
21+
/// @brief Gets the injected cubos table from the lua VM.
22+
/// @param state The current lua state.
23+
/// @returns Pointer to the stored @ref cubos::engine::Cubos instance.
24+
/// @ingroup lua-bindings-plugin
25+
cubos::engine::Cubos* getCubos(lua_State* state);
26+
} // namespace cubos::bindings::lua

bindings/lua/src/plugin.cpp

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
#include <algorithm>
2+
#include <string>
23

34
#include <cubos/bindings/lua/plugin.hpp>
45
#include <lua.hpp>
56

67
#include <cubos/core/data/fs/file_system.hpp>
78
#include <cubos/core/data/fs/standard_archive.hpp>
9+
#include <cubos/core/ecs/system/arguments/plugins.hpp>
810

911
#include <cubos/engine/settings/plugin.hpp>
1012

13+
#include "cubos.hpp"
14+
#include "cubos/engine/prelude.hpp"
15+
1116
using namespace cubos::engine;
1217
using cubos::core::data::FileSystem;
1318
using cubos::core::data::StandardArchive;
19+
using cubos::core::ecs::Plugins;
1420

15-
static constexpr std::string_view ScriptsMountPoint = "/scripts/lua";
21+
static constexpr std::string ScriptsMountPoint = "/scripts/lua";
1622

1723
namespace
1824
{
@@ -34,7 +40,7 @@ namespace
3440
};
3541
} // namespace
3642

37-
static void loadScripts(std::string_view path, State& state)
43+
static void loadScripts(std::string_view path, lua_State* state)
3844
{
3945
auto directory = FileSystem::find(path);
4046
if (directory != nullptr && directory->directory())
@@ -50,9 +56,9 @@ static void loadScripts(std::string_view path, State& state)
5056
{
5157
std::string moduleName = std::string{child->path().substr(
5258
ScriptsMountPoint.length() + 1, child->path().length() - 5 - ScriptsMountPoint.length())};
53-
std::replace(moduleName.begin(), moduleName.end(), '/', '.');
59+
std::ranges::replace(moduleName, '/', '.');
5460
std::string execString = std::string{"require('"} + moduleName + "')";
55-
luaL_dostring(state.luaState, execString.c_str());
61+
luaL_dostring(state, execString.c_str());
5662
}
5763
child = child->sibling();
5864
}
@@ -63,7 +69,7 @@ static int searchModule(lua_State* state)
6369
{
6470
const char* moduleName = luaL_checkstring(state, 1);
6571
std::string module = moduleName;
66-
std::replace(module.begin(), module.end(), '.', '/');
72+
std::ranges::replace(module, '.', '/');
6773
module = module + ".lua";
6874

6975
lua_pushstring(state, "scriptsPath");
@@ -91,7 +97,7 @@ static int searchModule(lua_State* state)
9197
static void setupCustomLoader(lua_State* state)
9298
{
9399
lua_pushstring(state, "scriptsPath");
94-
lua_pushstring(state, ScriptsMountPoint.data());
100+
lua_pushstring(state, ScriptsMountPoint.c_str());
95101
lua_settable(state, LUA_REGISTRYINDEX);
96102

97103
lua_getglobal(state, "package");
@@ -108,15 +114,15 @@ void cubos::bindings::lua::luaBindingsPlugin(Cubos& cubos)
108114

109115
cubos.uninitResource<State>();
110116

111-
cubos.startupSystem("initialize lua bindings").before(settingsTag).call([](Commands cmds) {
117+
cubos.startupSystem("initialize lua bindings").before(settingsTag).call([&cubos](Commands cmds) {
112118
lua_State* state = luaL_newstate();
113119
luaL_openlibs(state);
114120
setupCustomLoader(state);
115-
// TODO: add custom cubos functions here
121+
injectCubos(state, &cubos);
116122
cmds.emplaceResource<State>(state);
117123
});
118124

119-
cubos.startupSystem("load lua scripts").after(settingsTag).call([](Settings& settings, State& state) {
125+
cubos.startupSystem("load lua scripts").after(settingsTag).call([](Settings& settings, Plugins plugins) {
120126
std::string scriptsPath = settings.getString("scripts.lua.app.osPath", "");
121127
if (!scriptsPath.empty())
122128
{
@@ -129,8 +135,11 @@ void cubos::bindings::lua::luaBindingsPlugin(Cubos& cubos)
129135
{
130136
CUBOS_ERROR("Couldn't mount scripts directory with path {}", scriptsPath);
131137
}
132-
133-
loadScripts(ScriptsMountPoint, state);
138+
(void)plugins;
139+
plugins.add([](Cubos& other) {
140+
auto& state = other.world()->resource<State>();
141+
loadScripts(ScriptsMountPoint, state.luaState);
142+
});
134143
}
135144
});
136145
}

bindings/lua/src/systems.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include "systems.hpp"
2+
3+
#include <cubos/engine/prelude.hpp>
4+
5+
#include "cubos.hpp"
6+
7+
using cubos::bindings::lua::getCubos;
8+
using cubos::bindings::lua::SystemType;
9+
using cubos::engine::Cubos;
10+
11+
static int luaSystemCall(lua_State* state)
12+
{
13+
luaL_checktype(state, 1, LUA_TTABLE);
14+
luaL_checktype(state, 2, LUA_TFUNCTION);
15+
int ref = luaL_ref(state, LUA_REGISTRYINDEX);
16+
17+
Cubos* cubos = getCubos(state);
18+
19+
lua_getfield(state, 1, "systemName");
20+
const char* systemName = lua_tostring(state, -1);
21+
lua_getfield(state, 1, "systemType");
22+
auto type = static_cast<SystemType>(lua_tonumber(state, -1));
23+
switch (type)
24+
{
25+
case SystemType::Startup:
26+
cubos->startupSystem(systemName).call([state, ref]() {
27+
lua_rawgeti(state, LUA_REGISTRYINDEX, ref);
28+
lua_pcall(state, 0, 0, 0);
29+
});
30+
break;
31+
32+
case SystemType::Update:
33+
cubos->system(systemName).call([state, ref]() {
34+
lua_rawgeti(state, LUA_REGISTRYINDEX, ref);
35+
lua_pcall(state, 0, 0, 0);
36+
});
37+
break;
38+
}
39+
return 0;
40+
}
41+
42+
static void pushSystem(lua_State* state, const char* systemName, SystemType type)
43+
{
44+
lua_newtable(state);
45+
lua_pushstring(state, systemName);
46+
lua_setfield(state, -2, "systemName");
47+
lua_pushnumber(state, static_cast<int>(type));
48+
lua_setfield(state, -2, "systemType");
49+
lua_pushcfunction(state, luaSystemCall);
50+
lua_setfield(state, -2, "call");
51+
}
52+
53+
namespace cubos::bindings::lua
54+
{
55+
int startupSystem(lua_State* state)
56+
{
57+
luaL_checktype(state, 1, LUA_TSTRING);
58+
const char* systemName = lua_tostring(state, 1);
59+
pushSystem(state, systemName, SystemType::Startup);
60+
return 1;
61+
};
62+
63+
int system(lua_State* state)
64+
{
65+
luaL_checktype(state, 1, LUA_TSTRING);
66+
const char* systemName = lua_tostring(state, 1);
67+
pushSystem(state, systemName, SystemType::Update);
68+
return 1;
69+
};
70+
} // namespace cubos::bindings::lua

bindings/lua/src/systems.hpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// @dir
2+
/// @brief @ref lua-bindings-plugin plugin directory.
3+
4+
/// @file
5+
/// @brief Systems lua api.
6+
/// @ingroup lua-bindings-plugin
7+
#pragma once
8+
9+
#include <cstdint>
10+
11+
#include <lua.hpp>
12+
13+
namespace cubos::bindings::lua
14+
{
15+
/// @brief Possible system types that can be registered
16+
/// @ingroup lua-bindings-plugin
17+
enum class SystemType : uint8_t
18+
{
19+
Startup = 0,
20+
Update = 1
21+
};
22+
23+
/// @brief Lua api function to register a startup system (executed only once).
24+
/// @param state The current lua state.
25+
/// @ingroup lua-bindings-plugin
26+
int startupSystem(lua_State* state);
27+
28+
/// @brief Lua api function to register a system (executed more than once).
29+
/// @param state The current lua state.
30+
/// @ingroup lua-bindings-plugin
31+
int system(lua_State* state);
32+
} // namespace cubos::bindings::lua

0 commit comments

Comments
 (0)