Merge pull request #352 from MihailRis/add-rules

Add rules
This commit is contained in:
MihailRis 2024-11-06 20:00:50 +03:00 committed by GitHub
commit a414aec473
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 304 additions and 57 deletions

View File

@ -52,6 +52,22 @@ function submit(text)
add_to_history(text) add_to_history(text)
setup_variables() setup_variables()
text = text:trim()
local name
for s in text:gmatch("%S+") do
name = s
break
end
if name == nil then
name = text
end
if not rules.get("cheat-commands") and table.has(console.cheats, name) then
console.log("cheat commands are disabled")
document.prompt.text = ""
document.prompt.focused = true
return
end
document.log.caret = -1 document.log.caret = -1
local status, result = pcall(console.execute, text) local status, result = pcall(console.execute, text)
if result then if result then

View File

@ -28,17 +28,17 @@ console.add_command(
function(args, kwargs) function(args, kwargs)
local name = args[1] local name = args[1]
if #name == 0 then if #name == 0 then
local commands = console.get_commands_list() local commands = console.get_commands_list()
table.sort(commands) table.sort(commands)
local str = "Available commands:" local str = "Available commands:"
for i,k in ipairs(commands) do for i,k in ipairs(commands) do
str = str .. "\n " .. build_scheme(console.get_command_info(k)) if rules.get("cheat-commands") or not table.has(console.cheats, k) then
str = str .. "\n " .. build_scheme(console.get_command_info(k))
end
end end
return str .. "\nuse 'help <command>'" return str .. "\nuse 'help <command>'"
end end
local command = console.get_command_info(name) local command = console.get_command_info(name)
@ -223,3 +223,45 @@ console.add_command(
fragment:place({x, y, z}, rotation) fragment:place({x, y, z}, rotation)
end end
) )
console.add_command(
"rule.set name:str value:bool",
"Set rule value",
function(args, kwargs)
local name = args[1]
local value = args[2]
rules.set(name, value)
return "rule '"..name.."' set to "..tostring(value)
end
)
console.add_command(
"rule.list",
"Show registered rules list",
function(args, kwargs)
local names = ""
for name, rule in pairs(rules.rules) do
if #names > 0 then
names = names .. "\n "
else
names = " "
end
local value = rule.value
if value == nil then
value = "not set"
end
names = names .. name .. ":\t" .. tostring(value)
end
return "registered rules:\n" .. names
end
)
console.cheats = {
"blocks.fill",
"tp",
"fragment.place",
"time.set",
"time.daycycle",
"entity.despawn",
"player.respawn"
}

View File

