ServerEnvironment
*/
+// Random device to seed pseudo random generators.
+static std::random_device seed;
+
ServerEnvironment::ServerEnvironment(ServerMap *map,
ServerScripting *scriptIface, Server *server,
const std::string &path_world):
m_map(map),
m_script(scriptIface),
m_server(server),
- m_path_world(path_world)
+ m_path_world(path_world),
+ m_rgen(seed())
{
// Determine which database backend to use
std::string conf_path = path_world + DIR_DELIM + "world.mt";
Settings conf;
+
+ std::string player_backend_name = "sqlite3";
+ std::string auth_backend_name = "sqlite3";
+
bool succeeded = conf.readConfigFile(conf_path.c_str());
- if (!succeeded || !conf.exists("player_backend")) {
- // fall back to files
- conf.set("player_backend", "files");
- warningstream << "/!\\ You are using old player file backend. "
- << "This backend is deprecated and will be removed in next release /!\\"
- << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
- << "please read http://wiki.minetest.net/Database_backends." << std::endl;
- if (!conf.updateConfigFile(conf_path.c_str())) {
- errorstream << "ServerEnvironment::ServerEnvironment(): "
- << "Failed to update world.mt!" << std::endl;
+ // If we open world.mt read the backend configurations.
+ if (succeeded) {
+ // Read those values before setting defaults
+ bool player_backend_exists = conf.exists("player_backend");
+ bool auth_backend_exists = conf.exists("auth_backend");
+
+ // player backend is not set, assume it's legacy file backend.
+ if (!player_backend_exists) {
+ // fall back to files
+ conf.set("player_backend", "files");
+ player_backend_name = "files";
+
+ if (!conf.updateConfigFile(conf_path.c_str())) {
+ errorstream << "ServerEnvironment::ServerEnvironment(): "
+ << "Failed to update world.mt!" << std::endl;
+ }
+ } else {
+ conf.getNoEx("player_backend", player_backend_name);
}
- }
- std::string name;
- conf.getNoEx("player_backend", name);
- m_player_database = openPlayerDatabase(name, path_world, conf);
+ // auth backend is not set, assume it's legacy file backend.
+ if (!auth_backend_exists) {
+ conf.set("auth_backend", "files");
+ auth_backend_name = "files";
- std::string auth_name = "files";
- if (conf.exists("auth_backend")) {
- conf.getNoEx("auth_backend", auth_name);
- } else {
- conf.set("auth_backend", "files");
- if (!conf.updateConfigFile(conf_path.c_str())) {
- errorstream << "ServerEnvironment::ServerEnvironment(): "
- << "Failed to update world.mt!" << std::endl;
+ if (!conf.updateConfigFile(conf_path.c_str())) {
+ errorstream << "ServerEnvironment::ServerEnvironment(): "
+ << "Failed to update world.mt!" << std::endl;
+ }
+ } else {
+ conf.getNoEx("auth_backend", auth_backend_name);
}
}
- m_auth_database = openAuthDatabase(auth_name, path_world, conf);
+
+ if (player_backend_name == "files") {
+ warningstream << "/!\\ You are using old player file backend. "
+ << "This backend is deprecated and will be removed in a future release /!\\"
+ << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
+ << "please read http://wiki.minetest.net/Database_backends." << std::endl;
+ }
+
+ if (auth_backend_name == "files") {
+ warningstream << "/!\\ You are using old auth file backend. "
+ << "This backend is deprecated and will be removed in a future release /!\\"
+ << std::endl << "Switching to SQLite3 is advised, "
+ << "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);
}
ServerEnvironment::~ServerEnvironment()
// Iterate trough nodes on the line
voxalgo::VoxelLineIterator iterator(pos1 / BS, (pos2 - pos1) / BS);
do {
- MapNode n = getMap().getNodeNoEx(iterator.m_current_node_pos);
+ MapNode n = getMap().getNode(iterator.m_current_node_pos);
// Return non-air
if (n.param0 != CONTENT_AIR) {
}
}
-void ServerEnvironment::saveLoadedPlayers()
+void ServerEnvironment::saveLoadedPlayers(bool force)
{
- std::string players_path = m_path_world + DIR_DELIM + "players";
- fs::CreateDir(players_path);
-
for (RemotePlayer *player : m_players) {
- if (player->checkModified() || (player->getPlayerSAO() &&
+ if (force || player->checkModified() || (player->getPlayerSAO() &&
player->getPlayerSAO()->getMeta().isModified())) {
try {
m_player_database->savePlayer(player);
c = n.getContent();
} else {
// otherwise consult the map
- MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
+ MapNode n = map->getNode(p1 + block->getPosRelative());
c = n.getContent();
}
if (CONTAINS(aabm.required_neighbors, c))
bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
{
const NodeDefManager *ndef = m_server->ndef();
- MapNode n_old = m_map->getNodeNoEx(p);
+ MapNode n_old = m_map->getNode(p);
const ContentFeatures &cf_old = ndef->get(n_old);
bool ServerEnvironment::removeNode(v3s16 p)
{
const NodeDefManager *ndef = m_server->ndef();
- MapNode n_old = m_map->getNodeNoEx(p);
+ MapNode n_old = m_map->getNode(p);
// Call destructor
if (ndef->get(n_old).has_on_destruct)
void ServerEnvironment::step(float dtime)
{
+ ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
/* Step time of day */
stepTimeOfDay(dtime);
Handle players
*/
{
- ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
+ ScopeProfiler sp(g_profiler, "ServerEnv: move players", SPT_AVG);
for (RemotePlayer *player : m_players) {
// Ignore disconnected players
if (player->getPeerId() == PEER_ID_INEXISTENT)
Manage active block list
*/
if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
- ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
+ ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
/*
Get player block positions
*/
Mess around in active blocks
*/
if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
- ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
+ ScopeProfiler sp(g_profiler, "ServerEnv: Run node timers", SPT_AVG);
float dtime = m_cache_nodetimer_interval;
}
}
- if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
- do { // breakable
- if (m_active_block_interval_overload_skip > 0) {
- ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
- m_active_block_interval_overload_skip--;
- break;
- }
- ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
- TimeTaker timer("modify in active blocks per interval");
-
- // Initialize handling of ActiveBlockModifiers
- ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
-
- int blocks_scanned = 0;
- int abms_run = 0;
- int blocks_cached = 0;
- for (const v3s16 &p : m_active_blocks.m_abm_list) {
- MapBlock *block = m_map->getBlockNoCreateNoEx(p);
- if (!block)
- continue;
+ if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) {
+ ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
+ TimeTaker timer("modify in active blocks per interval");
- // Set current time as timestamp
- block->setTimestampNoChangedFlag(m_game_time);
+ // Initialize handling of ActiveBlockModifiers
+ ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
- /* Handle ActiveBlockModifiers */
- abmhandler.apply(block, blocks_scanned, abms_run, blocks_cached);
- }
- g_profiler->avg("SEnv: active blocks", m_active_blocks.m_abm_list.size());
- g_profiler->avg("SEnv: active blocks cached", blocks_cached);
- g_profiler->avg("SEnv: active blocks scanned for ABMs", blocks_scanned);
- g_profiler->avg("SEnv: ABMs run", abms_run);
+ int blocks_scanned = 0;
+ int abms_run = 0;
+ int blocks_cached = 0;
+
+ std::vector<v3s16> output(m_active_blocks.m_abm_list.size());
+
+ // Shuffle the active blocks so that each block gets an equal chance
+ // of having its ABMs run.
+ std::copy(m_active_blocks.m_abm_list.begin(), m_active_blocks.m_abm_list.end(), output.begin());
+ 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;
+ for (const v3s16 &p : output) {
+ MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+ if (!block)
+ continue;
+
+ i++;
+
+ // Set current time as timestamp
+ block->setTimestampNoChangedFlag(m_game_time);
+
+ /* Handle ActiveBlockModifiers */
+ abmhandler.apply(block, blocks_scanned, abms_run, blocks_cached);
+
+ u32 time_ms = timer.getTimerTime();
- u32 time_ms = timer.stop(true);
- u32 max_time_ms = 200;
if (time_ms > max_time_ms) {
- warningstream<<"active block modifiers took "
- <<time_ms<<"ms (longer than "
- <<max_time_ms<<"ms)"<<std::endl;
- m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
+ warningstream << "active block modifiers took "
+ << time_ms << "ms (processed " << i << " of "
+ << output.size() << " active blocks)" << std::endl;
+ break;
}
- }while(0);
+ }
+ g_profiler->avg("ServerEnv: active blocks", m_active_blocks.m_abm_list.size());
+ g_profiler->avg("ServerEnv: active blocks cached", blocks_cached);
+ g_profiler->avg("ServerEnv: active blocks scanned for ABMs", blocks_scanned);
+ g_profiler->avg("ServerEnv: ABMs run", abms_run);
+
+ timer.stop(true);
+ }
/*
Step script environment (run global on_step())
Step active objects
*/
{
- ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
+ ScopeProfiler sp(g_profiler, "ServerEnv: Run SAO::step()", SPT_AVG);
// This helps the objects to send data at the same time
bool send_recommended = false;
Manage active objects
*/
if (m_object_management_interval.step(dtime, 0.5)) {
- ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
removeRemovedObjects();
}
++i;
}
}
+
+ // Send outdated player inventories
+ for (RemotePlayer *player : m_players) {
+ if (player->getPeerId() == PEER_ID_INEXISTENT)
+ continue;
+
+ PlayerSAO *sao = player->getPlayerSAO();
+ if (sao && player->inventory.checkModified())
+ m_server->SendInventory(sao, true);
+ }
+
+ // Send outdated detached inventories
+ m_server->sendDetachedInventories(PEER_ID_INEXISTENT, true);
}
u32 ServerEnvironment::addParticleSpawner(float exptime)
*/
void ServerEnvironment::removeRemovedObjects()
{
+ ScopeProfiler sp(g_profiler, "ServerEnvironment::removeRemovedObjects()", SPT_AVG);
+
auto clear_cb = [this] (ServerActiveObject *obj, u16 id) {
// This shouldn't happen but check it
if (!obj) {
delete dstdb;
} catch (BaseException &e) {
- errorstream << "An error occured during migration: " << e.what() << std::endl;
+ errorstream << "An error occurred during migration: " << e.what() << std::endl;
return false;
}
return true;
}
} catch (BaseException &e) {
- errorstream << "An error occured during migration: " << e.what()
+ errorstream << "An error occurred during migration: " << e.what()
<< std::endl;
return false;
}