Add '/clearobjects quick'
parent
47464c9344
commit
b1428ab4bb
|
@ -848,14 +848,24 @@ core.register_chatcommand("kick", {
|
||||||
})
|
})
|
||||||
|
|
||||||
core.register_chatcommand("clearobjects", {
|
core.register_chatcommand("clearobjects", {
|
||||||
|
params = "[full|quick]",
|
||||||
description = "clear all objects in world",
|
description = "clear all objects in world",
|
||||||
privs = {server=true},
|
privs = {server=true},
|
||||||
func = function(name, param)
|
func = function(name, param)
|
||||||
|
options = {}
|
||||||
|
if param == "" or param == "full" then
|
||||||
|
options.mode = "full"
|
||||||
|
elseif param == "quick" then
|
||||||
|
options.mode = "quick"
|
||||||
|
else
|
||||||
|
return false, "Invalid usage, see /help clearobjects."
|
||||||
|
end
|
||||||
|
|
||||||
core.log("action", name .. " clears all objects.")
|
core.log("action", name .. " clears all objects.")
|
||||||
core.chat_send_all("Clearing all objects. This may take long."
|
core.chat_send_all("Clearing all objects. This may take long."
|
||||||
.. " You may experience a timeout. (by "
|
.. " You may experience a timeout. (by "
|
||||||
.. name .. ")")
|
.. name .. ")")
|
||||||
core.clear_objects()
|
core.clear_objects(options)
|
||||||
core.log("action", "Object clearing done.")
|
core.log("action", "Object clearing done.")
|
||||||
core.chat_send_all("*** Cleared all objects.")
|
core.chat_send_all("*** Cleared all objects.")
|
||||||
end,
|
end,
|
||||||
|
|
|
@ -2046,8 +2046,12 @@ and `minetest.auth_reload` call the authetification handler.
|
||||||
* `minetest.generate_decorations(vm, pos1, pos2)`
|
* `minetest.generate_decorations(vm, pos1, pos2)`
|
||||||
* Generate all registered decorations within the VoxelManip `vm` and in the area from `pos1` to `pos2`.
|
* Generate all registered decorations within the VoxelManip `vm` and in the area from `pos1` to `pos2`.
|
||||||
* `pos1` and `pos2` are optional and default to mapchunk minp and maxp.
|
* `pos1` and `pos2` are optional and default to mapchunk minp and maxp.
|
||||||
* `minetest.clear_objects()`
|
* `minetest.clear_objects([options])`
|
||||||
* clear all objects in the environments
|
* Clear all objects in the environment
|
||||||
|
* Takes an optional table as an argument with the field `mode`.
|
||||||
|
* mode = `"full"`: Load and go through every mapblock, clearing objects (default).
|
||||||
|
* mode = `"quick"`: Clear objects immediately in loaded mapblocks;
|
||||||
|
clear objects in unloaded mapblocks only when the mapblocks are next activated.
|
||||||
* `minetest.emerge_area(pos1, pos2, [callback], [param])`
|
* `minetest.emerge_area(pos1, pos2, [callback], [param])`
|
||||||
* Queue all blocks in the area from `pos1` to `pos2`, inclusive, to be asynchronously
|
* Queue all blocks in the area from `pos1` to `pos2`, inclusive, to be asynchronously
|
||||||
* fetched from memory, loaded from disk, or if inexistent, generates them.
|
* fetched from memory, loaded from disk, or if inexistent, generates them.
|
||||||
|
|
|
@ -354,6 +354,7 @@ ServerEnvironment::ServerEnvironment(ServerMap *map,
|
||||||
m_active_block_interval_overload_skip(0),
|
m_active_block_interval_overload_skip(0),
|
||||||
m_game_time(0),
|
m_game_time(0),
|
||||||
m_game_time_fraction_counter(0),
|
m_game_time_fraction_counter(0),
|
||||||
|
m_last_clear_objects_time(0),
|
||||||
m_recommended_send_interval(0.1),
|
m_recommended_send_interval(0.1),
|
||||||
m_max_lag_estimate(0.1)
|
m_max_lag_estimate(0.1)
|
||||||
{
|
{
|
||||||
|
@ -503,6 +504,7 @@ void ServerEnvironment::saveMeta()
|
||||||
Settings args;
|
Settings args;
|
||||||
args.setU64("game_time", m_game_time);
|
args.setU64("game_time", m_game_time);
|
||||||
args.setU64("time_of_day", getTimeOfDay());
|
args.setU64("time_of_day", getTimeOfDay());
|
||||||
|
args.setU64("last_clear_objects_time", m_last_clear_objects_time);
|
||||||
args.writeLines(ss);
|
args.writeLines(ss);
|
||||||
ss<<"EnvArgsEnd\n";
|
ss<<"EnvArgsEnd\n";
|
||||||
|
|
||||||
|
@ -546,6 +548,13 @@ void ServerEnvironment::loadMeta()
|
||||||
// This is not as important
|
// This is not as important
|
||||||
setTimeOfDay(9000);
|
setTimeOfDay(9000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
m_last_clear_objects_time = args.getU64("last_clear_objects_time");
|
||||||
|
} catch (SettingNotFoundException &e) {
|
||||||
|
// If missing, do as if clearObjects was never called
|
||||||
|
m_last_clear_objects_time = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ActiveABM
|
struct ActiveABM
|
||||||
|
@ -739,13 +748,19 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
|
||||||
// Get time difference
|
// Get time difference
|
||||||
u32 dtime_s = 0;
|
u32 dtime_s = 0;
|
||||||
u32 stamp = block->getTimestamp();
|
u32 stamp = block->getTimestamp();
|
||||||
if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
|
if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
|
||||||
dtime_s = m_game_time - block->getTimestamp();
|
dtime_s = m_game_time - stamp;
|
||||||
dtime_s += additional_dtime;
|
dtime_s += additional_dtime;
|
||||||
|
|
||||||
/*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
|
/*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
|
||||||
<<stamp<<", game time: "<<m_game_time<<std::endl;*/
|
<<stamp<<", game time: "<<m_game_time<<std::endl;*/
|
||||||
|
|
||||||
|
// Remove stored static objects if clearObjects was called since block's timestamp
|
||||||
|
if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
|
||||||
|
block->m_static_objects.m_stored.clear();
|
||||||
|
// do not set changed flag to avoid unnecessary mapblock writes
|
||||||
|
}
|
||||||
|
|
||||||
// Set current time as timestamp
|
// Set current time as timestamp
|
||||||
block->setTimestampNoChangedFlag(m_game_time);
|
block->setTimestampNoChangedFlag(m_game_time);
|
||||||
|
|
||||||
|
@ -858,22 +873,22 @@ void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f po
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerEnvironment::clearAllObjects()
|
void ServerEnvironment::clearObjects(ClearObjectsMode mode)
|
||||||
{
|
{
|
||||||
infostream<<"ServerEnvironment::clearAllObjects(): "
|
infostream << "ServerEnvironment::clearObjects(): "
|
||||||
<<"Removing all active objects"<<std::endl;
|
<< "Removing all active objects" << std::endl;
|
||||||
std::vector<u16> objects_to_remove;
|
std::vector<u16> objects_to_remove;
|
||||||
for(std::map<u16, ServerActiveObject*>::iterator
|
for (std::map<u16, ServerActiveObject*>::iterator
|
||||||
i = m_active_objects.begin();
|
i = m_active_objects.begin();
|
||||||
i != m_active_objects.end(); ++i) {
|
i != m_active_objects.end(); ++i) {
|
||||||
ServerActiveObject* obj = i->second;
|
ServerActiveObject* obj = i->second;
|
||||||
if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
|
if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
|
||||||
continue;
|
continue;
|
||||||
u16 id = i->first;
|
u16 id = i->first;
|
||||||
// Delete static object if block is loaded
|
// Delete static object if block is loaded
|
||||||
if(obj->m_static_exists){
|
if (obj->m_static_exists) {
|
||||||
MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
|
MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
|
||||||
if(block){
|
if (block) {
|
||||||
block->m_static_objects.remove(id);
|
block->m_static_objects.remove(id);
|
||||||
block->raiseModified(MOD_STATE_WRITE_NEEDED,
|
block->raiseModified(MOD_STATE_WRITE_NEEDED,
|
||||||
MOD_REASON_CLEAR_ALL_OBJECTS);
|
MOD_REASON_CLEAR_ALL_OBJECTS);
|
||||||
|
@ -881,7 +896,7 @@ void ServerEnvironment::clearAllObjects()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If known by some client, don't delete immediately
|
// If known by some client, don't delete immediately
|
||||||
if(obj->m_known_by_count > 0){
|
if (obj->m_known_by_count > 0) {
|
||||||
obj->m_pending_deactivation = true;
|
obj->m_pending_deactivation = true;
|
||||||
obj->m_removed = true;
|
obj->m_removed = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -893,39 +908,46 @@ void ServerEnvironment::clearAllObjects()
|
||||||
m_script->removeObjectReference(obj);
|
m_script->removeObjectReference(obj);
|
||||||
|
|
||||||
// Delete active object
|
// Delete active object
|
||||||
if(obj->environmentDeletes())
|
if (obj->environmentDeletes())
|
||||||
delete obj;
|
delete obj;
|
||||||
// Id to be removed from m_active_objects
|
// Id to be removed from m_active_objects
|
||||||
objects_to_remove.push_back(id);
|
objects_to_remove.push_back(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove references from m_active_objects
|
// Remove references from m_active_objects
|
||||||
for(std::vector<u16>::iterator i = objects_to_remove.begin();
|
for (std::vector<u16>::iterator i = objects_to_remove.begin();
|
||||||
i != objects_to_remove.end(); ++i) {
|
i != objects_to_remove.end(); ++i) {
|
||||||
m_active_objects.erase(*i);
|
m_active_objects.erase(*i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get list of loaded blocks
|
// Get list of loaded blocks
|
||||||
std::vector<v3s16> loaded_blocks;
|
std::vector<v3s16> loaded_blocks;
|
||||||
infostream<<"ServerEnvironment::clearAllObjects(): "
|
infostream << "ServerEnvironment::clearObjects(): "
|
||||||
<<"Listing all loaded blocks"<<std::endl;
|
<< "Listing all loaded blocks" << std::endl;
|
||||||
m_map->listAllLoadedBlocks(loaded_blocks);
|
m_map->listAllLoadedBlocks(loaded_blocks);
|
||||||
infostream<<"ServerEnvironment::clearAllObjects(): "
|
infostream << "ServerEnvironment::clearObjects(): "
|
||||||
<<"Done listing all loaded blocks: "
|
<< "Done listing all loaded blocks: "
|
||||||
<<loaded_blocks.size()<<std::endl;
|
<< loaded_blocks.size()<<std::endl;
|
||||||
|
|
||||||
// Get list of loadable blocks
|
// Get list of loadable blocks
|
||||||
std::vector<v3s16> loadable_blocks;
|
std::vector<v3s16> loadable_blocks;
|
||||||
infostream<<"ServerEnvironment::clearAllObjects(): "
|
if (mode == CLEAR_OBJECTS_MODE_FULL) {
|
||||||
<<"Listing all loadable blocks"<<std::endl;
|
infostream << "ServerEnvironment::clearObjects(): "
|
||||||
m_map->listAllLoadableBlocks(loadable_blocks);
|
<< "Listing all loadable blocks" << std::endl;
|
||||||
infostream<<"ServerEnvironment::clearAllObjects(): "
|
m_map->listAllLoadableBlocks(loadable_blocks);
|
||||||
<<"Done listing all loadable blocks: "
|
infostream << "ServerEnvironment::clearObjects(): "
|
||||||
<<loadable_blocks.size()
|
<< "Done listing all loadable blocks: "
|
||||||
<<", now clearing"<<std::endl;
|
<< loadable_blocks.size() << std::endl;
|
||||||
|
} else {
|
||||||
|
loadable_blocks = loaded_blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
infostream << "ServerEnvironment::clearObjects(): "
|
||||||
|
<< "Now clearing objects in " << loadable_blocks.size()
|
||||||
|
<< " blocks" << std::endl;
|
||||||
|
|
||||||
// Grab a reference on each loaded block to avoid unloading it
|
// Grab a reference on each loaded block to avoid unloading it
|
||||||
for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
|
for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
|
||||||
i != loaded_blocks.end(); ++i) {
|
i != loaded_blocks.end(); ++i) {
|
||||||
v3s16 p = *i;
|
v3s16 p = *i;
|
||||||
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
||||||
|
@ -934,24 +956,27 @@ void ServerEnvironment::clearAllObjects()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove objects in all loadable blocks
|
// Remove objects in all loadable blocks
|
||||||
u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
|
u32 unload_interval = U32_MAX;
|
||||||
unload_interval = MYMAX(unload_interval, 1);
|
if (mode == CLEAR_OBJECTS_MODE_FULL) {
|
||||||
|
unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
|
||||||
|
unload_interval = MYMAX(unload_interval, 1);
|
||||||
|
}
|
||||||
u32 report_interval = loadable_blocks.size() / 10;
|
u32 report_interval = loadable_blocks.size() / 10;
|
||||||
u32 num_blocks_checked = 0;
|
u32 num_blocks_checked = 0;
|
||||||
u32 num_blocks_cleared = 0;
|
u32 num_blocks_cleared = 0;
|
||||||
u32 num_objs_cleared = 0;
|
u32 num_objs_cleared = 0;
|
||||||
for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
|
for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
|
||||||
i != loadable_blocks.end(); ++i) {
|
i != loadable_blocks.end(); ++i) {
|
||||||
v3s16 p = *i;
|
v3s16 p = *i;
|
||||||
MapBlock *block = m_map->emergeBlock(p, false);
|
MapBlock *block = m_map->emergeBlock(p, false);
|
||||||
if(!block){
|
if (!block) {
|
||||||
errorstream<<"ServerEnvironment::clearAllObjects(): "
|
errorstream << "ServerEnvironment::clearObjects(): "
|
||||||
<<"Failed to emerge block "<<PP(p)<<std::endl;
|
<< "Failed to emerge block " << PP(p) << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
u32 num_stored = block->m_static_objects.m_stored.size();
|
u32 num_stored = block->m_static_objects.m_stored.size();
|
||||||
u32 num_active = block->m_static_objects.m_active.size();
|
u32 num_active = block->m_static_objects.m_active.size();
|
||||||
if(num_stored != 0 || num_active != 0){
|
if (num_stored != 0 || num_active != 0) {
|
||||||
block->m_static_objects.m_stored.clear();
|
block->m_static_objects.m_stored.clear();
|
||||||
block->m_static_objects.m_active.clear();
|
block->m_static_objects.m_active.clear();
|
||||||
block->raiseModified(MOD_STATE_WRITE_NEEDED,
|
block->raiseModified(MOD_STATE_WRITE_NEEDED,
|
||||||
|
@ -961,23 +986,23 @@ void ServerEnvironment::clearAllObjects()
|
||||||
}
|
}
|
||||||
num_blocks_checked++;
|
num_blocks_checked++;
|
||||||
|
|
||||||
if(report_interval != 0 &&
|
if (report_interval != 0 &&
|
||||||
num_blocks_checked % report_interval == 0){
|
num_blocks_checked % report_interval == 0) {
|
||||||
float percent = 100.0 * (float)num_blocks_checked /
|
float percent = 100.0 * (float)num_blocks_checked /
|
||||||
loadable_blocks.size();
|
loadable_blocks.size();
|
||||||
infostream<<"ServerEnvironment::clearAllObjects(): "
|
infostream << "ServerEnvironment::clearObjects(): "
|
||||||
<<"Cleared "<<num_objs_cleared<<" objects"
|
<< "Cleared " << num_objs_cleared << " objects"
|
||||||
<<" in "<<num_blocks_cleared<<" blocks ("
|
<< " in " << num_blocks_cleared << " blocks ("
|
||||||
<<percent<<"%)"<<std::endl;
|
<< percent << "%)" << std::endl;
|
||||||
}
|
}
|
||||||
if(num_blocks_checked % unload_interval == 0){
|
if (num_blocks_checked % unload_interval == 0) {
|
||||||
m_map->unloadUnreferencedBlocks();
|
m_map->unloadUnreferencedBlocks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_map->unloadUnreferencedBlocks();
|
m_map->unloadUnreferencedBlocks();
|
||||||
|
|
||||||
// Drop references that were added above
|
// Drop references that were added above
|
||||||
for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
|
for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
|
||||||
i != loaded_blocks.end(); ++i) {
|
i != loaded_blocks.end(); ++i) {
|
||||||
v3s16 p = *i;
|
v3s16 p = *i;
|
||||||
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
||||||
|
@ -985,9 +1010,11 @@ void ServerEnvironment::clearAllObjects()
|
||||||
block->refDrop();
|
block->refDrop();
|
||||||
}
|
}
|
||||||
|
|
||||||
infostream<<"ServerEnvironment::clearAllObjects(): "
|
m_last_clear_objects_time = m_game_time;
|
||||||
<<"Finished: Cleared "<<num_objs_cleared<<" objects"
|
|
||||||
<<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
|
infostream << "ServerEnvironment::clearObjects(): "
|
||||||
|
<< "Finished: Cleared " << num_objs_cleared << " objects"
|
||||||
|
<< " in " << num_blocks_cleared << " blocks" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerEnvironment::step(float dtime)
|
void ServerEnvironment::step(float dtime)
|
||||||
|
|
|
@ -203,6 +203,18 @@ public:
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Operation mode for ServerEnvironment::clearObjects()
|
||||||
|
*/
|
||||||
|
enum ClearObjectsMode {
|
||||||
|
// Load and go through every mapblock, clearing objects
|
||||||
|
CLEAR_OBJECTS_MODE_FULL,
|
||||||
|
|
||||||
|
// Clear objects immediately in loaded mapblocks;
|
||||||
|
// clear objects in unloaded mapblocks only when the mapblocks are next activated.
|
||||||
|
CLEAR_OBJECTS_MODE_QUICK,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The server-side environment.
|
The server-side environment.
|
||||||
|
|
||||||
|
@ -319,8 +331,8 @@ public:
|
||||||
// Find all active objects inside a radius around a point
|
// Find all active objects inside a radius around a point
|
||||||
void getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius);
|
void getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius);
|
||||||
|
|
||||||
// Clear all objects, loading and going through every MapBlock
|
// Clear objects, loading and going through every MapBlock
|
||||||
void clearAllObjects();
|
void clearObjects(ClearObjectsMode mode);
|
||||||
|
|
||||||
// This makes stuff happen
|
// This makes stuff happen
|
||||||
void step(f32 dtime);
|
void step(f32 dtime);
|
||||||
|
@ -410,6 +422,10 @@ private:
|
||||||
u32 m_game_time;
|
u32 m_game_time;
|
||||||
// A helper variable for incrementing the latter
|
// A helper variable for incrementing the latter
|
||||||
float m_game_time_fraction_counter;
|
float m_game_time_fraction_counter;
|
||||||
|
// Time of last clearObjects call (game time).
|
||||||
|
// When a mapblock older than this is loaded, its objects are cleared.
|
||||||
|
u32 m_last_clear_objects_time;
|
||||||
|
// Active block modifiers
|
||||||
std::vector<ABMWithState> m_abms;
|
std::vector<ABMWithState> m_abms;
|
||||||
// An interval for generally sending object positions and stuff
|
// An interval for generally sending object positions and stuff
|
||||||
float m_recommended_send_interval;
|
float m_recommended_send_interval;
|
||||||
|
|
|
@ -36,6 +36,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "emerge.h"
|
#include "emerge.h"
|
||||||
#include "pathfinder.h"
|
#include "pathfinder.h"
|
||||||
|
|
||||||
|
struct EnumString ModApiEnvMod::es_ClearObjectsMode[] =
|
||||||
|
{
|
||||||
|
{CLEAR_OBJECTS_MODE_FULL, "full"},
|
||||||
|
{CLEAR_OBJECTS_MODE_QUICK, "quick"},
|
||||||
|
{0, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
@ -727,13 +734,20 @@ int ModApiEnvMod::l_get_voxel_manip(lua_State *L)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear_objects()
|
// clear_objects([options])
|
||||||
// clear all objects in the environment
|
// clear all objects in the environment
|
||||||
|
// where options = {mode = "full" or "quick"}
|
||||||
int ModApiEnvMod::l_clear_objects(lua_State *L)
|
int ModApiEnvMod::l_clear_objects(lua_State *L)
|
||||||
{
|
{
|
||||||
GET_ENV_PTR;
|
GET_ENV_PTR;
|
||||||
|
|
||||||
env->clearAllObjects();
|
ClearObjectsMode mode = CLEAR_OBJECTS_MODE_FULL;
|
||||||
|
if (lua_istable(L, 1)) {
|
||||||
|
mode = (ClearObjectsMode)getenumfield(L, 1, "mode",
|
||||||
|
ModApiEnvMod::es_ClearObjectsMode, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
env->clearObjects(mode);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,8 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void Initialize(lua_State *L, int top);
|
static void Initialize(lua_State *L, int top);
|
||||||
|
|
||||||
|
static struct EnumString es_ClearObjectsMode[];
|
||||||
};
|
};
|
||||||
|
|
||||||
class LuaABM : public ActiveBlockModifier {
|
class LuaABM : public ActiveBlockModifier {
|
||||||
|
|
Loading…
Reference in New Issue