]> git.lizzy.rs Git - dragonfireclient.git/blob - src/serverenvironment.cpp
Increase limit for simultaneous blocks sent per client and the meshgen cache.
[dragonfireclient.git] / src / serverenvironment.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <algorithm>
21 #include "serverenvironment.h"
22 #include "settings.h"
23 #include "log.h"
24 #include "mapblock.h"
25 #include "nodedef.h"
26 #include "nodemetadata.h"
27 #include "gamedef.h"
28 #include "map.h"
29 #include "porting.h"
30 #include "profiler.h"
31 #include "raycast.h"
32 #include "remoteplayer.h"
33 #include "scripting_server.h"
34 #include "server.h"
35 #include "util/serialize.h"
36 #include "util/basic_macros.h"
37 #include "util/pointedthing.h"
38 #include "threading/mutex_auto_lock.h"
39 #include "filesys.h"
40 #include "gameparams.h"
41 #include "database/database-dummy.h"
42 #include "database/database-files.h"
43 #include "database/database-sqlite3.h"
44 #if USE_POSTGRESQL
45 #include "database/database-postgresql.h"
46 #endif
47 #if USE_LEVELDB
48 #include "database/database-leveldb.h"
49 #endif
50 #include "server/luaentity_sao.h"
51 #include "server/player_sao.h"
52
53 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
54
55 // A number that is much smaller than the timeout for particle spawners should/could ever be
56 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
57
58 /*
59         ABMWithState
60 */
61
62 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
63         abm(abm_)
64 {
65         // Initialize timer to random value to spread processing
66         float itv = abm->getTriggerInterval();
67         itv = MYMAX(0.001, itv); // No less than 1ms
68         int minval = MYMAX(-0.51*itv, -60); // Clamp to
69         int maxval = MYMIN(0.51*itv, 60);   // +-60 seconds
70         timer = myrand_range(minval, maxval);
71 }
72
73 /*
74         LBMManager
75 */
76
77 void LBMContentMapping::deleteContents()
78 {
79         for (auto &it : lbm_list) {
80                 delete it;
81         }
82 }
83
84 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
85 {
86         // Add the lbm_def to the LBMContentMapping.
87         // Unknown names get added to the global NameIdMapping.
88         const NodeDefManager *nodedef = gamedef->ndef();
89
90         lbm_list.push_back(lbm_def);
91
92         for (const std::string &nodeTrigger: lbm_def->trigger_contents) {
93                 std::vector<content_t> c_ids;
94                 bool found = nodedef->getIds(nodeTrigger, c_ids);
95                 if (!found) {
96                         content_t c_id = gamedef->allocateUnknownNodeId(nodeTrigger);
97                         if (c_id == CONTENT_IGNORE) {
98                                 // Seems it can't be allocated.
99                                 warningstream << "Could not internalize node name \"" << nodeTrigger
100                                         << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
101                                 continue;
102                         }
103                         c_ids.push_back(c_id);
104                 }
105
106                 for (content_t c_id : c_ids) {
107                         map[c_id].push_back(lbm_def);
108                 }
109         }
110 }
111
112 const std::vector<LoadingBlockModifierDef *> *
113 LBMContentMapping::lookup(content_t c) const
114 {
115         lbm_map::const_iterator it = map.find(c);
116         if (it == map.end())
117                 return NULL;
118         // This first dereferences the iterator, returning
119         // a std::vector<LoadingBlockModifierDef *>
120         // reference, then we convert it to a pointer.
121         return &(it->second);
122 }
123
124 LBMManager::~LBMManager()
125 {
126         for (auto &m_lbm_def : m_lbm_defs) {
127                 delete m_lbm_def.second;
128         }
129
130         for (auto &it : m_lbm_lookup) {
131                 (it.second).deleteContents();
132         }
133 }
134
135 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
136 {
137         // Precondition, in query mode the map isn't used anymore
138         FATAL_ERROR_IF(m_query_mode,
139                 "attempted to modify LBMManager in query mode");
140
141         if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
142                 throw ModError("Error adding LBM \"" + lbm_def->name +
143                         "\": Does not follow naming conventions: "
144                                 "Only characters [a-z0-9_:] are allowed.");
145         }
146
147         m_lbm_defs[lbm_def->name] = lbm_def;
148 }
149
150 void LBMManager::loadIntroductionTimes(const std::string &times,
151         IGameDef *gamedef, u32 now)
152 {
153         m_query_mode = true;
154
155         // name -> time map.
156         // Storing it in a map first instead of
157         // handling the stuff directly in the loop
158         // removes all duplicate entries.
159         // TODO make this std::unordered_map
160         std::map<std::string, u32> introduction_times;
161
162         /*
163         The introduction times string consists of name~time entries,
164         with each entry terminated by a semicolon. The time is decimal.
165          */
166
167         size_t idx = 0;
168         size_t idx_new;
169         while ((idx_new = times.find(';', idx)) != std::string::npos) {
170                 std::string entry = times.substr(idx, idx_new - idx);
171                 std::vector<std::string> components = str_split(entry, '~');
172                 if (components.size() != 2)
173                         throw SerializationError("Introduction times entry \""
174                                 + entry + "\" requires exactly one '~'!");
175                 const std::string &name = components[0];
176                 u32 time = from_string<u32>(components[1]);
177                 introduction_times[name] = time;
178                 idx = idx_new + 1;
179         }
180
181         // Put stuff from introduction_times into m_lbm_lookup
182         for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
183                 it != introduction_times.end(); ++it) {
184                 const std::string &name = it->first;
185                 u32 time = it->second;
186
187                 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
188                         m_lbm_defs.find(name);
189                 if (def_it == m_lbm_defs.end()) {
190                         // This seems to be an LBM entry for
191                         // an LBM we haven't loaded. Discard it.
192                         continue;
193                 }
194                 LoadingBlockModifierDef *lbm_def = def_it->second;
195                 if (lbm_def->run_at_every_load) {
196                         // This seems to be an LBM entry for
197                         // an LBM that runs at every load.
198                         // Don't add it just yet.
199                         continue;
200                 }
201
202                 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
203
204                 // Erase the entry so that we know later
205                 // what elements didn't get put into m_lbm_lookup
206                 m_lbm_defs.erase(name);
207         }
208
209         // Now also add the elements from m_lbm_defs to m_lbm_lookup
210         // that weren't added in the previous step.
211         // They are introduced first time to this world,
212         // or are run at every load (introducement time hardcoded to U32_MAX).
213
214         LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
215         LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
216
217         for (auto &m_lbm_def : m_lbm_defs) {
218                 if (m_lbm_def.second->run_at_every_load) {
219                         lbms_running_always.addLBM(m_lbm_def.second, gamedef);
220                 } else {
221                         lbms_we_introduce_now.addLBM(m_lbm_def.second, gamedef);
222                 }
223         }
224
225         // Clear the list, so that we don't delete remaining elements
226         // twice in the destructor
227         m_lbm_defs.clear();
228 }
229
230 std::string LBMManager::createIntroductionTimesString()
231 {
232         // Precondition, we must be in query mode
233         FATAL_ERROR_IF(!m_query_mode,
234                 "attempted to query on non fully set up LBMManager");
235
236         std::ostringstream oss;
237         for (const auto &it : m_lbm_lookup) {
238                 u32 time = it.first;
239                 const std::vector<LoadingBlockModifierDef *> &lbm_list = it.second.lbm_list;
240                 for (const auto &lbm_def : lbm_list) {
241                         // Don't add if the LBM runs at every load,
242                         // then introducement time is hardcoded
243                         // and doesn't need to be stored
244                         if (lbm_def->run_at_every_load)
245                                 continue;
246                         oss << lbm_def->name << "~" << time << ";";
247                 }
248         }
249         return oss.str();
250 }
251
252 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
253 {
254         // Precondition, we need m_lbm_lookup to be initialized
255         FATAL_ERROR_IF(!m_query_mode,
256                 "attempted to query on non fully set up LBMManager");
257         v3s16 pos_of_block = block->getPosRelative();
258         v3s16 pos;
259         MapNode n;
260         content_t c;
261         lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
262         for (; it != m_lbm_lookup.end(); ++it) {
263                 // Cache previous version to speedup lookup which has a very high performance
264                 // penalty on each call
265                 content_t previous_c{};
266                 std::vector<LoadingBlockModifierDef *> *lbm_list = nullptr;
267
268                 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
269                         for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
270                                 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) {
271                                         n = block->getNodeNoEx(pos);
272                                         c = n.getContent();
273
274                                         // If content_t are not matching perform an LBM lookup
275                                         if (previous_c != c) {
276                                                 lbm_list = (std::vector<LoadingBlockModifierDef *> *)
277                                                         it->second.lookup(c);
278                                                 previous_c = c;
279                                         }
280
281                                         if (!lbm_list)
282                                                 continue;
283                                         for (auto lbmdef : *lbm_list) {
284                                                 lbmdef->trigger(env, pos + pos_of_block, n);
285                                         }
286                                 }
287         }
288 }
289
290 /*
291         ActiveBlockList
292 */
293
294 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
295 {
296         v3s16 p;
297         for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
298                 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
299                         for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
300                         {
301                                 // limit to a sphere
302                                 if (p.getDistanceFrom(p0) <= r) {
303                                         // Set in list
304                                         list.insert(p);
305                                 }
306                         }
307 }
308
309 void fillViewConeBlock(v3s16 p0,
310         const s16 r,
311         const v3f camera_pos,
312         const v3f camera_dir,
313         const float camera_fov,
314         std::set<v3s16> &list)
315 {
316         v3s16 p;
317         const s16 r_nodes = r * BS * MAP_BLOCKSIZE;
318         for (p.X = p0.X - r; p.X <= p0.X+r; p.X++)
319         for (p.Y = p0.Y - r; p.Y <= p0.Y+r; p.Y++)
320         for (p.Z = p0.Z - r; p.Z <= p0.Z+r; p.Z++) {
321                 if (isBlockInSight(p, camera_pos, camera_dir, camera_fov, r_nodes)) {
322                         list.insert(p);
323                 }
324         }
325 }
326
327 void ActiveBlockList::update(std::vector<PlayerSAO*> &active_players,
328         s16 active_block_range,
329         s16 active_object_range,
330         std::set<v3s16> &blocks_removed,
331         std::set<v3s16> &blocks_added)
332 {
333         /*
334                 Create the new list
335         */
336         std::set<v3s16> newlist = m_forceloaded_list;
337         m_abm_list = m_forceloaded_list;
338         for (const PlayerSAO *playersao : active_players) {
339                 v3s16 pos = getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS));
340                 fillRadiusBlock(pos, active_block_range, m_abm_list);
341                 fillRadiusBlock(pos, active_block_range, newlist);
342
343                 s16 player_ao_range = std::min(active_object_range, playersao->getWantedRange());
344                 // only do this if this would add blocks
345                 if (player_ao_range > active_block_range) {
346                         v3f camera_dir = v3f(0,0,1);
347                         camera_dir.rotateYZBy(playersao->getLookPitch());
348                         camera_dir.rotateXZBy(playersao->getRotation().Y);
349                         fillViewConeBlock(pos,
350                                 player_ao_range,
351                                 playersao->getEyePosition(),
352                                 camera_dir,
353                                 playersao->getFov(),
354                                 newlist);
355                 }
356         }
357
358         /*
359                 Find out which blocks on the old list are not on the new list
360         */
361         // Go through old list
362         for (v3s16 p : m_list) {
363                 // If not on new list, it's been removed
364                 if (newlist.find(p) == newlist.end())
365                         blocks_removed.insert(p);
366         }
367
368         /*
369                 Find out which blocks on the new list are not on the old list
370         */
371         // Go through new list
372         for (v3s16 p : newlist) {
373                 // If not on old list, it's been added
374                 if(m_list.find(p) == m_list.end())
375                         blocks_added.insert(p);
376         }
377
378         /*
379                 Update m_list
380         */
381         m_list.clear();
382         for (v3s16 p : newlist) {
383                 m_list.insert(p);
384         }
385 }
386
387 /*
388         ServerEnvironment
389 */
390
391 // Random device to seed pseudo random generators.
392 static std::random_device seed;
393
394 ServerEnvironment::ServerEnvironment(ServerMap *map,
395         ServerScripting *scriptIface, Server *server,
396         const std::string &path_world):
397         Environment(server),
398         m_map(map),
399         m_script(scriptIface),
400         m_server(server),
401         m_path_world(path_world),
402         m_rgen(seed())
403 {
404         // Determine which database backend to use
405         std::string conf_path = path_world + DIR_DELIM + "world.mt";
406         Settings conf;
407
408         std::string player_backend_name = "sqlite3";
409         std::string auth_backend_name = "sqlite3";
410
411         bool succeeded = conf.readConfigFile(conf_path.c_str());
412
413         // If we open world.mt read the backend configurations.
414         if (succeeded) {
415                 // Read those values before setting defaults
416                 bool player_backend_exists = conf.exists("player_backend");
417                 bool auth_backend_exists = conf.exists("auth_backend");
418
419                 // player backend is not set, assume it's legacy file backend.
420                 if (!player_backend_exists) {
421                         // fall back to files
422                         conf.set("player_backend", "files");
423                         player_backend_name = "files";
424
425                         if (!conf.updateConfigFile(conf_path.c_str())) {
426                                 errorstream << "ServerEnvironment::ServerEnvironment(): "
427                                                 << "Failed to update world.mt!" << std::endl;
428                         }
429                 } else {
430                         conf.getNoEx("player_backend", player_backend_name);
431                 }
432
433                 // auth backend is not set, assume it's legacy file backend.
434                 if (!auth_backend_exists) {
435                         conf.set("auth_backend", "files");
436                         auth_backend_name = "files";
437
438                         if (!conf.updateConfigFile(conf_path.c_str())) {
439                                 errorstream << "ServerEnvironment::ServerEnvironment(): "
440                                                 << "Failed to update world.mt!" << std::endl;
441                         }
442                 } else {
443                         conf.getNoEx("auth_backend", auth_backend_name);
444                 }
445         }
446
447         if (player_backend_name == "files") {
448                 warningstream << "/!\\ You are using old player file backend. "
449                                 << "This backend is deprecated and will be removed in a future release /!\\"
450                                 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
451                                 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
452         }
453
454         if (auth_backend_name == "files") {
455                 warningstream << "/!\\ You are using old auth file backend. "
456                                 << "This backend is deprecated and will be removed in a future release /!\\"
457                                 << std::endl << "Switching to SQLite3 is advised, "
458                                 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
459         }
460
461         m_player_database = openPlayerDatabase(player_backend_name, path_world, conf);
462         m_auth_database = openAuthDatabase(auth_backend_name, path_world, conf);
463 }
464
465 ServerEnvironment::~ServerEnvironment()
466 {
467         // Clear active block list.
468         // This makes the next one delete all active objects.
469         m_active_blocks.clear();
470
471         // Convert all objects to static and delete the active objects
472         deactivateFarObjects(true);
473
474         // Drop/delete map
475         m_map->drop();
476
477         // Delete ActiveBlockModifiers
478         for (ABMWithState &m_abm : m_abms) {
479                 delete m_abm.abm;
480         }
481
482         // Deallocate players
483         for (RemotePlayer *m_player : m_players) {
484                 delete m_player;
485         }
486
487         delete m_player_database;
488         delete m_auth_database;
489 }
490
491 Map & ServerEnvironment::getMap()
492 {
493         return *m_map;
494 }
495
496 ServerMap & ServerEnvironment::getServerMap()
497 {
498         return *m_map;
499 }
500
501 RemotePlayer *ServerEnvironment::getPlayer(const session_t peer_id)
502 {
503         for (RemotePlayer *player : m_players) {
504                 if (player->getPeerId() == peer_id)
505                         return player;
506         }
507         return NULL;
508 }
509
510 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
511 {
512         for (RemotePlayer *player : m_players) {
513                 if (strcmp(player->getName(), name) == 0)
514                         return player;
515         }
516         return NULL;
517 }
518
519 void ServerEnvironment::addPlayer(RemotePlayer *player)
520 {
521         /*
522                 Check that peer_ids are unique.
523                 Also check that names are unique.
524                 Exception: there can be multiple players with peer_id=0
525         */
526         // If peer id is non-zero, it has to be unique.
527         if (player->getPeerId() != PEER_ID_INEXISTENT)
528                 FATAL_ERROR_IF(getPlayer(player->getPeerId()) != NULL, "Peer id not unique");
529         // Name has to be unique.
530         FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
531         // Add.
532         m_players.push_back(player);
533 }
534
535 void ServerEnvironment::removePlayer(RemotePlayer *player)
536 {
537         for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
538                 it != m_players.end(); ++it) {
539                 if ((*it) == player) {
540                         delete *it;
541                         m_players.erase(it);
542                         return;
543                 }
544         }
545 }
546
547 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
548 {
549         return m_player_database->removePlayer(name);
550 }
551
552 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
553         const std::string &str_reason, bool reconnect)
554 {
555         for (RemotePlayer *player : m_players) {
556                 m_server->DenyAccessVerCompliant(player->getPeerId(),
557                         player->protocol_version, reason, str_reason, reconnect);
558         }
559 }
560
561 void ServerEnvironment::saveLoadedPlayers(bool force)
562 {
563         for (RemotePlayer *player : m_players) {
564                 if (force || player->checkModified() || (player->getPlayerSAO() &&
565                                 player->getPlayerSAO()->getMeta().isModified())) {
566                         try {
567                                 m_player_database->savePlayer(player);
568                         } catch (DatabaseException &e) {
569                                 errorstream << "Failed to save player " << player->getName() << " exception: "
570                                         << e.what() << std::endl;
571                                 throw;
572                         }
573                 }
574         }
575 }
576
577 void ServerEnvironment::savePlayer(RemotePlayer *player)
578 {
579         try {
580                 m_player_database->savePlayer(player);
581         } catch (DatabaseException &e) {
582                 errorstream << "Failed to save player " << player->getName() << " exception: "
583                         << e.what() << std::endl;
584                 throw;
585         }
586 }
587
588 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
589         session_t peer_id, bool is_singleplayer)
590 {
591         PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
592         // Create player if it doesn't exist
593         if (!m_player_database->loadPlayer(player, playersao)) {
594                 *new_player = true;
595                 // Set player position
596                 infostream << "Server: Finding spawn place for player \""
597                         << player->getName() << "\"" << std::endl;
598                 playersao->setBasePosition(m_server->findSpawnPos());
599
600                 // Make sure the player is saved
601                 player->setModified(true);
602         } else {
603                 // If the player exists, ensure that they respawn inside legal bounds
604                 // This fixes an assert crash when the player can't be added
605                 // to the environment
606                 if (objectpos_over_limit(playersao->getBasePosition())) {
607                         actionstream << "Respawn position for player \""
608                                 << player->getName() << "\" outside limits, resetting" << std::endl;
609                         playersao->setBasePosition(m_server->findSpawnPos());
610                 }
611         }
612
613         // Add player to environment
614         addPlayer(player);
615
616         /* Clean up old HUD elements from previous sessions */
617         player->clearHud();
618
619         /* Add object to environment */
620         addActiveObject(playersao);
621
622         return playersao;
623 }
624
625 void ServerEnvironment::saveMeta()
626 {
627         if (!m_meta_loaded)
628                 return;
629
630         std::string path = m_path_world + DIR_DELIM "env_meta.txt";
631
632         // Open file and serialize
633         std::ostringstream ss(std::ios_base::binary);
634
635         Settings args;
636         args.setU64("game_time", m_game_time);
637         args.setU64("time_of_day", getTimeOfDay());
638         args.setU64("last_clear_objects_time", m_last_clear_objects_time);
639         args.setU64("lbm_introduction_times_version", 1);
640         args.set("lbm_introduction_times",
641                 m_lbm_mgr.createIntroductionTimesString());
642         args.setU64("day_count", m_day_count);
643         args.writeLines(ss);
644         ss<<"EnvArgsEnd\n";
645
646         if(!fs::safeWriteToFile(path, ss.str()))
647         {
648                 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
649                         <<path<<std::endl;
650                 throw SerializationError("Couldn't save env meta");
651         }
652 }
653
654 void ServerEnvironment::loadMeta()
655 {
656         SANITY_CHECK(!m_meta_loaded);
657         m_meta_loaded = true;
658
659         // If file doesn't exist, load default environment metadata
660         if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
661                 infostream << "ServerEnvironment: Loading default environment metadata"
662                         << std::endl;
663                 loadDefaultMeta();
664                 return;
665         }
666
667         infostream << "ServerEnvironment: Loading environment metadata" << std::endl;
668
669         std::string path = m_path_world + DIR_DELIM "env_meta.txt";
670
671         // Open file and deserialize
672         std::ifstream is(path.c_str(), std::ios_base::binary);
673         if (!is.good()) {
674                 infostream << "ServerEnvironment::loadMeta(): Failed to open "
675                         << path << std::endl;
676                 throw SerializationError("Couldn't load env meta");
677         }
678
679         Settings args;
680
681         if (!args.parseConfigLines(is, "EnvArgsEnd")) {
682                 throw SerializationError("ServerEnvironment::loadMeta(): "
683                         "EnvArgsEnd not found!");
684         }
685
686         try {
687                 m_game_time = args.getU64("game_time");
688         } catch (SettingNotFoundException &e) {
689                 // Getting this is crucial, otherwise timestamps are useless
690                 throw SerializationError("Couldn't load env meta game_time");
691         }
692
693         setTimeOfDay(args.exists("time_of_day") ?
694                 // set day to early morning by default
695                 args.getU64("time_of_day") : 5250);
696
697         m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
698                 // If missing, do as if clearObjects was never called
699                 args.getU64("last_clear_objects_time") : 0;
700
701         std::string lbm_introduction_times;
702         try {
703                 u64 ver = args.getU64("lbm_introduction_times_version");
704                 if (ver == 1) {
705                         lbm_introduction_times = args.get("lbm_introduction_times");
706                 } else {
707                         infostream << "ServerEnvironment::loadMeta(): Non-supported"
708                                 << " introduction time version " << ver << std::endl;
709                 }
710         } catch (SettingNotFoundException &e) {
711                 // No problem, this is expected. Just continue with an empty string
712         }
713         m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
714
715         m_day_count = args.exists("day_count") ?
716                 args.getU64("day_count") : 0;
717 }
718
719 /**
720  * called if env_meta.txt doesn't exist (e.g. new world)
721  */
722 void ServerEnvironment::loadDefaultMeta()
723 {
724         m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
725 }
726
727 struct ActiveABM
728 {
729         ActiveBlockModifier *abm;
730         int chance;
731         std::vector<content_t> required_neighbors;
732         bool check_required_neighbors; // false if required_neighbors is known to be empty
733 };
734
735 class ABMHandler
736 {
737 private:
738         ServerEnvironment *m_env;
739         std::vector<std::vector<ActiveABM> *> m_aabms;
740 public:
741         ABMHandler(std::vector<ABMWithState> &abms,
742                 float dtime_s, ServerEnvironment *env,
743                 bool use_timers):
744                 m_env(env)
745         {
746                 if(dtime_s < 0.001)
747                         return;
748                 const NodeDefManager *ndef = env->getGameDef()->ndef();
749                 for (ABMWithState &abmws : abms) {
750                         ActiveBlockModifier *abm = abmws.abm;
751                         float trigger_interval = abm->getTriggerInterval();
752                         if(trigger_interval < 0.001)
753                                 trigger_interval = 0.001;
754                         float actual_interval = dtime_s;
755                         if(use_timers){
756                                 abmws.timer += dtime_s;
757                                 if(abmws.timer < trigger_interval)
758                                         continue;
759                                 abmws.timer -= trigger_interval;
760                                 actual_interval = trigger_interval;
761                         }
762                         float chance = abm->getTriggerChance();
763                         if(chance == 0)
764                                 chance = 1;
765                         ActiveABM aabm;
766                         aabm.abm = abm;
767                         if (abm->getSimpleCatchUp()) {
768                                 float intervals = actual_interval / trigger_interval;
769                                 if(intervals == 0)
770                                         continue;
771                                 aabm.chance = chance / intervals;
772                                 if(aabm.chance == 0)
773                                         aabm.chance = 1;
774                         } else {
775                                 aabm.chance = chance;
776                         }
777
778                         // Trigger neighbors
779                         const std::vector<std::string> &required_neighbors_s =
780                                 abm->getRequiredNeighbors();
781                         for (const std::string &required_neighbor_s : required_neighbors_s) {
782                                 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
783                         }
784                         aabm.check_required_neighbors = !required_neighbors_s.empty();
785
786                         // Trigger contents
787                         const std::vector<std::string> &contents_s = abm->getTriggerContents();
788                         for (const std::string &content_s : contents_s) {
789                                 std::vector<content_t> ids;
790                                 ndef->getIds(content_s, ids);
791                                 for (content_t c : ids) {
792                                         if (c >= m_aabms.size())
793                                                 m_aabms.resize(c + 256, NULL);
794                                         if (!m_aabms[c])
795                                                 m_aabms[c] = new std::vector<ActiveABM>;
796                                         m_aabms[c]->push_back(aabm);
797                                 }
798                         }
799                 }
800         }
801
802         ~ABMHandler()
803         {
804                 for (auto &aabms : m_aabms)
805                         delete aabms;
806         }
807
808         // Find out how many objects the given block and its neighbours contain.
809         // Returns the number of objects in the block, and also in 'wider' the
810         // number of objects in the block and all its neighbours. The latter
811         // may an estimate if any neighbours are unloaded.
812         u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
813         {
814                 wider = 0;
815                 u32 wider_unknown_count = 0;
816                 for(s16 x=-1; x<=1; x++)
817                         for(s16 y=-1; y<=1; y++)
818                                 for(s16 z=-1; z<=1; z++)
819                                 {
820                                         MapBlock *block2 = map->getBlockNoCreateNoEx(
821                                                 block->getPos() + v3s16(x,y,z));
822                                         if(block2==NULL){
823                                                 wider_unknown_count++;
824                                                 continue;
825                                         }
826                                         wider += block2->m_static_objects.m_active.size()
827                                                 + block2->m_static_objects.m_stored.size();
828                                 }
829                 // Extrapolate
830                 u32 active_object_count = block->m_static_objects.m_active.size();
831                 u32 wider_known_count = 3*3*3 - wider_unknown_count;
832                 wider += wider_unknown_count * wider / wider_known_count;
833                 return active_object_count;
834
835         }
836         void apply(MapBlock *block, int &blocks_scanned, int &abms_run, int &blocks_cached)
837         {
838                 if(m_aabms.empty() || block->isDummy())
839                         return;
840
841                 // Check the content type cache first
842                 // to see whether there are any ABMs
843                 // to be run at all for this block.
844                 if (block->contents_cached) {
845                         blocks_cached++;
846                         bool run_abms = false;
847                         for (content_t c : block->contents) {
848                                 if (c < m_aabms.size() && m_aabms[c]) {
849                                         run_abms = true;
850                                         break;
851                                 }
852                         }
853                         if (!run_abms)
854                                 return;
855                 } else {
856                         // Clear any caching
857                         block->contents.clear();
858                 }
859                 blocks_scanned++;
860
861                 ServerMap *map = &m_env->getServerMap();
862
863                 u32 active_object_count_wider;
864                 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
865                 m_env->m_added_objects = 0;
866
867                 v3s16 p0;
868                 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
869                 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
870                 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
871                 {
872                         const MapNode &n = block->getNodeUnsafe(p0);
873                         content_t c = n.getContent();
874                         // Cache content types as we go
875                         if (!block->contents_cached && !block->do_not_cache_contents) {
876                                 block->contents.insert(c);
877                                 if (block->contents.size() > 64) {
878                                         // Too many different nodes... don't try to cache
879                                         block->do_not_cache_contents = true;
880                                         block->contents.clear();
881                                 }
882                         }
883
884                         if (c >= m_aabms.size() || !m_aabms[c])
885                                 continue;
886
887                         v3s16 p = p0 + block->getPosRelative();
888                         for (ActiveABM &aabm : *m_aabms[c]) {
889                                 if (myrand() % aabm.chance != 0)
890                                         continue;
891
892                                 // Check neighbors
893                                 if (aabm.check_required_neighbors) {
894                                         v3s16 p1;
895                                         for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
896                                         for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
897                                         for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
898                                         {
899                                                 if(p1 == p0)
900                                                         continue;
901                                                 content_t c;
902                                                 if (block->isValidPosition(p1)) {
903                                                         // if the neighbor is found on the same map block
904                                                         // get it straight from there
905                                                         const MapNode &n = block->getNodeUnsafe(p1);
906                                                         c = n.getContent();
907                                                 } else {
908                                                         // otherwise consult the map
909                                                         MapNode n = map->getNode(p1 + block->getPosRelative());
910                                                         c = n.getContent();
911                                                 }
912                                                 if (CONTAINS(aabm.required_neighbors, c))
913                                                         goto neighbor_found;
914                                         }
915                                         // No required neighbor found
916                                         continue;
917                                 }
918                                 neighbor_found:
919
920                                 abms_run++;
921                                 // Call all the trigger variations
922                                 aabm.abm->trigger(m_env, p, n);
923                                 aabm.abm->trigger(m_env, p, n,
924                                         active_object_count, active_object_count_wider);
925
926                                 // Count surrounding objects again if the abms added any
927                                 if(m_env->m_added_objects > 0) {
928                                         active_object_count = countObjects(block, map, active_object_count_wider);
929                                         m_env->m_added_objects = 0;
930                                 }
931                         }
932                 }
933                 block->contents_cached = !block->do_not_cache_contents;
934         }
935 };
936
937 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
938 {
939         // Reset usage timer immediately, otherwise a block that becomes active
940         // again at around the same time as it would normally be unloaded will
941         // get unloaded incorrectly. (I think this still leaves a small possibility
942         // of a race condition between this and server::AsyncRunStep, which only
943         // some kind of synchronisation will fix, but it at least reduces the window
944         // of opportunity for it to break from seconds to nanoseconds)
945         block->resetUsageTimer();
946
947         // Get time difference
948         u32 dtime_s = 0;
949         u32 stamp = block->getTimestamp();
950         if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
951                 dtime_s = m_game_time - stamp;
952         dtime_s += additional_dtime;
953
954         /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
955                         <<stamp<<", game time: "<<m_game_time<<std::endl;*/
956
957         // Remove stored static objects if clearObjects was called since block's timestamp
958         if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
959                 block->m_static_objects.m_stored.clear();
960                 // do not set changed flag to avoid unnecessary mapblock writes
961         }
962
963         // Set current time as timestamp
964         block->setTimestampNoChangedFlag(m_game_time);
965
966         /*infostream<<"ServerEnvironment::activateBlock(): block is "
967                         <<dtime_s<<" seconds old."<<std::endl;*/
968
969         // Activate stored objects
970         activateObjects(block, dtime_s);
971
972         /* Handle LoadingBlockModifiers */
973         m_lbm_mgr.applyLBMs(this, block, stamp);
974
975         // Run node timers
976         std::vector<NodeTimer> elapsed_timers =
977                 block->m_node_timers.step((float)dtime_s);
978         if (!elapsed_timers.empty()) {
979                 MapNode n;
980                 for (const NodeTimer &elapsed_timer : elapsed_timers) {
981                         n = block->getNodeNoEx(elapsed_timer.position);
982                         v3s16 p = elapsed_timer.position + block->getPosRelative();
983                         if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
984                                 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
985                                         elapsed_timer.position));
986                 }
987         }
988 }
989
990 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
991 {
992         m_abms.emplace_back(abm);
993 }
994
995 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
996 {
997         m_lbm_mgr.addLBMDef(lbm);
998 }
999
1000 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1001 {
1002         const NodeDefManager *ndef = m_server->ndef();
1003         MapNode n_old = m_map->getNode(p);
1004
1005         const ContentFeatures &cf_old = ndef->get(n_old);
1006
1007         // Call destructor
1008         if (cf_old.has_on_destruct)
1009                 m_script->node_on_destruct(p, n_old);
1010
1011         // Replace node
1012         if (!m_map->addNodeWithEvent(p, n))
1013                 return false;
1014
1015         // Update active VoxelManipulator if a mapgen thread
1016         m_map->updateVManip(p);
1017
1018         // Call post-destructor
1019         if (cf_old.has_after_destruct)
1020                 m_script->node_after_destruct(p, n_old);
1021
1022         // Retrieve node content features
1023         // if new node is same as old, reuse old definition to prevent a lookup
1024         const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
1025
1026         // Call constructor
1027         if (cf_new.has_on_construct)
1028                 m_script->node_on_construct(p, n);
1029
1030         return true;
1031 }
1032
1033 bool ServerEnvironment::removeNode(v3s16 p)
1034 {
1035         const NodeDefManager *ndef = m_server->ndef();
1036         MapNode n_old = m_map->getNode(p);
1037
1038         // Call destructor
1039         if (ndef->get(n_old).has_on_destruct)
1040                 m_script->node_on_destruct(p, n_old);
1041
1042         // Replace with air
1043         // This is slightly optimized compared to addNodeWithEvent(air)
1044         if (!m_map->removeNodeWithEvent(p))
1045                 return false;
1046
1047         // Update active VoxelManipulator if a mapgen thread
1048         m_map->updateVManip(p);
1049
1050         // Call post-destructor
1051         if (ndef->get(n_old).has_after_destruct)
1052                 m_script->node_after_destruct(p, n_old);
1053
1054         // Air doesn't require constructor
1055         return true;
1056 }
1057
1058 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1059 {
1060         if (!m_map->addNodeWithEvent(p, n, false))
1061                 return false;
1062
1063         // Update active VoxelManipulator if a mapgen thread
1064         m_map->updateVManip(p);
1065
1066         return true;
1067 }
1068
1069 u8 ServerEnvironment::findSunlight(v3s16 pos) const
1070 {
1071         // Directions for neighbouring nodes with specified order
1072         static const v3s16 dirs[] = {
1073                 v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1),
1074                 v3s16(0, -1, 0), v3s16(0, 1, 0)
1075         };
1076
1077         const NodeDefManager *ndef = m_server->ndef();
1078
1079         // found_light remembers the highest known sunlight value at pos
1080         u8 found_light = 0;
1081
1082         struct stack_entry {
1083                 v3s16 pos;
1084                 s16 dist;
1085         };
1086         std::stack<stack_entry> stack;
1087         stack.push({pos, 0});
1088
1089         std::unordered_map<s64, s8> dists;
1090         dists[MapDatabase::getBlockAsInteger(pos)] = 0;
1091
1092         while (!stack.empty()) {
1093                 struct stack_entry e = stack.top();
1094                 stack.pop();
1095
1096                 v3s16 currentPos = e.pos;
1097                 s8 dist = e.dist + 1;
1098
1099                 for (const v3s16& off : dirs) {
1100                         v3s16 neighborPos = currentPos + off;
1101                         s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos);
1102
1103                         // Do not walk neighborPos multiple times unless the distance to the start
1104                         // position is shorter
1105                         auto it = dists.find(neighborHash);
1106                         if (it != dists.end() && dist >= it->second)
1107                                 continue;
1108
1109                         // Position to walk
1110                         bool is_position_ok;
1111                         MapNode node = m_map->getNode(neighborPos, &is_position_ok);
1112                         if (!is_position_ok) {
1113                                 // This happens very rarely because the map at currentPos is loaded
1114                                 m_map->emergeBlock(neighborPos, false);
1115                                 node = m_map->getNode(neighborPos, &is_position_ok);
1116                                 if (!is_position_ok)
1117                                         continue;  // not generated
1118                         }
1119
1120                         const ContentFeatures &def = ndef->get(node);
1121                         if (!def.sunlight_propagates) {
1122                                 // Do not test propagation here again
1123                                 dists[neighborHash] = -1;
1124                                 continue;
1125                         }
1126
1127                         // Sunlight could have come from here
1128                         dists[neighborHash] = dist;
1129                         u8 daylight = node.param1 & 0x0f;
1130
1131                         // In the special case where sunlight shines from above and thus
1132                         // does not decrease with upwards distance, daylight is always
1133                         // bigger than nightlight, which never reaches 15
1134                         int possible_finlight = daylight - dist;
1135                         if (possible_finlight <= found_light) {
1136                                 // Light from here cannot make a brighter light at currentPos than
1137                                 // found_light
1138                                 continue;
1139                         }
1140
1141                         u8 nightlight = node.param1 >> 4;
1142                         if (daylight > nightlight) {
1143                                 // Found a valid daylight
1144                                 found_light = possible_finlight;
1145                         } else {
1146                                 // Sunlight may be darker, so walk the neighbours
1147                                 stack.push({neighborPos, dist});
1148                         }
1149                 }
1150         }
1151         return found_light;
1152 }
1153
1154 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1155 {
1156         infostream << "ServerEnvironment::clearObjects(): "
1157                 << "Removing all active objects" << std::endl;
1158         auto cb_removal = [this] (ServerActiveObject *obj, u16 id) {
1159                 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1160                         return false;
1161
1162                 // Delete static object if block is loaded
1163                 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1164
1165                 // If known by some client, don't delete immediately
1166                 if (obj->m_known_by_count > 0) {
1167                         obj->m_pending_removal = true;
1168                         return false;
1169                 }
1170
1171                 // Tell the object about removal
1172                 obj->removingFromEnvironment();
1173                 // Deregister in scripting api
1174                 m_script->removeObjectReference(obj);
1175
1176                 // Delete active object
1177                 if (obj->environmentDeletes())
1178                         delete obj;
1179
1180                 return true;
1181         };
1182
1183         m_ao_manager.clear(cb_removal);
1184
1185         // Get list of loaded blocks
1186         std::vector<v3s16> loaded_blocks;
1187         infostream << "ServerEnvironment::clearObjects(): "
1188                 << "Listing all loaded blocks" << std::endl;
1189         m_map->listAllLoadedBlocks(loaded_blocks);
1190         infostream << "ServerEnvironment::clearObjects(): "
1191                 << "Done listing all loaded blocks: "
1192                 << loaded_blocks.size()<<std::endl;
1193
1194         // Get list of loadable blocks
1195         std::vector<v3s16> loadable_blocks;
1196         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1197                 infostream << "ServerEnvironment::clearObjects(): "
1198                         << "Listing all loadable blocks" << std::endl;
1199                 m_map->listAllLoadableBlocks(loadable_blocks);
1200                 infostream << "ServerEnvironment::clearObjects(): "
1201                         << "Done listing all loadable blocks: "
1202                         << loadable_blocks.size() << std::endl;
1203         } else {
1204                 loadable_blocks = loaded_blocks;
1205         }
1206
1207         actionstream << "ServerEnvironment::clearObjects(): "
1208                 << "Now clearing objects in " << loadable_blocks.size()
1209                 << " blocks" << std::endl;
1210
1211         // Grab a reference on each loaded block to avoid unloading it
1212         for (v3s16 p : loaded_blocks) {
1213                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1214                 assert(block != NULL);
1215                 block->refGrab();
1216         }
1217
1218         // Remove objects in all loadable blocks
1219         u32 unload_interval = U32_MAX;
1220         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1221                 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1222                 unload_interval = MYMAX(unload_interval, 1);
1223         }
1224         u32 report_interval = loadable_blocks.size() / 10;
1225         u32 num_blocks_checked = 0;
1226         u32 num_blocks_cleared = 0;
1227         u32 num_objs_cleared = 0;
1228         for (auto i = loadable_blocks.begin();
1229                 i != loadable_blocks.end(); ++i) {
1230                 v3s16 p = *i;
1231                 MapBlock *block = m_map->emergeBlock(p, false);
1232                 if (!block) {
1233                         errorstream << "ServerEnvironment::clearObjects(): "
1234                                 << "Failed to emerge block " << PP(p) << std::endl;
1235                         continue;
1236                 }
1237                 u32 num_stored = block->m_static_objects.m_stored.size();
1238                 u32 num_active = block->m_static_objects.m_active.size();
1239                 if (num_stored != 0 || num_active != 0) {
1240                         block->m_static_objects.m_stored.clear();
1241                         block->m_static_objects.m_active.clear();
1242                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1243                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1244                         num_objs_cleared += num_stored + num_active;
1245                         num_blocks_cleared++;
1246                 }
1247                 num_blocks_checked++;
1248
1249                 if (report_interval != 0 &&
1250                         num_blocks_checked % report_interval == 0) {
1251                         float percent = 100.0 * (float)num_blocks_checked /
1252                                 loadable_blocks.size();
1253                         actionstream << "ServerEnvironment::clearObjects(): "
1254                                 << "Cleared " << num_objs_cleared << " objects"
1255                                 << " in " << num_blocks_cleared << " blocks ("
1256                                 << percent << "%)" << std::endl;
1257                 }
1258                 if (num_blocks_checked % unload_interval == 0) {
1259                         m_map->unloadUnreferencedBlocks();
1260                 }
1261         }
1262         m_map->unloadUnreferencedBlocks();
1263
1264         // Drop references that were added above
1265         for (v3s16 p : loaded_blocks) {
1266                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1267                 assert(block);
1268                 block->refDrop();
1269         }
1270
1271         m_last_clear_objects_time = m_game_time;
1272
1273         actionstream << "ServerEnvironment::clearObjects(): "
1274                 << "Finished: Cleared " << num_objs_cleared << " objects"
1275                 << " in " << num_blocks_cleared << " blocks" << std::endl;
1276 }
1277
1278 void ServerEnvironment::step(float dtime)
1279 {
1280         ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
1281         /* Step time of day */
1282         stepTimeOfDay(dtime);
1283
1284         // Update this one
1285         // NOTE: This is kind of funny on a singleplayer game, but doesn't
1286         // really matter that much.
1287         static thread_local const float server_step =
1288                         g_settings->getFloat("dedicated_server_step");
1289         m_recommended_send_interval = server_step;
1290
1291         /*
1292                 Increment game time
1293         */
1294         {
1295                 m_game_time_fraction_counter += dtime;
1296                 u32 inc_i = (u32)m_game_time_fraction_counter;
1297                 m_game_time += inc_i;
1298                 m_game_time_fraction_counter -= (float)inc_i;
1299         }
1300
1301         /*
1302                 Handle players
1303         */
1304         {
1305                 ScopeProfiler sp(g_profiler, "ServerEnv: move players", SPT_AVG);
1306                 for (RemotePlayer *player : m_players) {
1307                         // Ignore disconnected players
1308                         if (player->getPeerId() == PEER_ID_INEXISTENT)
1309                                 continue;
1310
1311                         // Move
1312                         player->move(dtime, this, 100 * BS);
1313                 }
1314         }
1315
1316         /*
1317                 Manage active block list
1318         */
1319         if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1320                 ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
1321                 /*
1322                         Get player block positions
1323                 */
1324                 std::vector<PlayerSAO*> players;
1325                 for (RemotePlayer *player: m_players) {
1326                         // Ignore disconnected players
1327                         if (player->getPeerId() == PEER_ID_INEXISTENT)
1328                                 continue;
1329
1330                         PlayerSAO *playersao = player->getPlayerSAO();
1331                         assert(playersao);
1332
1333                         players.push_back(playersao);
1334                 }
1335
1336                 /*
1337                         Update list of active blocks, collecting changes
1338                 */
1339                 // use active_object_send_range_blocks since that is max distance
1340                 // for active objects sent the client anyway
1341                 static thread_local const s16 active_object_range =
1342                                 g_settings->getS16("active_object_send_range_blocks");
1343                 static thread_local const s16 active_block_range =
1344                                 g_settings->getS16("active_block_range");
1345                 std::set<v3s16> blocks_removed;
1346                 std::set<v3s16> blocks_added;
1347                 m_active_blocks.update(players, active_block_range, active_object_range,
1348                         blocks_removed, blocks_added);
1349
1350                 /*
1351                         Handle removed blocks
1352                 */
1353
1354                 // Convert active objects that are no more in active blocks to static
1355                 deactivateFarObjects(false);
1356
1357                 for (const v3s16 &p: blocks_removed) {
1358                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1359                         if (!block)
1360                                 continue;
1361
1362                         // Set current time as timestamp (and let it set ChangedFlag)
1363                         block->setTimestamp(m_game_time);
1364                 }
1365
1366                 /*
1367                         Handle added blocks
1368                 */
1369
1370                 for (const v3s16 &p: blocks_added) {
1371                         MapBlock *block = m_map->getBlockOrEmerge(p);
1372                         if (!block) {
1373                                 m_active_blocks.m_list.erase(p);
1374                                 m_active_blocks.m_abm_list.erase(p);
1375                                 continue;
1376                         }
1377
1378                         activateBlock(block);
1379                 }
1380         }
1381
1382         /*
1383                 Mess around in active blocks
1384         */
1385         if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1386                 ScopeProfiler sp(g_profiler, "ServerEnv: Run node timers", SPT_AVG);
1387
1388                 float dtime = m_cache_nodetimer_interval;
1389
1390                 for (const v3s16 &p: m_active_blocks.m_list) {
1391                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1392                         if (!block)
1393                                 continue;
1394
1395                         // Reset block usage timer
1396                         block->resetUsageTimer();
1397
1398                         // Set current time as timestamp
1399                         block->setTimestampNoChangedFlag(m_game_time);
1400                         // If time has changed much from the one on disk,
1401                         // set block to be saved when it is unloaded
1402                         if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1403                                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1404                                         MOD_REASON_BLOCK_EXPIRED);
1405
1406                         // Run node timers
1407                         std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1408                         if (!elapsed_timers.empty()) {
1409                                 MapNode n;
1410                                 v3s16 p2;
1411                                 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1412                                         n = block->getNodeNoEx(elapsed_timer.position);
1413                                         p2 = elapsed_timer.position + block->getPosRelative();
1414                                         if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1415                                                 block->setNodeTimer(NodeTimer(
1416                                                         elapsed_timer.timeout, 0, elapsed_timer.position));
1417                                         }
1418                                 }
1419                         }
1420                 }
1421         }
1422
1423         if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) {
1424                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1425                 TimeTaker timer("modify in active blocks per interval");
1426
1427                 // Initialize handling of ActiveBlockModifiers
1428                 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1429
1430                 int blocks_scanned = 0;
1431                 int abms_run = 0;
1432                 int blocks_cached = 0;
1433
1434                 std::vector<v3s16> output(m_active_blocks.m_abm_list.size());
1435
1436                 // Shuffle the active blocks so that each block gets an equal chance
1437                 // of having its ABMs run.
1438                 std::copy(m_active_blocks.m_abm_list.begin(), m_active_blocks.m_abm_list.end(), output.begin());
1439                 std::shuffle(output.begin(), output.end(), m_rgen);
1440
1441                 int i = 0;
1442                 // determine the time budget for ABMs
1443                 u32 max_time_ms = m_cache_abm_interval * 1000 * m_cache_abm_time_budget;
1444                 for (const v3s16 &p : output) {
1445                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1446                         if (!block)
1447                                 continue;
1448
1449                         i++;
1450
1451                         // Set current time as timestamp
1452                         block->setTimestampNoChangedFlag(m_game_time);
1453
1454                         /* Handle ActiveBlockModifiers */
1455                         abmhandler.apply(block, blocks_scanned, abms_run, blocks_cached);
1456
1457                         u32 time_ms = timer.getTimerTime();
1458
1459                         if (time_ms > max_time_ms) {
1460                                 warningstream << "active block modifiers took "
1461                                           << time_ms << "ms (processed " << i << " of "
1462                                           << output.size() << " active blocks)" << std::endl;
1463                                 break;
1464                         }
1465                 }
1466                 g_profiler->avg("ServerEnv: active blocks", m_active_blocks.m_abm_list.size());
1467                 g_profiler->avg("ServerEnv: active blocks cached", blocks_cached);
1468                 g_profiler->avg("ServerEnv: active blocks scanned for ABMs", blocks_scanned);
1469                 g_profiler->avg("ServerEnv: ABMs run", abms_run);
1470
1471                 timer.stop(true);
1472         }
1473
1474         /*
1475                 Step script environment (run global on_step())
1476         */
1477         m_script->environment_Step(dtime);
1478
1479         /*
1480                 Step active objects
1481         */
1482         {
1483                 ScopeProfiler sp(g_profiler, "ServerEnv: Run SAO::step()", SPT_AVG);
1484
1485                 // This helps the objects to send data at the same time
1486                 bool send_recommended = false;
1487                 m_send_recommended_timer += dtime;
1488                 if (m_send_recommended_timer > getSendRecommendedInterval()) {
1489                         m_send_recommended_timer -= getSendRecommendedInterval();
1490                         send_recommended = true;
1491                 }
1492
1493                 auto cb_state = [this, dtime, send_recommended] (ServerActiveObject *obj) {
1494                         if (obj->isGone())
1495                                 return;
1496
1497                         // Step object
1498                         obj->step(dtime, send_recommended);
1499                         // Read messages from object
1500                         obj->dumpAOMessagesToQueue(m_active_object_messages);
1501                 };
1502                 m_ao_manager.step(dtime, cb_state);
1503         }
1504
1505         /*
1506                 Manage active objects
1507         */
1508         if (m_object_management_interval.step(dtime, 0.5)) {
1509                 removeRemovedObjects();
1510         }
1511
1512         /*
1513                 Manage particle spawner expiration
1514         */
1515         if (m_particle_management_interval.step(dtime, 1.0)) {
1516                 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1517                         i != m_particle_spawners.end(); ) {
1518                         //non expiring spawners
1519                         if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1520                                 ++i;
1521                                 continue;
1522                         }
1523
1524                         i->second -= 1.0f;
1525                         if (i->second <= 0.f)
1526                                 m_particle_spawners.erase(i++);
1527                         else
1528                                 ++i;
1529                 }
1530         }
1531
1532         // Send outdated player inventories
1533         for (RemotePlayer *player : m_players) {
1534                 if (player->getPeerId() == PEER_ID_INEXISTENT)
1535                         continue;
1536
1537                 PlayerSAO *sao = player->getPlayerSAO();
1538                 if (sao && player->inventory.checkModified())
1539                         m_server->SendInventory(sao, true);
1540         }
1541
1542         // Send outdated detached inventories
1543         m_server->sendDetachedInventories(PEER_ID_INEXISTENT, true);
1544 }
1545
1546 u32 ServerEnvironment::addParticleSpawner(float exptime)
1547 {
1548         // Timers with lifetime 0 do not expire
1549         float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1550
1551         u32 id = 0;
1552         for (;;) { // look for unused particlespawner id
1553                 id++;
1554                 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1555                 if (f == m_particle_spawners.end()) {
1556                         m_particle_spawners[id] = time;
1557                         break;
1558                 }
1559         }
1560         return id;
1561 }
1562
1563 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1564 {
1565         u32 id = addParticleSpawner(exptime);
1566         m_particle_spawner_attachments[id] = attached_id;
1567         if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1568                 obj->attachParticleSpawner(id);
1569         }
1570         return id;
1571 }
1572
1573 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1574 {
1575         m_particle_spawners.erase(id);
1576         const auto &it = m_particle_spawner_attachments.find(id);
1577         if (it != m_particle_spawner_attachments.end()) {
1578                 u16 obj_id = it->second;
1579                 ServerActiveObject *sao = getActiveObject(obj_id);
1580                 if (sao != NULL && remove_from_object) {
1581                         sao->detachParticleSpawner(id);
1582                 }
1583                 m_particle_spawner_attachments.erase(id);
1584         }
1585 }
1586
1587 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1588 {
1589         assert(object); // Pre-condition
1590         m_added_objects++;
1591         u16 id = addActiveObjectRaw(object, true, 0);
1592         return id;
1593 }
1594
1595 /*
1596         Finds out what new objects have been added to
1597         inside a radius around a position
1598 */
1599 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1600         s16 player_radius,
1601         std::set<u16> &current_objects,
1602         std::queue<u16> &added_objects)
1603 {
1604         f32 radius_f = radius * BS;
1605         f32 player_radius_f = player_radius * BS;
1606
1607         if (player_radius_f < 0.0f)
1608                 player_radius_f = 0.0f;
1609
1610         m_ao_manager.getAddedActiveObjectsAroundPos(playersao->getBasePosition(), radius_f,
1611                 player_radius_f, current_objects, added_objects);
1612 }
1613
1614 /*
1615         Finds out what objects have been removed from
1616         inside a radius around a position
1617 */
1618 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1619         s16 player_radius,
1620         std::set<u16> &current_objects,
1621         std::queue<u16> &removed_objects)
1622 {
1623         f32 radius_f = radius * BS;
1624         f32 player_radius_f = player_radius * BS;
1625
1626         if (player_radius_f < 0)
1627                 player_radius_f = 0;
1628         /*
1629                 Go through current_objects; object is removed if:
1630                 - object is not found in m_active_objects (this is actually an
1631                   error condition; objects should be removed only after all clients
1632                   have been informed about removal), or
1633                 - object is to be removed or deactivated, or
1634                 - object is too far away
1635         */
1636         for (u16 id : current_objects) {
1637                 ServerActiveObject *object = getActiveObject(id);
1638
1639                 if (object == NULL) {
1640                         infostream << "ServerEnvironment::getRemovedActiveObjects():"
1641                                 << " object in current_objects is NULL" << std::endl;
1642                         removed_objects.push(id);
1643                         continue;
1644                 }
1645
1646                 if (object->isGone()) {
1647                         removed_objects.push(id);
1648                         continue;
1649                 }
1650
1651                 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1652                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1653                         if (distance_f <= player_radius_f || player_radius_f == 0)
1654                                 continue;
1655                 } else if (distance_f <= radius_f)
1656                         continue;
1657
1658                 // Object is no longer visible
1659                 removed_objects.push(id);
1660         }
1661 }
1662
1663 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1664         v3s16 blockpos, bool static_exists, v3s16 static_block)
1665 {
1666         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1667         if (!block)
1668                 return;
1669
1670         for (auto &so_it : block->m_static_objects.m_active) {
1671                 // Get the ServerActiveObject counterpart to this StaticObject
1672                 ServerActiveObject *sao = m_ao_manager.getActiveObject(so_it.first);
1673                 if (!sao) {
1674                         // If this ever happens, there must be some kind of nasty bug.
1675                         errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1676                                 "Object from MapBlock::m_static_objects::m_active not found "
1677                                 "in m_active_objects";
1678                         continue;
1679                 }
1680
1681                 sao->m_static_exists = static_exists;
1682                 sao->m_static_block  = static_block;
1683         }
1684 }
1685
1686 bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest)
1687 {
1688         if(m_active_object_messages.empty())
1689                 return false;
1690
1691         *dest = std::move(m_active_object_messages.front());
1692         m_active_object_messages.pop();
1693         return true;
1694 }
1695
1696 void ServerEnvironment::getSelectedActiveObjects(
1697         const core::line3d<f32> &shootline_on_map,
1698         std::vector<PointedThing> &objects)
1699 {
1700         std::vector<ServerActiveObject *> objs;
1701         getObjectsInsideRadius(objs, shootline_on_map.start,
1702                 shootline_on_map.getLength() + 10.0f, nullptr);
1703         const v3f line_vector = shootline_on_map.getVector();
1704
1705         for (auto obj : objs) {
1706                 if (obj->isGone())
1707                         continue;
1708                 aabb3f selection_box;
1709                 if (!obj->getSelectionBox(&selection_box))
1710                         continue;
1711
1712                 v3f pos = obj->getBasePosition();
1713
1714                 aabb3f offsetted_box(selection_box.MinEdge + pos,
1715                         selection_box.MaxEdge + pos);
1716
1717                 v3f current_intersection;
1718                 v3s16 current_normal;
1719                 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1720                                 &current_intersection, &current_normal)) {
1721                         objects.emplace_back(
1722                                 (s16) obj->getId(), current_intersection, current_normal,
1723                                 (current_intersection - shootline_on_map.start).getLengthSQ());
1724                 }
1725         }
1726 }
1727
1728 /*
1729         ************ Private methods *************
1730 */
1731
1732 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1733         bool set_changed, u32 dtime_s)
1734 {
1735         if (!m_ao_manager.registerObject(object)) {
1736                 return 0;
1737         }
1738
1739         // Register reference in scripting api (must be done before post-init)
1740         m_script->addObjectReference(object);
1741         // Post-initialize object
1742         object->addedToEnvironment(dtime_s);
1743
1744         // Add static data to block
1745         if (object->isStaticAllowed()) {
1746                 // Add static object to active static list of the block
1747                 v3f objectpos = object->getBasePosition();
1748                 StaticObject s_obj(object, objectpos);
1749                 // Add to the block where the object is located in
1750                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1751                 MapBlock *block = m_map->emergeBlock(blockpos);
1752                 if(block){
1753                         block->m_static_objects.m_active[object->getId()] = s_obj;
1754                         object->m_static_exists = true;
1755                         object->m_static_block = blockpos;
1756
1757                         if(set_changed)
1758                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1759                                         MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1760                 } else {
1761                         v3s16 p = floatToInt(objectpos, BS);
1762                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1763                                 <<"could not emerge block for storing id="<<object->getId()
1764                                 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1765                 }
1766         }
1767
1768         return object->getId();
1769 }
1770
1771 /*
1772         Remove objects that satisfy (isGone() && m_known_by_count==0)
1773 */
1774 void ServerEnvironment::removeRemovedObjects()
1775 {
1776         ScopeProfiler sp(g_profiler, "ServerEnvironment::removeRemovedObjects()", SPT_AVG);
1777
1778         auto clear_cb = [this] (ServerActiveObject *obj, u16 id) {
1779                 // This shouldn't happen but check it
1780                 if (!obj) {
1781                         errorstream << "ServerEnvironment::removeRemovedObjects(): "
1782                                         << "NULL object found. id=" << id << std::endl;
1783                         return true;
1784                 }
1785
1786                 /*
1787                         We will handle objects marked for removal or deactivation
1788                 */
1789                 if (!obj->isGone())
1790                         return false;
1791
1792                 /*
1793                         Delete static data from block if removed
1794                 */
1795                 if (obj->m_pending_removal)
1796                         deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1797
1798                 // If still known by clients, don't actually remove. On some future
1799                 // invocation this will be 0, which is when removal will continue.
1800                 if(obj->m_known_by_count > 0)
1801                         return false;
1802
1803                 /*
1804                         Move static data from active to stored if deactivated
1805                 */
1806                 if (!obj->m_pending_removal && obj->m_static_exists) {
1807                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1808                         if (block) {
1809                                 const auto i = block->m_static_objects.m_active.find(id);
1810                                 if (i != block->m_static_objects.m_active.end()) {
1811                                         block->m_static_objects.m_stored.push_back(i->second);
1812                                         block->m_static_objects.m_active.erase(id);
1813                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1814                                                 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1815                                 } else {
1816                                         warningstream << "ServerEnvironment::removeRemovedObjects(): "
1817                                                         << "id=" << id << " m_static_exists=true but "
1818                                                         << "static data doesn't actually exist in "
1819                                                         << PP(obj->m_static_block) << std::endl;
1820                                 }
1821                         } else {
1822                                 infostream << "Failed to emerge block from which an object to "
1823                                                 << "be deactivated was loaded from. id=" << id << std::endl;
1824                         }
1825                 }
1826
1827                 // Tell the object about removal
1828                 obj->removingFromEnvironment();
1829                 // Deregister in scripting api
1830                 m_script->removeObjectReference(obj);
1831
1832                 // Delete
1833                 if (obj->environmentDeletes())
1834                         delete obj;
1835
1836                 return true;
1837         };
1838
1839         m_ao_manager.clear(clear_cb);
1840 }
1841
1842 static void print_hexdump(std::ostream &o, const std::string &data)
1843 {
1844         const int linelength = 16;
1845         for(int l=0; ; l++){
1846                 int i0 = linelength * l;
1847                 bool at_end = false;
1848                 int thislinelength = linelength;
1849                 if(i0 + thislinelength > (int)data.size()){
1850                         thislinelength = data.size() - i0;
1851                         at_end = true;
1852                 }
1853                 for(int di=0; di<linelength; di++){
1854                         int i = i0 + di;
1855                         char buf[4];
1856                         if(di<thislinelength)
1857                                 porting::mt_snprintf(buf, sizeof(buf), "%.2x ", data[i]);
1858                         else
1859                                 porting::mt_snprintf(buf, sizeof(buf), "   ");
1860                         o<<buf;
1861                 }
1862                 o<<" ";
1863                 for(int di=0; di<thislinelength; di++){
1864                         int i = i0 + di;
1865                         if(data[i] >= 32)
1866                                 o<<data[i];
1867                         else
1868                                 o<<".";
1869                 }
1870                 o<<std::endl;
1871                 if(at_end)
1872                         break;
1873         }
1874 }
1875
1876 ServerActiveObject* ServerEnvironment::createSAO(ActiveObjectType type, v3f pos,
1877                 const std::string &data)
1878 {
1879         switch (type) {
1880                 case ACTIVEOBJECT_TYPE_LUAENTITY:
1881                         return new LuaEntitySAO(this, pos, data);
1882                 default:
1883                         warningstream << "ServerActiveObject: No factory for type=" << type << std::endl;
1884         }
1885         return nullptr;
1886 }
1887
1888 /*
1889         Convert stored objects from blocks near the players to active.
1890 */
1891 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1892 {
1893         if(block == NULL)
1894                 return;
1895
1896         // Ignore if no stored objects (to not set changed flag)
1897         if(block->m_static_objects.m_stored.empty())
1898                 return;
1899
1900         verbosestream<<"ServerEnvironment::activateObjects(): "
1901                 <<"activating objects of block "<<PP(block->getPos())
1902                 <<" ("<<block->m_static_objects.m_stored.size()
1903                 <<" objects)"<<std::endl;
1904         bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1905         if (large_amount) {
1906                 errorstream<<"suspiciously large amount of objects detected: "
1907                         <<block->m_static_objects.m_stored.size()<<" in "
1908                         <<PP(block->getPos())
1909                         <<"; removing all of them."<<std::endl;
1910                 // Clear stored list
1911                 block->m_static_objects.m_stored.clear();
1912                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1913                         MOD_REASON_TOO_MANY_OBJECTS);
1914                 return;
1915         }
1916
1917         // Activate stored objects
1918         std::vector<StaticObject> new_stored;
1919         for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1920                 // Create an active object from the data
1921                 ServerActiveObject *obj = createSAO((ActiveObjectType) s_obj.type, s_obj.pos,
1922                         s_obj.data);
1923                 // If couldn't create object, store static data back.
1924                 if (!obj) {
1925                         errorstream<<"ServerEnvironment::activateObjects(): "
1926                                 <<"failed to create active object from static object "
1927                                 <<"in block "<<PP(s_obj.pos/BS)
1928                                 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1929                         print_hexdump(verbosestream, s_obj.data);
1930
1931                         new_stored.push_back(s_obj);
1932                         continue;
1933                 }
1934                 verbosestream<<"ServerEnvironment::activateObjects(): "
1935                         <<"activated static object pos="<<PP(s_obj.pos/BS)
1936                         <<" type="<<(int)s_obj.type<<std::endl;
1937                 // This will also add the object to the active static list
1938                 addActiveObjectRaw(obj, false, dtime_s);
1939         }
1940
1941         // Clear stored list
1942         block->m_static_objects.m_stored.clear();
1943         // Add leftover failed stuff to stored list
1944         for (const StaticObject &s_obj : new_stored) {
1945                 block->m_static_objects.m_stored.push_back(s_obj);
1946         }
1947
1948         /*
1949                 Note: Block hasn't really been modified here.
1950                 The objects have just been activated and moved from the stored
1951                 static list to the active static list.
1952                 As such, the block is essentially the same.
1953                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1954                 Otherwise there would be a huge amount of unnecessary I/O.
1955         */
1956 }
1957
1958 /*
1959         Convert objects that are not standing inside active blocks to static.
1960
1961         If m_known_by_count != 0, active object is not deleted, but static
1962         data is still updated.
1963
1964         If force_delete is set, active object is deleted nevertheless. It
1965         shall only be set so in the destructor of the environment.
1966
1967         If block wasn't generated (not in memory or on disk),
1968 */
1969 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1970 {
1971         auto cb_deactivate = [this, _force_delete] (ServerActiveObject *obj, u16 id) {
1972                 // force_delete might be overriden per object
1973                 bool force_delete = _force_delete;
1974
1975                 // Do not deactivate if disallowed
1976                 if (!force_delete && !obj->shouldUnload())
1977                         return false;
1978
1979                 // removeRemovedObjects() is responsible for these
1980                 if (!force_delete && obj->isGone())
1981                         return false;
1982
1983                 const v3f &objectpos = obj->getBasePosition();
1984
1985                 // The block in which the object resides in
1986                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1987
1988                 // If object's static data is stored in a deactivated block and object
1989                 // is actually located in an active block, re-save to the block in
1990                 // which the object is actually located in.
1991                 if (!force_delete && obj->m_static_exists &&
1992                    !m_active_blocks.contains(obj->m_static_block) &&
1993                    m_active_blocks.contains(blockpos_o)) {
1994                         // Delete from block where object was located
1995                         deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
1996
1997                         StaticObject s_obj(obj, objectpos);
1998                         // Save to block where object is located
1999                         saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
2000
2001                         return false;
2002                 }
2003
2004                 // If block is still active, don't remove
2005                 bool still_active = obj->isStaticAllowed() ?
2006                         m_active_blocks.contains(blockpos_o) :
2007                         getMap().getBlockNoCreateNoEx(blockpos_o) != nullptr;
2008                 if (!force_delete && still_active)
2009                         return false;
2010
2011                 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2012                                           << "deactivating object id=" << id << " on inactive block "
2013                                           << PP(blockpos_o) << std::endl;
2014
2015                 // If known by some client, don't immediately delete.
2016                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2017
2018                 /*
2019                         Update the static data
2020                 */
2021                 if (obj->isStaticAllowed()) {
2022                         // Create new static object
2023                         StaticObject s_obj(obj, objectpos);
2024
2025                         bool stays_in_same_block = false;
2026                         bool data_changed = true;
2027
2028                         // Check if static data has changed considerably
2029                         if (obj->m_static_exists) {
2030                                 if (obj->m_static_block == blockpos_o)
2031                                         stays_in_same_block = true;
2032
2033                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2034
2035                                 if (block) {
2036                                         const auto n = block->m_static_objects.m_active.find(id);
2037                                         if (n != block->m_static_objects.m_active.end()) {
2038                                                 StaticObject static_old = n->second;
2039
2040                                                 float save_movem = obj->getMinimumSavedMovement();
2041
2042                                                 if (static_old.data == s_obj.data &&
2043                                                         (static_old.pos - objectpos).getLength() < save_movem)
2044                                                         data_changed = false;
2045                                         } else {
2046                                                 warningstream << "ServerEnvironment::deactivateFarObjects(): "
2047                                                                 << "id=" << id << " m_static_exists=true but "
2048                                                                 << "static data doesn't actually exist in "
2049                                                                 << PP(obj->m_static_block) << std::endl;
2050                                         }
2051                                 }
2052                         }
2053
2054                         /*
2055                                 While changes are always saved, blocks are only marked as modified
2056                                 if the object has moved or different staticdata. (see above)
2057                         */
2058                         bool shall_be_written = (!stays_in_same_block || data_changed);
2059                         u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2060
2061                         // Delete old static object
2062                         deleteStaticFromBlock(obj, id, reason, false);
2063
2064                         // Add to the block where the object is located in
2065                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2066                         u16 store_id = pending_delete ? id : 0;
2067                         if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2068                                 force_delete = true;
2069                 }
2070
2071                 /*
2072                         If known by some client, set pending deactivation.
2073                         Otherwise delete it immediately.
2074                 */
2075                 if (pending_delete && !force_delete) {
2076                         verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2077                                                   << "object id=" << id << " is known by clients"
2078                                                   << "; not deleting yet" << std::endl;
2079
2080                         obj->m_pending_deactivation = true;
2081                         return false;
2082                 }
2083
2084                 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2085                                           << "object id=" << id << " is not known by clients"
2086                                           << "; deleting" << std::endl;
2087
2088                 // Tell the object about removal
2089                 obj->removingFromEnvironment();
2090                 // Deregister in scripting api
2091                 m_script->removeObjectReference(obj);
2092
2093                 // Delete active object
2094                 if (obj->environmentDeletes())
2095                         delete obj;
2096
2097                 return true;
2098         };
2099
2100         m_ao_manager.clear(cb_deactivate);
2101 }
2102
2103 void ServerEnvironment::deleteStaticFromBlock(
2104                 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2105 {
2106         if (!obj->m_static_exists)
2107                 return;
2108
2109         MapBlock *block;
2110         if (no_emerge)
2111                 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2112         else
2113                 block = m_map->emergeBlock(obj->m_static_block, false);
2114         if (!block) {
2115                 if (!no_emerge)
2116                         errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2117                                         << " when deleting static data of object from it. id=" << id << std::endl;
2118                 return;
2119         }
2120
2121         block->m_static_objects.remove(id);
2122         if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2123                 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2124
2125         obj->m_static_exists = false;
2126 }
2127
2128 bool ServerEnvironment::saveStaticToBlock(
2129                 v3s16 blockpos, u16 store_id,
2130                 ServerActiveObject *obj, const StaticObject &s_obj,
2131                 u32 mod_reason)
2132 {
2133         MapBlock *block = nullptr;
2134         try {
2135                 block = m_map->emergeBlock(blockpos);
2136         } catch (InvalidPositionException &e) {
2137                 // Handled via NULL pointer
2138                 // NOTE: emergeBlock's failure is usually determined by it
2139                 //       actually returning NULL
2140         }
2141
2142         if (!block) {
2143                 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2144                                 << " when saving static data of object to it. id=" << store_id << std::endl;
2145                 return false;
2146         }
2147         if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2148                 warningstream << "ServerEnv: Trying to store id = " << store_id
2149                                 << " statically but block " << PP(blockpos)
2150                                 << " already contains "
2151                                 << block->m_static_objects.m_stored.size()
2152                                 << " objects." << std::endl;
2153                 return false;
2154         }
2155
2156         block->m_static_objects.insert(store_id, s_obj);
2157         if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2158                 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2159
2160         obj->m_static_exists = true;
2161         obj->m_static_block = blockpos;
2162
2163         return true;
2164 }
2165
2166 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2167                 const std::string &savedir, const Settings &conf)
2168 {
2169
2170         if (name == "sqlite3")
2171                 return new PlayerDatabaseSQLite3(savedir);
2172
2173         if (name == "dummy")
2174                 return new Database_Dummy();
2175
2176 #if USE_POSTGRESQL
2177         if (name == "postgresql") {
2178                 std::string connect_string;
2179                 conf.getNoEx("pgsql_player_connection", connect_string);
2180                 return new PlayerDatabasePostgreSQL(connect_string);
2181         }
2182 #endif
2183
2184 #if USE_LEVELDB
2185         if (name == "leveldb")
2186                 return new PlayerDatabaseLevelDB(savedir);
2187 #endif
2188
2189         if (name == "files")
2190                 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2191
2192         throw BaseException(std::string("Database backend ") + name + " not supported.");
2193 }
2194
2195 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2196                 const Settings &cmd_args)
2197 {
2198         std::string migrate_to = cmd_args.get("migrate-players");
2199         Settings world_mt;
2200         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2201         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2202                 errorstream << "Cannot read world.mt!" << std::endl;
2203                 return false;
2204         }
2205
2206         if (!world_mt.exists("player_backend")) {
2207                 errorstream << "Please specify your current backend in world.mt:"
2208                         << std::endl
2209                         << "    player_backend = {files|sqlite3|leveldb|postgresql}"
2210                         << std::endl;
2211                 return false;
2212         }
2213
2214         std::string backend = world_mt.get("player_backend");
2215         if (backend == migrate_to) {
2216                 errorstream << "Cannot migrate: new backend is same"
2217                         << " as the old one" << std::endl;
2218                 return false;
2219         }
2220
2221         const std::string players_backup_path = game_params.world_path + DIR_DELIM
2222                 + "players.bak";
2223
2224         if (backend == "files") {
2225                 // Create backup directory
2226                 fs::CreateDir(players_backup_path);
2227         }
2228
2229         try {
2230                 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2231                         game_params.world_path, world_mt);
2232                 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2233                         game_params.world_path, world_mt);
2234
2235                 std::vector<std::string> player_list;
2236                 srcdb->listPlayers(player_list);
2237                 for (std::vector<std::string>::const_iterator it = player_list.begin();
2238                         it != player_list.end(); ++it) {
2239                         actionstream << "Migrating player " << it->c_str() << std::endl;
2240                         RemotePlayer player(it->c_str(), NULL);
2241                         PlayerSAO playerSAO(NULL, &player, 15000, false);
2242
2243                         srcdb->loadPlayer(&player, &playerSAO);
2244
2245                         playerSAO.finalize(&player, std::set<std::string>());
2246                         player.setPlayerSAO(&playerSAO);
2247
2248                         dstdb->savePlayer(&player);
2249
2250                         // For files source, move player files to backup dir
2251                         if (backend == "files") {
2252                                 fs::Rename(
2253                                         game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2254                                         players_backup_path + DIR_DELIM + (*it));
2255                         }
2256                 }
2257
2258                 actionstream << "Successfully migrated " << player_list.size() << " players"
2259                         << std::endl;
2260                 world_mt.set("player_backend", migrate_to);
2261                 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2262                         errorstream << "Failed to update world.mt!" << std::endl;
2263                 else
2264                         actionstream << "world.mt updated" << std::endl;
2265
2266                 // When migration is finished from file backend, remove players directory if empty
2267                 if (backend == "files") {
2268                         fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2269                                 + "players");
2270                 }
2271
2272                 delete srcdb;
2273                 delete dstdb;
2274
2275         } catch (BaseException &e) {
2276                 errorstream << "An error occurred during migration: " << e.what() << std::endl;
2277                 return false;
2278         }
2279         return true;
2280 }
2281
2282 AuthDatabase *ServerEnvironment::openAuthDatabase(
2283                 const std::string &name, const std::string &savedir, const Settings &conf)
2284 {
2285         if (name == "sqlite3")
2286                 return new AuthDatabaseSQLite3(savedir);
2287
2288 #if USE_POSTGRESQL
2289         if (name == "postgresql") {
2290                 std::string connect_string;
2291                 conf.getNoEx("pgsql_auth_connection", connect_string);
2292                 return new AuthDatabasePostgreSQL(connect_string);
2293         }
2294 #endif
2295
2296         if (name == "files")
2297                 return new AuthDatabaseFiles(savedir);
2298
2299 #if USE_LEVELDB
2300         if (name == "leveldb")
2301                 return new AuthDatabaseLevelDB(savedir);
2302 #endif
2303
2304         throw BaseException(std::string("Database backend ") + name + " not supported.");
2305 }
2306
2307 bool ServerEnvironment::migrateAuthDatabase(
2308                 const GameParams &game_params, const Settings &cmd_args)
2309 {
2310         std::string migrate_to = cmd_args.get("migrate-auth");
2311         Settings world_mt;
2312         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2313         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2314                 errorstream << "Cannot read world.mt!" << std::endl;
2315                 return false;
2316         }
2317
2318         std::string backend = "files";
2319         if (world_mt.exists("auth_backend"))
2320                 backend = world_mt.get("auth_backend");
2321         else
2322                 warningstream << "No auth_backend found in world.mt, "
2323                                 "assuming \"files\"." << std::endl;
2324
2325         if (backend == migrate_to) {
2326                 errorstream << "Cannot migrate: new backend is same"
2327                                 << " as the old one" << std::endl;
2328                 return false;
2329         }
2330
2331         try {
2332                 const std::unique_ptr<AuthDatabase> srcdb(ServerEnvironment::openAuthDatabase(
2333                                 backend, game_params.world_path, world_mt));
2334                 const std::unique_ptr<AuthDatabase> dstdb(ServerEnvironment::openAuthDatabase(
2335                                 migrate_to, game_params.world_path, world_mt));
2336
2337                 std::vector<std::string> names_list;
2338                 srcdb->listNames(names_list);
2339                 for (const std::string &name : names_list) {
2340                         actionstream << "Migrating auth entry for " << name << std::endl;
2341                         bool success;
2342                         AuthEntry authEntry;
2343                         success = srcdb->getAuth(name, authEntry);
2344                         success = success && dstdb->createAuth(authEntry);
2345                         if (!success)
2346                                 errorstream << "Failed to migrate " << name << std::endl;
2347                 }
2348
2349                 actionstream << "Successfully migrated " << names_list.size()
2350                                 << " auth entries" << std::endl;
2351                 world_mt.set("auth_backend", migrate_to);
2352                 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2353                         errorstream << "Failed to update world.mt!" << std::endl;
2354                 else
2355                         actionstream << "world.mt updated" << std::endl;
2356
2357                 if (backend == "files") {
2358                         // special-case files migration:
2359                         // move auth.txt to auth.txt.bak if possible
2360                         std::string auth_txt_path =
2361                                         game_params.world_path + DIR_DELIM + "auth.txt";
2362                         std::string auth_bak_path = auth_txt_path + ".bak";
2363                         if (!fs::PathExists(auth_bak_path))
2364                                 if (fs::Rename(auth_txt_path, auth_bak_path))
2365                                         actionstream << "Renamed auth.txt to auth.txt.bak"
2366                                                         << std::endl;
2367                                 else
2368                                         errorstream << "Could not rename auth.txt to "
2369                                                         "auth.txt.bak" << std::endl;
2370                         else
2371                                 warningstream << "auth.txt.bak already exists, auth.txt "
2372                                                 "not renamed" << std::endl;
2373                 }
2374
2375         } catch (BaseException &e) {
2376                 errorstream << "An error occurred during migration: " << e.what()
2377                             << std::endl;
2378                 return false;
2379         }
2380         return true;
2381 }