@ -103,6 +103,9 @@ function time.post_runnable(runnable)
table.insert(__post_runnables, runnable) table.insert(__post_runnables, runnable)
end end
--- Console library extension ---
console.cheats = {}
local log_element = Document.new("core:console").log local log_element = Document.new("core:console").log
function console.log(...) function console.log(...)
local args = {...} local args = {...}
@ -168,6 +171,119 @@ end
math.randomseed(time.uptime() * 1536227939) math.randomseed(time.uptime() * 1536227939)
rules = {nexid = 1, rules = {}}
local _rules = rules
function _rules.get_rule(name)
local rule = _rules.rules[name]
if rule == nil then
rule = {listeners={}}
_rules.rules[name] = rule
end
return rule
end
function _rules.get(name)
local rule = _rules.rules[name]
if rule == nil then
return nil
end
return rule.value
end
function _rules.set(name, value)
local rule = _rules.get_rule(name)
rule.value = value
for _, handler in pairs(rule.listeners) do
handler(value)
end
end
function _rules.reset(name)
local rule = _rules.get_rule(name)
_rules.set(rule.default)
end
function _rules.listen(name, handler)
local rule = _rules.get_rule(name)
local id = _rules.nexid
_rules.nextid = _rules.nexid + 1
rule.listeners[utf8.encode(id)] = handler
return id
end
function _rules.create(name, value, handler)
local rule = _rules.get_rule(name)
rule.default = value
local handlerid
if handler ~= nil then
handlerid = _rules.listen(name, handler)
end
if _rules.get(name) == nil then
_rules.set(name, value)
else
handler(_rules.get(name))
end
return handlerid
end
function _rules.unlisten(name, id)
local rule = _rules.get_rule(name)
rule.listeners[utf8.encode(id)] = nil
end
function _rules.clear()
_rules.rules = {}
_rules.nextid = 1
_rules.create("cheat-commands", true)
end
function __vc_create_hud_rules()
_rules.create("show-content-access", hud._is_content_access(), function(value)
hud._set_content_access(value)
end)
_rules.create("allow-flight", true, function(value)
input.set_enabled("player.flight", value)
end)
_rules.create("allow-noclip", true, function(value)
input.set_enabled("player.noclip", value)
end)
_rules.create("allow-destruct", true, function(value)
input.set_enabled("player.attack", value)
end)
_rules.create("allow-cheat-movement", true, function(value)
input.set_enabled("movement.cheat", value)
end)
_rules.create("allow-debug-cheats", true, function(value)
hud._set_debug_cheats(value)
end)
end
local RULES_FILE = "world:rules.toml"
function __vc_on_world_open()
if not file.exists(RULES_FILE) then
return
end
local rule_values = toml.parse(file.read(RULES_FILE))
for name, value in pairs(rule_values) do
_rules.set(name, value)
end
end
function __vc_on_world_save()
local rule_values = {}
for name, rule in pairs(rules.rules) do
rule_values[name] = rule.value
end
file.write(RULES_FILE, toml.tostring(rule_values))
end
function __vc_on_world_quit()
_rules.clear()
end
-- --------- Deprecated functions ------ -- -- --------- Deprecated functions ------ --
local function wrap_deprecated(func, name, alternatives) local function wrap_deprecated(func, name, alternatives)
return function (...) return function (...)

View File

@ -360,7 +360,7 @@ void Engine::loadContent() {
for (auto& pack : contentPacks) { for (auto& pack : contentPacks) {
ContentLoader(&pack, contentBuilder, *resPaths).load(); ContentLoader(&pack, contentBuilder, *resPaths).load();
load_configs(pack.folder); load_configs(pack.folder);
} }
content = contentBuilder.build(); content = contentBuilder.build();
@ -421,8 +421,6 @@ double Engine::getDelta() const {
} }
void Engine::setScreen(std::shared_ptr<Screen> screen) { void Engine::setScreen(std::shared_ptr<Screen> screen) {
// unblock all bindings
Events::enableBindings();
// reset audio channels (stop all sources) // reset audio channels (stop all sources)
audio::reset_channel(audio::get_channel_index("regular")); audio::reset_channel(audio::get_channel_index("regular"));
audio::reset_channel(audio::get_channel_index("ambient")); audio::reset_channel(audio::get_channel_index("ambient"));

View File

@ -42,7 +42,8 @@ static std::shared_ptr<Label> create_label(wstringsupplier supplier) {
std::shared_ptr<UINode> create_debug_panel( std::shared_ptr<UINode> create_debug_panel(
Engine* engine, Engine* engine,
Level* level, Level* level,
Player* player Player* player,
bool allowDebugCheats
) { ) {
auto panel = std::make_shared<Panel>(glm::vec2(300, 200), glm::vec4(5.0f), 2.0f); auto panel = std::make_shared<Panel>(glm::vec2(300, 200), glm::vec4(5.0f), 2.0f);
panel->setId("hud.debug-panel"); panel->setId("hud.debug-panel");
@ -162,16 +163,20 @@ std::shared_ptr<UINode> create_debug_panel(
box->setTextSupplier([=]() { box->setTextSupplier([=]() {
return util::to_wstring(player->getPosition()[ax], 2); return util::to_wstring(player->getPosition()[ax], 2);
}); });
box->setTextConsumer([=](const std::wstring& text) { if (allowDebugCheats) {
try { box->setTextConsumer([=](const std::wstring& text) {
glm::vec3 position = player->getPosition(); try {
position[ax] = std::stoi(text); glm::vec3 position = player->getPosition();
player->teleport(position); position[ax] = std::stoi(text);
} catch (std::exception& _){ player->teleport(position);
} } catch (std::exception& _){
}); }
box->setOnEditStart([=](){ });
boxRef->setText(std::to_wstring(static_cast<int>(player->getPosition()[ax]))); }
box->setOnEditStart([=]() {
boxRef->setText(
std::to_wstring(static_cast<int>(player->getPosition()[ax]))
);
}); });
box->setSize(glm::vec2(230, 27)); box->setSize(glm::vec2(230, 27));
@ -188,13 +193,13 @@ std::shared_ptr<UINode> create_debug_panel(
util::lfill(std::to_wstring(minute), 2, L'0'); util::lfill(std::to_wstring(minute), 2, L'0');
return L"time: "+timeString; return L"time: "+timeString;
})); }));
{ if (allowDebugCheats) {
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 1.0f, 0.005f, 8); auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 1.0f, 0.005f, 8);
bar->setSupplier([&]() {return worldInfo.daytime;}); bar->setSupplier([&]() {return worldInfo.daytime;});
bar->setConsumer([&](double val) {worldInfo.daytime = val;}); bar->setConsumer([&](double val) {worldInfo.daytime = val;});
panel->add(bar); panel->add(bar);
} }
{ if (allowDebugCheats) {
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 0.0f, 0.005f, 8); auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 0.0f, 0.005f, 8);
bar->setSupplier([&]() {return worldInfo.fog;}); bar->setSupplier([&]() {return worldInfo.fog;});
bar->setConsumer([&](double val) {worldInfo.fog = val;}); bar->setConsumer([&](double val) {worldInfo.fog = val;});

