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, nullptr);
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;
}
/*
Update m_list
*/
- m_list.clear();
- for (v3s16 p : newlist) {
- m_list.insert(p);
- }
+ m_list = std::move(newlist);
}
/*
ServerEnvironment::ServerEnvironment(ServerMap *map,
ServerScripting *scriptIface, Server *server,
- const std::string &path_world):
+ const std::string &path_world, MetricsBackend *mb):
Environment(server),
m_map(map),
m_script(scriptIface),
m_player_database = openPlayerDatabase(player_backend_name, path_world, conf);
m_auth_database = openAuthDatabase(auth_backend_name, path_world, conf);
+
+ 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");
}
ServerEnvironment::~ServerEnvironment()
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 asap so objects in those blocks appear on the client
+ m_force_update_active_blocks = true;
+
return playersao;
}
// 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()))
{
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
} 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 =
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;
return true;
}
+u8 ServerEnvironment::findSunlight(v3s16 pos) const
+{
+ // Directions for neighbouring 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 neighbours
+ 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;
}
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);
/*
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_force_update_active_blocks) {
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());
}
+ m_force_update_active_blocks = false;
/*
Mess around in active blocks
*/
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 && !block->isDummy())
+ return BS_LOADED;
+
+ if (m_map->isBlockInQueue(blockpos))
+ return BS_EMERGING;
+
+ return BS_UNKNOWN;
}
u32 ServerEnvironment::addParticleSpawner(float exptime)
/*
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
/*
Move static data from active to stored if deactivated
*/
- if (!obj->m_pending_removal && obj->m_static_exists) {
+ if (!obj->isPendingRemoval() && 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);
// force_delete might be overriden 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(): "
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;
}