v3s16 pos;
MapNode n;
content_t c;
- lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
+ auto it = getLBMsIntroducedAfter(stamp);
for (; it != m_lbm_lookup.end(); ++it) {
// Cache previous version to speedup lookup which has a very high performance
// penalty on each call
- content_t previous_c{};
- std::vector<LoadingBlockModifierDef *> *lbm_list = nullptr;
+ content_t previous_c = CONTENT_IGNORE;
+ const std::vector<LoadingBlockModifierDef *> *lbm_list = nullptr;
for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) {
- n = block->getNodeNoEx(pos);
+ n = block->getNodeNoCheck(pos);
c = n.getContent();
// If content_t are not matching perform an LBM lookup
if (previous_c != c) {
- lbm_list = (std::vector<LoadingBlockModifierDef *> *)
- it->second.lookup(c);
+ lbm_list = it->second.lookup(c);
previous_c = c;
}
// Go through new list
for (v3s16 p : newlist) {
// If not on old list, it's been added
- if(m_list.find(p) == m_list.end())
+ if (m_list.find(p) == m_list.end())
blocks_added.insert(p);
}
/*
Update m_list
*/
- m_list.clear();
- for (v3s16 p : newlist) {
- m_list.insert(p);
- }
+ m_list = std::move(newlist);
}
/*
static std::random_device seed;
ServerEnvironment::ServerEnvironment(ServerMap *map,
- ServerScripting *scriptIface, Server *server,
- const std::string &path_world):
+ ServerScripting *script_iface, Server *server,
+ const std::string &path_world, MetricsBackend *mb):
Environment(server),
m_map(map),
- m_script(scriptIface),
+ m_script(script_iface),
m_server(server),
m_path_world(path_world),
m_rgen(seed())
+{
+ m_step_time_counter = mb->addCounter(
+ "minetest_env_step_time", "Time spent in environment step (in microseconds)");
+
+ m_active_block_gauge = mb->addGauge(
+ "minetest_env_active_blocks", "Number of active blocks");
+
+ m_active_object_gauge = mb->addGauge(
+ "minetest_env_active_objects", "Number of active objects");
+}
+
+void ServerEnvironment::init()
{
// Determine which database backend to use
- std::string conf_path = path_world + DIR_DELIM + "world.mt";
+ std::string conf_path = m_path_world + DIR_DELIM + "world.mt";
Settings conf;
std::string player_backend_name = "sqlite3";
// If we open world.mt read the backend configurations.
if (succeeded) {
+ // Check that the world's blocksize matches the compiled MAP_BLOCKSIZE
+ u16 blocksize = 16;
+ conf.getU16NoEx("blocksize", blocksize);
+ if (blocksize != MAP_BLOCKSIZE) {
+ throw BaseException(std::string("The map's blocksize is not supported."));
+ }
+
// Read those values before setting defaults
bool player_backend_exists = conf.exists("player_backend");
bool auth_backend_exists = conf.exists("auth_backend");
<< "please read http://wiki.minetest.net/Database_backends." << std::endl;
}
- m_player_database = openPlayerDatabase(player_backend_name, path_world, conf);
- m_auth_database = openAuthDatabase(auth_backend_name, path_world, conf);
+ m_player_database = openPlayerDatabase(player_backend_name, m_path_world, conf);
+ m_auth_database = openAuthDatabase(auth_backend_name, m_path_world, conf);
}
ServerEnvironment::~ServerEnvironment()
deactivateFarObjects(true);
// Drop/delete map
- m_map->drop();
+ if (m_map)
+ m_map->drop();
// Delete ActiveBlockModifiers
for (ABMWithState &m_abm : m_abms) {
void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
const std::string &str_reason, bool reconnect)
{
- for (RemotePlayer *player : m_players) {
- m_server->DenyAccessVerCompliant(player->getPeerId(),
- player->protocol_version, reason, str_reason, reconnect);
- }
+ for (RemotePlayer *player : m_players)
+ m_server->DenyAccess(player->getPeerId(), reason, str_reason, reconnect);
}
void ServerEnvironment::saveLoadedPlayers(bool force)
/* Add object to environment */
addActiveObject(playersao);
+ // Update active blocks quickly for a bit so objects in those blocks appear on the client
+ m_fast_active_block_divider = 10;
+
return playersao;
}
void ServerEnvironment::saveMeta()
{
+ if (!m_meta_loaded)
+ return;
+
std::string path = m_path_world + DIR_DELIM "env_meta.txt";
// Open file and serialize
std::ostringstream ss(std::ios_base::binary);
- Settings args;
+ Settings args("EnvArgsEnd");
args.setU64("game_time", m_game_time);
args.setU64("time_of_day", getTimeOfDay());
args.setU64("last_clear_objects_time", m_last_clear_objects_time);
m_lbm_mgr.createIntroductionTimesString());
args.setU64("day_count", m_day_count);
args.writeLines(ss);
- ss<<"EnvArgsEnd\n";
if(!fs::safeWriteToFile(path, ss.str()))
{
void ServerEnvironment::loadMeta()
{
+ SANITY_CHECK(!m_meta_loaded);
+ m_meta_loaded = true;
+
// If file doesn't exist, load default environment metadata
if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
infostream << "ServerEnvironment: Loading default environment metadata"
throw SerializationError("Couldn't load env meta");
}
- Settings args;
+ Settings args("EnvArgsEnd");
- if (!args.parseConfigLines(is, "EnvArgsEnd")) {
+ if (!args.parseConfigLines(is)) {
throw SerializationError("ServerEnvironment::loadMeta(): "
"EnvArgsEnd not found!");
}
int chance;
std::vector<content_t> required_neighbors;
bool check_required_neighbors; // false if required_neighbors is known to be empty
+ s16 min_y;
+ s16 max_y;
};
class ABMHandler
actual_interval = trigger_interval;
}
float chance = abm->getTriggerChance();
- if(chance == 0)
+ if (chance == 0)
chance = 1;
ActiveABM aabm;
aabm.abm = abm;
if (abm->getSimpleCatchUp()) {
float intervals = actual_interval / trigger_interval;
- if(intervals == 0)
+ if (intervals == 0)
continue;
aabm.chance = chance / intervals;
- if(aabm.chance == 0)
+ if (aabm.chance == 0)
aabm.chance = 1;
} else {
aabm.chance = chance;
}
+ // y limits
+ aabm.min_y = abm->getMinY();
+ aabm.max_y = abm->getMaxY();
// Trigger neighbors
const std::vector<std::string> &required_neighbors_s =
delete aabms;
}
- // Find out how many objects the given block and its neighbours contain.
+ // Find out how many objects the given block and its neighbors contain.
// Returns the number of objects in the block, and also in 'wider' the
- // number of objects in the block and all its neighbours. The latter
- // may an estimate if any neighbours are unloaded.
+ // number of objects in the block and all its neighbors. The latter
+ // may an estimate if any neighbors are unloaded.
u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
{
wider = 0;
wider_unknown_count++;
continue;
}
- wider += block2->m_static_objects.m_active.size()
- + block2->m_static_objects.m_stored.size();
+ wider += block2->m_static_objects.size();
}
// Extrapolate
- u32 active_object_count = block->m_static_objects.m_active.size();
- u32 wider_known_count = 3*3*3 - wider_unknown_count;
+ u32 active_object_count = block->m_static_objects.getActiveSize();
+ u32 wider_known_count = 3 * 3 * 3 - wider_unknown_count;
wider += wider_unknown_count * wider / wider_known_count;
return active_object_count;
-
}
void apply(MapBlock *block, int &blocks_scanned, int &abms_run, int &blocks_cached)
{
- if(m_aabms.empty() || block->isDummy())
+ if (m_aabms.empty())
return;
// Check the content type cache first
for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
{
- const MapNode &n = block->getNodeUnsafe(p0);
+ const MapNode &n = block->getNodeNoCheck(p0);
content_t c = n.getContent();
// Cache content types as we go
if (!block->contents_cached && !block->do_not_cache_contents) {
v3s16 p = p0 + block->getPosRelative();
for (ActiveABM &aabm : *m_aabms[c]) {
+ if ((p.Y < aabm.min_y) || (p.Y > aabm.max_y))
+ continue;
+
if (myrand() % aabm.chance != 0)
continue;
if (block->isValidPosition(p1)) {
// if the neighbor is found on the same map block
// get it straight from there
- const MapNode &n = block->getNodeUnsafe(p1);
+ const MapNode &n = block->getNodeNoCheck(p1);
c = n.getContent();
} else {
// otherwise consult the map
// 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();
+ block->m_static_objects.clearStored();
// do not set changed flag to avoid unnecessary mapblock writes
}
m_lbm_mgr.applyLBMs(this, block, stamp);
// Run node timers
- std::vector<NodeTimer> elapsed_timers =
- block->m_node_timers.step((float)dtime_s);
- if (!elapsed_timers.empty()) {
- MapNode n;
- for (const NodeTimer &elapsed_timer : elapsed_timers) {
- n = block->getNodeNoEx(elapsed_timer.position);
- v3s16 p = elapsed_timer.position + block->getPosRelative();
- if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
- block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
- elapsed_timer.position));
- }
- }
+ block->step((float)dtime_s, [&](v3s16 p, MapNode n, f32 d) -> bool {
+ return m_script->node_on_timer(p, n, d);
+ });
}
void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
return true;
}
+u8 ServerEnvironment::findSunlight(v3s16 pos) const
+{
+ // Directions for neighboring nodes with specified order
+ static const v3s16 dirs[] = {
+ v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1),
+ v3s16(0, -1, 0), v3s16(0, 1, 0)
+ };
+
+ const NodeDefManager *ndef = m_server->ndef();
+
+ // found_light remembers the highest known sunlight value at pos
+ u8 found_light = 0;
+
+ struct stack_entry {
+ v3s16 pos;
+ s16 dist;
+ };
+ std::stack<stack_entry> stack;
+ stack.push({pos, 0});
+
+ std::unordered_map<s64, s8> dists;
+ dists[MapDatabase::getBlockAsInteger(pos)] = 0;
+
+ while (!stack.empty()) {
+ struct stack_entry e = stack.top();
+ stack.pop();
+
+ v3s16 currentPos = e.pos;
+ s8 dist = e.dist + 1;
+
+ for (const v3s16& off : dirs) {
+ v3s16 neighborPos = currentPos + off;
+ s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos);
+
+ // Do not walk neighborPos multiple times unless the distance to the start
+ // position is shorter
+ auto it = dists.find(neighborHash);
+ if (it != dists.end() && dist >= it->second)
+ continue;
+
+ // Position to walk
+ bool is_position_ok;
+ MapNode node = m_map->getNode(neighborPos, &is_position_ok);
+ if (!is_position_ok) {
+ // This happens very rarely because the map at currentPos is loaded
+ m_map->emergeBlock(neighborPos, false);
+ node = m_map->getNode(neighborPos, &is_position_ok);
+ if (!is_position_ok)
+ continue; // not generated
+ }
+
+ const ContentFeatures &def = ndef->get(node);
+ if (!def.sunlight_propagates) {
+ // Do not test propagation here again
+ dists[neighborHash] = -1;
+ continue;
+ }
+
+ // Sunlight could have come from here
+ dists[neighborHash] = dist;
+ u8 daylight = node.param1 & 0x0f;
+
+ // In the special case where sunlight shines from above and thus
+ // does not decrease with upwards distance, daylight is always
+ // bigger than nightlight, which never reaches 15
+ int possible_finlight = daylight - dist;
+ if (possible_finlight <= found_light) {
+ // Light from here cannot make a brighter light at currentPos than
+ // found_light
+ continue;
+ }
+
+ u8 nightlight = node.param1 >> 4;
+ if (daylight > nightlight) {
+ // Found a valid daylight
+ found_light = possible_finlight;
+ } else {
+ // Sunlight may be darker, so walk the neighbors
+ stack.push({neighborPos, dist});
+ }
+ }
+ }
+ return found_light;
+}
+
void ServerEnvironment::clearObjects(ClearObjectsMode mode)
{
infostream << "ServerEnvironment::clearObjects(): "
// If known by some client, don't delete immediately
if (obj->m_known_by_count > 0) {
- obj->m_pending_removal = true;
+ obj->markForRemoval();
return false;
}
<< "Failed to emerge block " << PP(p) << std::endl;
continue;
}
- u32 num_stored = block->m_static_objects.m_stored.size();
- u32 num_active = block->m_static_objects.m_active.size();
- if (num_stored != 0 || num_active != 0) {
- block->m_static_objects.m_stored.clear();
- block->m_static_objects.m_active.clear();
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- MOD_REASON_CLEAR_ALL_OBJECTS);
- num_objs_cleared += num_stored + num_active;
+
+ u32 num_cleared = block->clearObjects();
+ if (num_cleared > 0) {
+ num_objs_cleared += num_cleared;
num_blocks_cleared++;
}
num_blocks_checked++;
void ServerEnvironment::step(float dtime)
{
ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
+ const auto start_time = porting::getTimeUs();
+
/* Step time of day */
stepTimeOfDay(dtime);
}
}
- if (m_database_check_interval.step(dtime, 10.0f)) {
- m_auth_database->pingDatabase();
- m_player_database->pingDatabase();
- m_map->pingDatabase();
- }
/*
Manage active block list
*/
- if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
+ if (m_active_blocks_mgmt_interval.step(dtime, m_cache_active_block_mgmt_interval / m_fast_active_block_divider)) {
ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
+
/*
Get player block positions
*/
std::vector<PlayerSAO*> players;
- for (RemotePlayer *player: m_players) {
+ players.reserve(m_players.size());
+ for (RemotePlayer *player : m_players) {
// Ignore disconnected players
if (player->getPeerId() == PEER_ID_INEXISTENT)
continue;
for (const v3s16 &p: blocks_added) {
MapBlock *block = m_map->getBlockOrEmerge(p);
if (!block) {
- m_active_blocks.m_list.erase(p);
- m_active_blocks.m_abm_list.erase(p);
+ // TODO: The blocks removed here will only be picked up again
+ // on the next cycle. To minimize the latency of objects being
+ // activated we could remember the blocks pending activating
+ // and activate them instantly as soon as they're loaded.
+ m_active_blocks.remove(p);
continue;
}
activateBlock(block);
}
+
+ // Some blocks may be removed again by the code above so do this here
+ m_active_block_gauge->set(m_active_blocks.size());
+
+ if (m_fast_active_block_divider > 1)
+ --m_fast_active_block_divider;
}
/*
MOD_REASON_BLOCK_EXPIRED);
// Run node timers
- std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
- if (!elapsed_timers.empty()) {
- MapNode n;
- v3s16 p2;
- for (const NodeTimer &elapsed_timer: elapsed_timers) {
- n = block->getNodeNoEx(elapsed_timer.position);
- p2 = elapsed_timer.position + block->getPosRelative();
- if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
- block->setNodeTimer(NodeTimer(
- elapsed_timer.timeout, 0, elapsed_timer.position));
- }
- }
- }
+ block->step(dtime, [&](v3s16 p, MapNode n, f32 d) -> bool {
+ return m_script->node_on_timer(p, n, d);
+ });
}
}
std::shuffle(output.begin(), output.end(), m_rgen);
int i = 0;
- // The time budget for ABMs is 20%.
- u32 max_time_ms = m_cache_abm_interval * 1000 / 5;
+ // determine the time budget for ABMs
+ u32 max_time_ms = m_cache_abm_interval * 1000 * m_cache_abm_time_budget;
for (const v3s16 &p : output) {
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if (!block)
*/
m_script->environment_Step(dtime);
+ m_script->stepAsync();
+
/*
Step active objects
*/
send_recommended = true;
}
- auto cb_state = [this, dtime, send_recommended] (ServerActiveObject *obj) {
+ u32 object_count = 0;
+
+ auto cb_state = [&](ServerActiveObject *obj) {
if (obj->isGone())
return;
+ object_count++;
// Step object
obj->step(dtime, send_recommended);
obj->dumpAOMessagesToQueue(m_active_object_messages);
};
m_ao_manager.step(dtime, cb_state);
+
+ m_active_object_gauge->set(object_count);
}
/*
// Send outdated detached inventories
m_server->sendDetachedInventories(PEER_ID_INEXISTENT, true);
+
+ const auto end_time = porting::getTimeUs();
+ m_step_time_counter->increment(end_time - start_time);
+}
+
+ServerEnvironment::BlockStatus ServerEnvironment::getBlockStatus(v3s16 blockpos)
+{
+ if (m_active_blocks.contains(blockpos))
+ return BS_ACTIVE;
+
+ const MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
+ if (block)
+ return BS_LOADED;
+
+ if (m_map->isBlockInQueue(blockpos))
+ return BS_EMERGING;
+
+ return BS_UNKNOWN;
}
u32 ServerEnvironment::addParticleSpawner(float exptime)
if (!block)
return;
- for (auto &so_it : block->m_static_objects.m_active) {
+ for (auto &so_it : block->m_static_objects.getAllActives()) {
// Get the ServerActiveObject counterpart to this StaticObject
ServerActiveObject *sao = m_ao_manager.getActiveObject(so_it.first);
if (!sao) {
}
}
-ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
+bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest)
{
- if(m_active_object_messages.empty())
- return ActiveObjectMessage(0);
+ if (m_active_object_messages.empty())
+ return false;
- ActiveObjectMessage message = m_active_object_messages.front();
+ *dest = std::move(m_active_object_messages.front());
m_active_object_messages.pop();
- return message;
+ return true;
}
void ServerEnvironment::getSelectedActiveObjects(
const v3f line_vector = shootline_on_map.getVector();
for (auto obj : objs) {
+ if (obj->isGone())
+ continue;
aabb3f selection_box;
if (!obj->getSelectionBox(&selection_box))
continue;
v3f pos = obj->getBasePosition();
-
- aabb3f offsetted_box(selection_box.MinEdge + pos,
- selection_box.MaxEdge + pos);
+ v3f rel_pos = shootline_on_map.start - pos;
v3f current_intersection;
- v3s16 current_normal;
- if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
- ¤t_intersection, ¤t_normal)) {
+ v3f current_normal;
+ v3f current_raw_normal;
+
+ ObjectProperties *props = obj->accessObjectProperties();
+ bool collision;
+ UnitSAO* usao = dynamic_cast<UnitSAO*>(obj);
+ if (props->rotate_selectionbox && usao != nullptr) {
+ collision = boxLineCollision(selection_box, usao->getTotalRotation(),
+ rel_pos, line_vector, ¤t_intersection, ¤t_normal, ¤t_raw_normal);
+ } else {
+ collision = boxLineCollision(selection_box, rel_pos, line_vector,
+ ¤t_intersection, ¤t_normal);
+ current_raw_normal = current_normal;
+ }
+ if (collision) {
+ current_intersection += pos;
objects.emplace_back(
- (s16) obj->getId(), current_intersection, current_normal,
+ (s16) obj->getId(), current_intersection, current_normal, current_raw_normal,
(current_intersection - shootline_on_map.start).getLengthSQ());
}
}
// Add to the block where the object is located in
v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
MapBlock *block = m_map->emergeBlock(blockpos);
- if(block){
- block->m_static_objects.m_active[object->getId()] = s_obj;
+ if (block) {
+ block->m_static_objects.setActive(object->getId(), s_obj);
object->m_static_exists = true;
object->m_static_block = blockpos;
{
ScopeProfiler sp(g_profiler, "ServerEnvironment::removeRemovedObjects()", SPT_AVG);
- auto clear_cb = [this] (ServerActiveObject *obj, u16 id) {
+ auto clear_cb = [this](ServerActiveObject *obj, u16 id) {
// This shouldn't happen but check it
if (!obj) {
errorstream << "ServerEnvironment::removeRemovedObjects(): "
/*
Delete static data from block if removed
*/
- if (obj->m_pending_removal)
+ if (obj->isPendingRemoval())
deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
// If still known by clients, don't actually remove. On some future
// invocation this will be 0, which is when removal will continue.
- if(obj->m_known_by_count > 0)
+ if (obj->m_known_by_count > 0)
return false;
/*
Move static data from active to stored if deactivated
*/
- if (!obj->m_pending_removal && obj->m_static_exists) {
- MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
- if (block) {
- const auto i = block->m_static_objects.m_active.find(id);
- if (i != block->m_static_objects.m_active.end()) {
- block->m_static_objects.m_stored.push_back(i->second);
- block->m_static_objects.m_active.erase(id);
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
- } else {
+ if (!obj->isPendingRemoval() && obj->m_static_exists) {
+ if (MapBlock *block = m_map->emergeBlock(obj->m_static_block, false)) {
+ if (!block->storeActiveObject(id)) {
warningstream << "ServerEnvironment::removeRemovedObjects(): "
<< "id=" << id << " m_static_exists=true but "
<< "static data doesn't actually exist in "
static void print_hexdump(std::ostream &o, const std::string &data)
{
const int linelength = 16;
- for(int l=0; ; l++){
+ for (int l = 0;; l++) {
int i0 = linelength * l;
bool at_end = false;
int thislinelength = linelength;
- if(i0 + thislinelength > (int)data.size()){
+ if (i0 + thislinelength > (int)data.size()) {
thislinelength = data.size() - i0;
at_end = true;
}
- for(int di=0; di<linelength; di++){
+ for (int di = 0; di < linelength; di++) {
int i = i0 + di;
char buf[4];
- if(di<thislinelength)
+ if (di < thislinelength)
porting::mt_snprintf(buf, sizeof(buf), "%.2x ", data[i]);
else
porting::mt_snprintf(buf, sizeof(buf), " ");
- o<<buf;
+ o << buf;
}
- o<<" ";
- for(int di=0; di<thislinelength; di++){
+ o << " ";
+ for (int di = 0; di < thislinelength; di++) {
int i = i0 + di;
- if(data[i] >= 32)
- o<<data[i];
+ if (data[i] >= 32)
+ o << data[i];
else
- o<<".";
+ o << ".";
}
- o<<std::endl;
- if(at_end)
+ o << std::endl;
+ if (at_end)
break;
}
}
*/
void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
{
- if(block == NULL)
- return;
-
- // Ignore if no stored objects (to not set changed flag)
- if(block->m_static_objects.m_stored.empty())
+ if (block == NULL)
return;
- verbosestream<<"ServerEnvironment::activateObjects(): "
- <<"activating objects of block "<<PP(block->getPos())
- <<" ("<<block->m_static_objects.m_stored.size()
- <<" objects)"<<std::endl;
- bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
- if (large_amount) {
- errorstream<<"suspiciously large amount of objects detected: "
- <<block->m_static_objects.m_stored.size()<<" in "
- <<PP(block->getPos())
- <<"; removing all of them."<<std::endl;
- // Clear stored list
- block->m_static_objects.m_stored.clear();
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- MOD_REASON_TOO_MANY_OBJECTS);
+ if (!block->onObjectsActivation())
return;
- }
// Activate stored objects
std::vector<StaticObject> new_stored;
- for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
+ for (const StaticObject &s_obj : block->m_static_objects.getAllStored()) {
// Create an active object from the data
- ServerActiveObject *obj = createSAO((ActiveObjectType) s_obj.type, s_obj.pos,
- s_obj.data);
+ ServerActiveObject *obj =
+ createSAO((ActiveObjectType)s_obj.type, s_obj.pos, s_obj.data);
// If couldn't create object, store static data back.
if (!obj) {
- errorstream<<"ServerEnvironment::activateObjects(): "
- <<"failed to create active object from static object "
- <<"in block "<<PP(s_obj.pos/BS)
- <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
+ errorstream << "ServerEnvironment::activateObjects(): "
+ << "failed to create active object from static object "
+ << "in block " << PP(s_obj.pos / BS)
+ << " type=" << (int)s_obj.type << " data:" << std::endl;
print_hexdump(verbosestream, s_obj.data);
new_stored.push_back(s_obj);
continue;
}
- verbosestream<<"ServerEnvironment::activateObjects(): "
- <<"activated static object pos="<<PP(s_obj.pos/BS)
- <<" type="<<(int)s_obj.type<<std::endl;
+ verbosestream << "ServerEnvironment::activateObjects(): "
+ << "activated static object pos=" << PP(s_obj.pos / BS)
+ << " type=" << (int)s_obj.type << std::endl;
// This will also add the object to the active static list
addActiveObjectRaw(obj, false, dtime_s);
}
// Clear stored list
- block->m_static_objects.m_stored.clear();
+ block->m_static_objects.clearStored();
// Add leftover failed stuff to stored list
for (const StaticObject &s_obj : new_stored) {
- block->m_static_objects.m_stored.push_back(s_obj);
+ block->m_static_objects.pushStored(s_obj);
}
/*
*/
void ServerEnvironment::deactivateFarObjects(bool _force_delete)
{
- auto cb_deactivate = [this, _force_delete] (ServerActiveObject *obj, u16 id) {
- // force_delete might be overriden per object
+ auto cb_deactivate = [this, _force_delete](ServerActiveObject *obj, u16 id) {
+ // force_delete might be overridden per object
bool force_delete = _force_delete;
- // Do not deactivate if static data creation not allowed
- if (!force_delete && !obj->isStaticAllowed())
+ // Do not deactivate if disallowed
+ if (!force_delete && !obj->shouldUnload())
return false;
// removeRemovedObjects() is responsible for these
if (!force_delete && obj->m_static_exists &&
!m_active_blocks.contains(obj->m_static_block) &&
m_active_blocks.contains(blockpos_o)) {
+
// Delete from block where object was located
deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
}
// If block is still active, don't remove
- if (!force_delete && m_active_blocks.contains(blockpos_o))
+ bool still_active = obj->isStaticAllowed() ?
+ m_active_blocks.contains(blockpos_o) :
+ getMap().getBlockNoCreateNoEx(blockpos_o) != nullptr;
+ if (!force_delete && still_active)
return false;
verbosestream << "ServerEnvironment::deactivateFarObjects(): "
if (obj->m_static_block == blockpos_o)
stays_in_same_block = true;
- MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
-
- if (block) {
- const auto n = block->m_static_objects.m_active.find(id);
- if (n != block->m_static_objects.m_active.end()) {
+ if (MapBlock *block = m_map->emergeBlock(obj->m_static_block, false)) {
+ const auto n = block->m_static_objects.getAllActives().find(id);
+ if (n != block->m_static_objects.getAllActives().end()) {
StaticObject static_old = n->second;
float save_movem = obj->getMinimumSavedMovement();
force_delete = true;
}
+ // Regardless of what happens to the object at this point, deactivate it first.
+ // This ensures that LuaEntity on_deactivate is always called.
+ obj->markForDeactivation();
+
/*
If known by some client, set pending deactivation.
Otherwise delete it immediately.
<< "object id=" << id << " is known by clients"
<< "; not deleting yet" << std::endl;
- obj->m_pending_deactivation = true;
return false;
}
<< " when saving static data of object to it. id=" << store_id << std::endl;
return false;
}
- if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
- warningstream << "ServerEnv: Trying to store id = " << store_id
- << " statically but block " << PP(blockpos)
- << " already contains "
- << block->m_static_objects.m_stored.size()
- << " objects." << std::endl;
- return false;
- }
- block->m_static_objects.insert(store_id, s_obj);
- if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
- block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
+ if (!block->saveStaticObject(store_id, s_obj, mod_reason))
+ return false;
obj->m_static_exists = true;
obj->m_static_block = blockpos;
if (name == "dummy")
return new Database_Dummy();
+
#if USE_POSTGRESQL
if (name == "postgresql") {
std::string connect_string;
return new PlayerDatabasePostgreSQL(connect_string);
}
#endif
+
+#if USE_LEVELDB
+ if (name == "leveldb")
+ return new PlayerDatabaseLevelDB(savedir);
+#endif
+
if (name == "files")
return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
if (!world_mt.exists("player_backend")) {
errorstream << "Please specify your current backend in world.mt:"
<< std::endl
- << " player_backend = {files|sqlite3|postgresql}"
+ << " player_backend = {files|sqlite3|leveldb|postgresql}"
<< std::endl;
return false;
}
if (name == "sqlite3")
return new AuthDatabaseSQLite3(savedir);
+#if USE_POSTGRESQL
+ if (name == "postgresql") {
+ std::string connect_string;
+ conf.getNoEx("pgsql_auth_connection", connect_string);
+ return new AuthDatabasePostgreSQL(connect_string);
+ }
+#endif
+
if (name == "files")
return new AuthDatabaseFiles(savedir);