View File

@ -60,9 +60,10 @@ bool Hud::showGeneratorMinimap = false;
// implemented in debug_panel.cpp // implemented in debug_panel.cpp
extern std::shared_ptr<UINode> create_debug_panel( extern std::shared_ptr<UINode> create_debug_panel(
Engine* engine, Engine* engine,
Level* level, Level* level,
Player* player Player* player,
bool allowDebugCheats
); );
HudElement::HudElement( HudElement::HudElement(
@ -149,7 +150,8 @@ std::shared_ptr<InventoryView> Hud::createHotbar() {
static constexpr uint WORLDGEN_IMG_SIZE = 128U; static constexpr uint WORLDGEN_IMG_SIZE = 128U;
Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player) Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player)
: assets(engine->getAssets()), : engine(engine),
assets(engine->getAssets()),
gui(engine->getGUI()), gui(engine->getGUI()),
frontend(frontend), frontend(frontend),
player(player), player(player),
@ -175,12 +177,14 @@ Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player)
uicamera->perspective = false; uicamera->perspective = false;
uicamera->flipped = true; uicamera->flipped = true;
debugPanel = create_debug_panel(engine, frontend->getLevel(), player); debugPanel = create_debug_panel(
engine, frontend->getLevel(), player, allowDebugCheats
);
debugPanel->setZIndex(2); debugPanel->setZIndex(2);
gui->add(debugPanel);
gui->add(darkOverlay); gui->add(darkOverlay);
gui->add(hotbarView); gui->add(hotbarView);
gui->add(debugPanel);
gui->add(contentAccessPanel); gui->add(contentAccessPanel);
auto dplotter = std::make_shared<Plotter>(350, 250, 2000, 16); auto dplotter = std::make_shared<Plotter>(350, 250, 2000, 16);
@ -348,7 +352,7 @@ void Hud::update(bool visible) {
} }
glm::vec2 invSize = contentAccessPanel->getSize(); glm::vec2 invSize = contentAccessPanel->getSize();
contentAccessPanel->setVisible(inventoryView != nullptr); contentAccessPanel->setVisible(inventoryView != nullptr && showContentPanel);
contentAccessPanel->setSize(glm::vec2(invSize.x, Window::height)); contentAccessPanel->setSize(glm::vec2(invSize.x, Window::height));
contentAccess->setMinSize(glm::vec2(1, Window::height)); contentAccess->setMinSize(glm::vec2(1, Window::height));
hotbarView->setVisible(visible && !(secondUI && !inventoryView)); hotbarView->setVisible(visible && !(secondUI && !inventoryView));
@ -553,7 +557,9 @@ void Hud::updateElementsPosition(const Viewport& viewport) {
const uint height = viewport.getHeight(); const uint height = viewport.getHeight();
if (inventoryOpen) { if (inventoryOpen) {
float caWidth = inventoryView ? contentAccess->getSize().x : 0.0f; float caWidth = inventoryView && showContentPanel
? contentAccess->getSize().x
: 0.0f;
contentAccessPanel->setPos(glm::vec2(width-caWidth, 0)); contentAccessPanel->setPos(glm::vec2(width-caWidth, 0));
glm::vec2 invSize = inventoryView ? inventoryView->getSize() : glm::vec2(); glm::vec2 invSize = inventoryView ? inventoryView->getSize() : glm::vec2();
@ -628,3 +634,22 @@ std::shared_ptr<Inventory> Hud::getBlockInventory() {
} }
return blockUI->getInventory(); return blockUI->getInventory();
} }
bool Hud::isContentAccess() const {
return showContentPanel;
}
void Hud::setContentAccess(bool flag) {
showContentPanel = flag;
}
void Hud::setDebugCheats(bool flag) {
allowDebugCheats = flag;
gui->remove(debugPanel);
debugPanel = create_debug_panel(
engine, frontend->getLevel(), player, allowDebugCheats
);
debugPanel->setZIndex(2);
gui->add(debugPanel);
}

