]> git.lizzy.rs Git - minetest.git/blob - src/serverenvironment.cpp
Include irrlichttypes.h first to work around Irrlicht#433 (#10872)
[minetest.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->markForRemoval();
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->isPendingRemoval())
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->isPendingRemoval() && 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
1995                         // Delete from block where object was located
1996                         deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
1997
1998                         StaticObject s_obj(obj, objectpos);
1999                         // Save to block where object is located
2000                         saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
2001
2002                         return false;
2003                 }
2004
2005                 // If block is still active, don't remove
2006                 bool still_active = obj->isStaticAllowed() ?
2007                         m_active_blocks.contains(blockpos_o) :
2008                         getMap().getBlockNoCreateNoEx(blockpos_o) != nullptr;
2009                 if (!force_delete && still_active)
2010                         return false;
2011
2012                 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2013                                           << "deactivating object id=" << id << " on inactive block "
2014                                           << PP(blockpos_o) << std::endl;
2015
2016                 // If known by some client, don't immediately delete.
2017                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2018
2019                 /*
2020                         Update the static data
2021                 */
2022                 if (obj->isStaticAllowed()) {
2023                         // Create new static object
2024                         StaticObject s_obj(obj, objectpos);
2025
2026                         bool stays_in_same_block = false;
2027                         bool data_changed = true;
2028
2029                         // Check if static data has changed considerably
2030                         if (obj->m_static_exists) {
2031                                 if (obj->m_static_block == blockpos_o)
2032                                         stays_in_same_block = true;
2033
2034                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2035
2036                                 if (block) {
2037                                         const auto n = block->m_static_objects.m_active.find(id);
2038                                         if (n != block->m_static_objects.m_active.end()) {
2039                                                 StaticObject static_old = n->second;
2040
2041                                                 float save_movem = obj->getMinimumSavedMovement();
2042
2043                                                 if (static_old.data == s_obj.data &&
2044                                                         (static_old.pos - objectpos).getLength() < save_movem)
2045                                                         data_changed = false;
2046                                         } else {
2047                                                 warningstream << "ServerEnvironment::deactivateFarObjects(): "
2048                                                                 << "id=" << id << " m_static_exists=true but "
2049                                                                 << "static data doesn't actually exist in "
2050                                                                 << PP(obj->m_static_block) << std::endl;
2051                                         }
2052                                 }
2053                         }
2054
2055                         /*
2056                                 While changes are always saved, blocks are only marked as modified
2057                                 if the object has moved or different staticdata. (see above)
2058                         */
2059                         bool shall_be_written = (!stays_in_same_block || data_changed);
2060                         u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2061
2062                         // Delete old static object
2063                         deleteStaticFromBlock(obj, id, reason, false);
2064
2065                         // Add to the block where the object is located in
2066                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2067                         u16 store_id = pending_delete ? id : 0;
2068                         if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2069                                 force_delete = true;
2070                 }
2071
2072                 // Regardless of what happens to the object at this point, deactivate it first.
2073                 // This ensures that LuaEntity on_deactivate is always called.
2074                 obj->markForDeactivation();
2075
2076                 /*
2077                         If known by some client, set pending deactivation.
2078                         Otherwise delete it immediately.
2079                 */
2080                 if (pending_delete && !force_delete) {
2081                         verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2082                                                   << "object id=" << id << " is known by clients"
2083                                                   << "; not deleting yet" << std::endl;
2084
2085                         return false;
2086                 }
2087
2088                 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2089                                           << "object id=" << id << " is not known by clients"
2090                                           << "; deleting" << std::endl;
2091
2092                 // Tell the object about removal
2093                 obj->removingFromEnvironment();
2094                 // Deregister in scripting api
2095                 m_script->removeObjectReference(obj);
2096
2097                 // Delete active object
2098                 if (obj->environmentDeletes())
2099                         delete obj;
2100
2101                 return true;
2102         };
2103
2104         m_ao_manager.clear(cb_deactivate);
2105 }
2106
2107 void ServerEnvironment::deleteStaticFromBlock(
2108                 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2109 {
2110         if (!obj->m_static_exists)
2111                 return;
2112
2113         MapBlock *block;
2114         if (no_emerge)
2115                 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2116         else
2117                 block = m_map->emergeBlock(obj->m_static_block, false);
2118         if (!block) {
2119                 if (!no_emerge)
2120                         errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2121                                         << " when deleting static data of object from it. id=" << id << std::endl;
2122                 return;
2123         }
2124
2125         block->m_static_objects.remove(id);
2126         if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2127                 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2128
2129         obj->m_static_exists = false;
2130 }
2131
2132 bool ServerEnvironment::saveStaticToBlock(
2133                 v3s16 blockpos, u16 store_id,
2134                 ServerActiveObject *obj, const StaticObject &s_obj,
2135                 u32 mod_reason)
2136 {
2137         MapBlock *block = nullptr;
2138         try {
2139                 block = m_map->emergeBlock(blockpos);
2140         } catch (InvalidPositionException &e) {
2141                 // Handled via NULL pointer
2142                 // NOTE: emergeBlock's failure is usually determined by it
2143                 //       actually returning NULL
2144         }
2145
2146         if (!block) {
2147                 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2148                                 << " when saving static data of object to it. id=" << store_id << std::endl;
2149                 return false;
2150         }
2151         if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2152                 warningstream << "ServerEnv: Trying to store id = " << store_id
2153                                 << " statically but block " << PP(blockpos)
2154                                 << " already contains "
2155                                 << block->m_static_objects.m_stored.size()
2156                                 << " objects." << std::endl;
2157                 return false;
2158         }
2159
2160         block->m_static_objects.insert(store_id, s_obj);
2161         if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2162                 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2163
2164         obj->m_static_exists = true;
2165         obj->m_static_block = blockpos;
2166
2167         return true;
2168 }
2169
2170 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2171                 const std::string &savedir, const Settings &conf)
2172 {
2173
2174         if (name == "sqlite3")
2175                 return new PlayerDatabaseSQLite3(savedir);
2176
2177         if (name == "dummy")
2178                 return new Database_Dummy();
2179
2180 #if USE_POSTGRESQL
2181         if (name == "postgresql") {
2182                 std::string connect_string;
2183                 conf.getNoEx("pgsql_player_connection", connect_string);
2184                 return new PlayerDatabasePostgreSQL(connect_string);
2185         }
2186 #endif
2187
2188 #if USE_LEVELDB
2189         if (name == "leveldb")
2190                 return new PlayerDatabaseLevelDB(savedir);
2191 #endif
2192
2193         if (name == "files")
2194                 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2195
2196         throw BaseException(std::string("Database backend ") + name + " not supported.");
2197 }
2198
2199 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2200                 const Settings &cmd_args)
2201 {
2202         std::string migrate_to = cmd_args.get("migrate-players");
2203         Settings world_mt;
2204         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2205         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2206                 errorstream << "Cannot read world.mt!" << std::endl;
2207                 return false;
2208         }
2209
2210         if (!world_mt.exists("player_backend")) {
2211                 errorstream << "Please specify your current backend in world.mt:"
2212                         << std::endl
2213                         << "    player_backend = {files|sqlite3|leveldb|postgresql}"
2214                         << std::endl;
2215                 return false;
2216         }
2217
2218         std::string backend = world_mt.get("player_backend");
2219         if (backend == migrate_to) {
2220                 errorstream << "Cannot migrate: new backend is same"
2221                         << " as the old one" << std::endl;
2222                 return false;
2223         }
2224
2225         const std::string players_backup_path = game_params.world_path + DIR_DELIM
2226                 + "players.bak";
2227
2228         if (backend == "files") {
2229                 // Create backup directory
2230                 fs::CreateDir(players_backup_path);
2231         }
2232
2233         try {
2234                 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2235                         game_params.world_path, world_mt);
2236                 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2237                         game_params.world_path, world_mt);
2238
2239                 std::vector<std::string> player_list;
2240                 srcdb->listPlayers(player_list);
2241                 for (std::vector<std::string>::const_iterator it = player_list.begin();
2242                         it != player_list.end(); ++it) {
2243                         actionstream << "Migrating player " << it->c_str() << std::endl;
2244                         RemotePlayer player(it->c_str(), NULL);
2245                         PlayerSAO playerSAO(NULL, &player, 15000, false);
2246
2247                         srcdb->loadPlayer(&player, &playerSAO);
2248
2249                         playerSAO.finalize(&player, std::set<std::string>());
2250                         player.setPlayerSAO(&playerSAO);
2251
2252                         dstdb->savePlayer(&player);
2253
2254                         // For files source, move player files to backup dir
2255                         if (backend == "files") {
2256                                 fs::Rename(
2257                                         game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2258                                         players_backup_path + DIR_DELIM + (*it));
2259                         }
2260                 }
2261
2262                 actionstream << "Successfully migrated " << player_list.size() << " players"
2263                         << std::endl;
2264                 world_mt.set("player_backend", migrate_to);
2265                 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2266                         errorstream << "Failed to update world.mt!" << std::endl;
2267                 else
2268                         actionstream << "world.mt updated" << std::endl;
2269
2270                 // When migration is finished from file backend, remove players directory if empty
2271                 if (backend == "files") {
2272                         fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2273                                 + "players");
2274                 }
2275
2276                 delete srcdb;
2277                 delete dstdb;
2278
2279         } catch (BaseException &e) {
2280                 errorstream << "An error occurred during migration: " << e.what() << std::endl;
2281                 return false;
2282         }
2283         return true;
2284 }
2285
2286 AuthDatabase *ServerEnvironment::openAuthDatabase(
2287                 const std::string &name, const std::string &savedir, const Settings &conf)
2288 {
2289         if (name == "sqlite3")
2290                 return new AuthDatabaseSQLite3(savedir);
2291
2292 #if USE_POSTGRESQL
2293         if (name == "postgresql") {
2294                 std::string connect_string;
2295                 conf.getNoEx("pgsql_auth_connection", connect_string);
2296                 return new AuthDatabasePostgreSQL(connect_string);
2297         }
2298 #endif
2299
2300         if (name == "files")
2301                 return new AuthDatabaseFiles(savedir);
2302
2303 #if USE_LEVELDB
2304         if (name == "leveldb")
2305                 return new AuthDatabaseLevelDB(savedir);
2306 #endif
2307
2308         throw BaseException(std::string("Database backend ") + name + " not supported.");
2309 }
2310
2311 bool ServerEnvironment::migrateAuthDatabase(
2312                 const GameParams &game_params, const Settings &cmd_args)
2313 {
2314         std::string migrate_to = cmd_args.get("migrate-auth");
2315         Settings world_mt;
2316         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2317         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2318                 errorstream << "Cannot read world.mt!" << std::endl;
2319                 return false;
2320         }
2321
2322         std::string backend = "files";
2323         if (world_mt.exists("auth_backend"))
2324                 backend = world_mt.get("auth_backend");
2325         else
2326                 warningstream << "No auth_backend found in world.mt, "
2327                                 "assuming \"files\"." << std::endl;
2328
2329         if (backend == migrate_to) {
2330                 errorstream << "Cannot migrate: new backend is same"
2331                                 << " as the old one" << std::endl;
2332                 return false;
2333         }
2334
2335         try {
2336                 const std::unique_ptr<AuthDatabase> srcdb(ServerEnvironment::openAuthDatabase(
2337                                 backend, game_params.world_path, world_mt));
2338                 const std::unique_ptr<AuthDatabase> dstdb(ServerEnvironment::openAuthDatabase(
2339                                 migrate_to, game_params.world_path, world_mt));
2340
2341                 std::vector<std::string> names_list;
2342                 srcdb->listNames(names_list);
2343                 for (const std::string &name : names_list) {
2344                         actionstream << "Migrating auth entry for " << name << std::endl;
2345                         bool success;
2346                         AuthEntry authEntry;
2347                         success = srcdb->getAuth(name, authEntry);
2348                         success = success && dstdb->createAuth(authEntry);
2349                         if (!success)
2350                                 errorstream << "Failed to migrate " << name << std::endl;
2351                 }
2352
2353                 actionstream << "Successfully migrated " << names_list.size()
2354                                 << " auth entries" << std::endl;
2355                 world_mt.set("auth_backend", migrate_to);
2356                 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2357                         errorstream << "Failed to update world.mt!" << std::endl;
2358                 else
2359                         actionstream << "world.mt updated" << std::endl;
2360
2361                 if (backend == "files") {
2362                         // special-case files migration:
2363                         // move auth.txt to auth.txt.bak if possible
2364                         std::string auth_txt_path =
2365                                         game_params.world_path + DIR_DELIM + "auth.txt";
2366                         std::string auth_bak_path = auth_txt_path + ".bak";
2367                         if (!fs::PathExists(auth_bak_path))
2368                                 if (fs::Rename(auth_txt_path, auth_bak_path))
2369                                         actionstream << "Renamed auth.txt to auth.txt.bak"
2370                                                         << std::endl;
2371                                 else
2372                                         errorstream << "Could not rename auth.txt to "
2373                                                         "auth.txt.bak" << std::endl;
2374                         else
2375                                 warningstream << "auth.txt.bak already exists, auth.txt "
2376                                                 "not renamed" << std::endl;
2377                 }
2378
2379         } catch (BaseException &e) {
2380                 errorstream << "An error occurred during migration: " << e.what()
2381                             << std::endl;
2382                 return false;
2383         }
2384         return true;
2385 }