View File

@ -69,6 +69,7 @@ public:
}; };
class Hud : public util::ObjectsKeeper { class Hud : public util::ObjectsKeeper {
Engine* engine;
Assets* assets; Assets* assets;
std::unique_ptr<Camera> uicamera; std::unique_ptr<Camera> uicamera;
gui::GUI* gui; gui::GUI* gui;
@ -105,7 +106,10 @@ class Hud : public util::ObjectsKeeper {
glm::ivec3 blockPos {}; glm::ivec3 blockPos {};
/// @brief Id of the block open (used to detect block destruction or replacement) /// @brief Id of the block open (used to detect block destruction or replacement)
blockid_t currentblockid = 0; blockid_t currentblockid = 0;
/// @brief Show content access panel
bool showContentPanel = true;
/// @brief Provide cheat controllers to the debug panel
bool allowDebugCheats = true;
/// @brief UI element will be dynamicly positioned near to inventory or in screen center /// @brief UI element will be dynamicly positioned near to inventory or in screen center
std::shared_ptr<gui::UINode> secondUI = nullptr; std::shared_ptr<gui::UINode> secondUI = nullptr;
@ -172,6 +176,12 @@ public:
std::shared_ptr<Inventory> getBlockInventory(); std::shared_ptr<Inventory> getBlockInventory();
bool isContentAccess() const;
void setContentAccess(bool flag);
void setDebugCheats(bool flag);
static bool showGeneratorMinimap; static bool showGeneratorMinimap;
/// @brief Runtime updating debug visualization texture /// @brief Runtime updating debug visualization texture

View File

@ -87,6 +87,8 @@ void LevelScreen::initializePack(ContentPackRuntime* pack) {
LevelScreen::~LevelScreen() { LevelScreen::~LevelScreen() {
saveWorldPreview(); saveWorldPreview();
scripting::on_frontend_close(); scripting::on_frontend_close();
// unblock all bindings
Events::enableBindings();
controller->onWorldQuit(); controller->onWorldQuit();
engine->getPaths()->setCurrentWorldFolder(fs::path()); engine->getPaths()->setCurrentWorldFolder(fs::path());
} }

View File

@ -504,7 +504,7 @@ void TextBox::performEditingKeyboardEvents(keycode key) {
paste(L"\n"); paste(L"\n");
} else { } else {
defocus(); defocus();
if (validate()) { if (validate() && consumer) {
consumer(label->getText()); consumer(label->getText());
} }
} }

View File

@ -22,21 +22,21 @@ namespace scripting {
} }
using namespace scripting; using namespace scripting;
static int l_hud_open_inventory(lua::State*) { static int l_open_inventory(lua::State*) {
if (!hud->isInventoryOpen()) { if (!hud->isInventoryOpen()) {
hud->openInventory(); hud->openInventory();
} }
return 0; return 0;
} }
static int l_hud_close_inventory(lua::State*) { static int l_close_inventory(lua::State*) {
if (hud->isInventoryOpen()) { if (hud->isInventoryOpen()) {
hud->closeInventory(); hud->closeInventory();
} }
return 0; return 0;
} }
static int l_hud_open_block(lua::State* L) { static int l_open_block(lua::State* L) {
auto x = lua::tointeger(L, 1); auto x = lua::tointeger(L, 1);
auto y = lua::tointeger(L, 2); auto y = lua::tointeger(L, 2);
auto z = lua::tointeger(L, 3); auto z = lua::tointeger(L, 3);
@ -69,7 +69,7 @@ static int l_hud_open_block(lua::State* L) {
return 2; return 2;
} }
static int l_hud_show_overlay(lua::State* L) { static int l_show_overlay(lua::State* L) {
auto name = lua::require_string(L, 1); auto name = lua::require_string(L, 1);
bool playerInventory = lua::toboolean(L, 2); bool playerInventory = lua::toboolean(L, 2);
@ -93,29 +93,29 @@ static UiDocument* require_layout(const char* name) {
return layout; return layout;
} }
static int l_hud_open_permanent(lua::State* L) { static int l_open_permanent(lua::State* L) {
auto layout = require_layout(lua::require_string(L, 1)); auto layout = require_layout(lua::require_string(L, 1));
hud->openPermanent(layout); hud->openPermanent(layout);
return 0; return 0;
} }
static int l_hud_close(lua::State* L) { static int l_close(lua::State* L) {
auto layout = require_layout(lua::require_string(L, 1)); auto layout = require_layout(lua::require_string(L, 1));
hud->remove(layout->getRoot()); hud->remove(layout->getRoot());
return 0; return 0;
} }
static int l_hud_pause(lua::State*) { static int l_pause(lua::State*) {
hud->setPause(true); hud->setPause(true);
return 0; return 0;
} }
static int l_hud_resume(lua::State*) { static int l_resume(lua::State*) {
hud->setPause(false); hud->setPause(false);
return 0; return 0;
} }
static int l_hud_get_block_inventory(lua::State* L) { static int l_get_block_inventory(lua::State* L) {
auto inventory = hud->getBlockInventory(); auto inventory = hud->getBlockInventory();
if (inventory == nullptr) { if (inventory == nullptr) {
return lua::pushinteger(L, 0); return lua::pushinteger(L, 0);
@ -124,30 +124,47 @@ static int l_hud_get_block_inventory(lua::State* L) {
} }
} }
static int l_hud_get_player(lua::State* L) { static int l_get_player(lua::State* L) {
auto player = hud->getPlayer(); auto player = hud->getPlayer();
return lua::pushinteger(L, player->getId()); return lua::pushinteger(L, player->getId());
} }
static int l_hud_is_paused(lua::State* L) { static int l_is_paused(lua::State* L) {
return lua::pushboolean(L, hud->isPause()); return lua::pushboolean(L, hud->isPause());
} }
static int l_hud_is_inventory_open(lua::State* L) { static int l_is_inventory_open(lua::State* L) {
return lua::pushboolean(L, hud->isInventoryOpen()); return lua::pushboolean(L, hud->isInventoryOpen());
} }
static int l_is_content_access(lua::State* L) {
return lua::pushboolean(L, hud->isContentAccess());
}
static int l_set_content_access(lua::State* L) {
hud->setContentAccess(lua::toboolean(L, 1));
return 0;
}
static int l_set_debug_cheats(lua::State* L) {
hud->setDebugCheats(lua::toboolean(L, 1));
return 0;
}
const luaL_Reg hudlib[] = { const luaL_Reg hudlib[] = {
{"open_inventory", lua::wrap<l_hud_open_inventory>}, {"open_inventory", lua::wrap<l_open_inventory>},
{"close_inventory", lua::wrap<l_hud_close_inventory>}, {"close_inventory", lua::wrap<l_close_inventory>},
{"open_block", lua::wrap<l_hud_open_block>}, {"open_block", lua::wrap<l_open_block>},
{"open_permanent", lua::wrap<l_hud_open_permanent>}, {"open_permanent", lua::wrap<l_open_permanent>},
{"show_overlay", lua::wrap<l_hud_show_overlay>}, {"show_overlay", lua::wrap<l_show_overlay>},
{"get_block_inventory", lua::wrap<l_hud_get_block_inventory>}, {"get_block_inventory", lua::wrap<l_get_block_inventory>},
{"close", lua::wrap<l_hud_close>}, {"close", lua::wrap<l_close>},
{"pause", lua::wrap<l_hud_pause>}, {"pause", lua::wrap<l_pause>},
{"resume", lua::wrap<l_hud_resume>}, {"resume", lua::wrap<l_resume>},
{"is_paused", lua::wrap<l_hud_is_paused>}, {"is_paused", lua::wrap<l_is_paused>},
{"is_inventory_open", lua::wrap<l_hud_is_inventory_open>}, {"is_inventory_open", lua::wrap<l_is_inventory_open>},
{"get_player", lua::wrap<l_hud_get_player>}, {"get_player", lua::wrap<l_get_player>},
{"_is_content_access", lua::wrap<l_is_content_access>},
{"_set_content_access", lua::wrap<l_set_content_access>},
{"_set_debug_cheats", lua::wrap<l_set_debug_cheats>},
{NULL, NULL}}; {NULL, NULL}};

View File

@ -150,7 +150,7 @@ void scripting::initialize(Engine* engine) {
void scripting::process_post_runnables() { void scripting::process_post_runnables() {
auto L = lua::get_main_state(); auto L = lua::get_main_state();
if (lua::getglobal(L, "__process_post_runnables")) { if (lua::getglobal(L, "__process_post_runnables")) {
lua::call_nothrow(L, 0); lua::call_nothrow(L, 0, 0);
} }
} }
@ -160,9 +160,13 @@ void scripting::on_world_load(LevelController* controller) {
scripting::indices = level->content->getIndices(); scripting::indices = level->content->getIndices();
scripting::blocks = controller->getBlocksController(); scripting::blocks = controller->getBlocksController();
scripting::controller = controller; scripting::controller = controller;
load_script("world.lua", false);
auto L = lua::get_main_state(); auto L = lua::get_main_state();
if (lua::getglobal(L, "__vc_on_world_open")) {
lua::call_nothrow(L, 0, 0);
}
load_script("world.lua", false);
for (auto& pack : scripting::engine->getContentPacks()) { for (auto& pack : scripting::engine->getContentPacks()) {
lua::emit_event(L, pack.id + ":.worldopen"); lua::emit_event(L, pack.id + ":.worldopen");
} }
@ -180,6 +184,9 @@ void scripting::on_world_save() {
for (auto& pack : scripting::engine->getContentPacks()) { for (auto& pack : scripting::engine->getContentPacks()) {
lua::emit_event(L, pack.id + ":.worldsave"); lua::emit_event(L, pack.id + ":.worldsave");
} }
if (lua::getglobal(L, "__vc_on_world_save")) {
lua::call_nothrow(L, 0, 0);
}
} }
void scripting::on_world_quit() { void scripting::on_world_quit() {
@ -187,6 +194,9 @@ void scripting::on_world_quit() {
for (auto& pack : scripting::engine->getContentPacks()) { for (auto& pack : scripting::engine->getContentPacks()) {
lua::emit_event(L, pack.id + ":.worldquit"); lua::emit_event(L, pack.id + ":.worldquit");
} }
if (lua::getglobal(L, "__vc_on_world_quit")) {
lua::call_nothrow(L, 0, 0);
}
scripting::level = nullptr; scripting::level = nullptr;
scripting::content = nullptr; scripting::content = nullptr;
scripting::indices = nullptr; scripting::indices = nullptr;

View File

@ -21,8 +21,14 @@ void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) {
scripting::hud = hud; scripting::hud = hud;
scripting::renderer = renderer; scripting::renderer = renderer;
lua::openlib(lua::get_main_state(), "hud", hudlib); auto L = lua::get_main_state();
lua::openlib(lua::get_main_state(), "particles", particleslib);
lua::openlib(L, "hud", hudlib);
lua::openlib(L, "particles", particleslib);
if (lua::getglobal(L, "__vc_create_hud_rules")) {
lua::call_nothrow(L, 0, 0);
}
for (auto& pack : engine->getContentPacks()) { for (auto& pack : engine->getContentPacks()) {
lua::emit_event( lua::emit_event(