]> git.lizzy.rs Git - minetest.git/blob - src/serverenvironment.cpp
27432e9736544330de7e674384375fa730f66224
[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         std::string path = m_path_world + DIR_DELIM "env_meta.txt";
628
629         // Open file and serialize
630         std::ostringstream ss(std::ios_base::binary);
631
632         Settings args;
633         args.setU64("game_time", m_game_time);
634         args.setU64("time_of_day", getTimeOfDay());
635         args.setU64("last_clear_objects_time", m_last_clear_objects_time);
636         args.setU64("lbm_introduction_times_version", 1);
637         args.set("lbm_introduction_times",
638                 m_lbm_mgr.createIntroductionTimesString());
639         args.setU64("day_count", m_day_count);
640         args.writeLines(ss);
641         ss<<"EnvArgsEnd\n";
642
643         if(!fs::safeWriteToFile(path, ss.str()))
644         {
645                 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
646                         <<path<<std::endl;
647                 throw SerializationError("Couldn't save env meta");
648         }
649 }
650
651 void ServerEnvironment::loadMeta()
652 {
653         // If file doesn't exist, load default environment metadata
654         if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
655                 infostream << "ServerEnvironment: Loading default environment metadata"
656                         << std::endl;
657                 loadDefaultMeta();
658                 return;
659         }
660
661         infostream << "ServerEnvironment: Loading environment metadata" << std::endl;
662
663         std::string path = m_path_world + DIR_DELIM "env_meta.txt";
664
665         // Open file and deserialize
666         std::ifstream is(path.c_str(), std::ios_base::binary);
667         if (!is.good()) {
668                 infostream << "ServerEnvironment::loadMeta(): Failed to open "
669                         << path << std::endl;
670                 throw SerializationError("Couldn't load env meta");
671         }
672
673         Settings args;
674
675         if (!args.parseConfigLines(is, "EnvArgsEnd")) {
676                 throw SerializationError("ServerEnvironment::loadMeta(): "
677                         "EnvArgsEnd not found!");
678         }
679
680         try {
681                 m_game_time = args.getU64("game_time");
682         } catch (SettingNotFoundException &e) {
683                 // Getting this is crucial, otherwise timestamps are useless
684                 throw SerializationError("Couldn't load env meta game_time");
685         }
686
687         setTimeOfDay(args.exists("time_of_day") ?
688                 // set day to early morning by default
689                 args.getU64("time_of_day") : 5250);
690
691         m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
692                 // If missing, do as if clearObjects was never called
693                 args.getU64("last_clear_objects_time") : 0;
694
695         std::string lbm_introduction_times;
696         try {
697                 u64 ver = args.getU64("lbm_introduction_times_version");
698                 if (ver == 1) {
699                         lbm_introduction_times = args.get("lbm_introduction_times");
700                 } else {
701                         infostream << "ServerEnvironment::loadMeta(): Non-supported"
702                                 << " introduction time version " << ver << std::endl;
703                 }
704         } catch (SettingNotFoundException &e) {
705                 // No problem, this is expected. Just continue with an empty string
706         }
707         m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
708
709         m_day_count = args.exists("day_count") ?
710                 args.getU64("day_count") : 0;
711 }
712
713 /**
714  * called if env_meta.txt doesn't exist (e.g. new world)
715  */
716 void ServerEnvironment::loadDefaultMeta()
717 {
718         m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
719 }
720
721 struct ActiveABM
722 {
723         ActiveBlockModifier *abm;
724         int chance;
725         std::vector<content_t> required_neighbors;
726         bool check_required_neighbors; // false if required_neighbors is known to be empty
727 };
728
729 class ABMHandler
730 {
731 private:
732         ServerEnvironment *m_env;
733         std::vector<std::vector<ActiveABM> *> m_aabms;
734 public:
735         ABMHandler(std::vector<ABMWithState> &abms,
736                 float dtime_s, ServerEnvironment *env,
737                 bool use_timers):
738                 m_env(env)
739         {
740                 if(dtime_s < 0.001)
741                         return;
742                 const NodeDefManager *ndef = env->getGameDef()->ndef();
743                 for (ABMWithState &abmws : abms) {
744                         ActiveBlockModifier *abm = abmws.abm;
745                         float trigger_interval = abm->getTriggerInterval();
746                         if(trigger_interval < 0.001)
747                                 trigger_interval = 0.001;
748                         float actual_interval = dtime_s;
749                         if(use_timers){
750                                 abmws.timer += dtime_s;
751                                 if(abmws.timer < trigger_interval)
752                                         continue;
753                                 abmws.timer -= trigger_interval;
754                                 actual_interval = trigger_interval;
755                         }
756                         float chance = abm->getTriggerChance();
757                         if(chance == 0)
758                                 chance = 1;
759                         ActiveABM aabm;
760                         aabm.abm = abm;
761                         if (abm->getSimpleCatchUp()) {
762                                 float intervals = actual_interval / trigger_interval;
763                                 if(intervals == 0)
764                                         continue;
765                                 aabm.chance = chance / intervals;
766                                 if(aabm.chance == 0)
767                                         aabm.chance = 1;
768                         } else {
769                                 aabm.chance = chance;
770                         }
771
772                         // Trigger neighbors
773                         const std::vector<std::string> &required_neighbors_s =
774                                 abm->getRequiredNeighbors();
775                         for (const std::string &required_neighbor_s : required_neighbors_s) {
776                                 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
777                         }
778                         aabm.check_required_neighbors = !required_neighbors_s.empty();
779
780                         // Trigger contents
781                         const std::vector<std::string> &contents_s = abm->getTriggerContents();
782                         for (const std::string &content_s : contents_s) {
783                                 std::vector<content_t> ids;
784                                 ndef->getIds(content_s, ids);
785                                 for (content_t c : ids) {
786                                         if (c >= m_aabms.size())
787                                                 m_aabms.resize(c + 256, NULL);
788                                         if (!m_aabms[c])
789                                                 m_aabms[c] = new std::vector<ActiveABM>;
790                                         m_aabms[c]->push_back(aabm);
791                                 }
792                         }
793                 }
794         }
795
796         ~ABMHandler()
797         {
798                 for (auto &aabms : m_aabms)
799                         delete aabms;
800         }
801
802         // Find out how many objects the given block and its neighbours contain.
803         // Returns the number of objects in the block, and also in 'wider' the
804         // number of objects in the block and all its neighbours. The latter
805         // may an estimate if any neighbours are unloaded.
806         u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
807         {
808                 wider = 0;
809                 u32 wider_unknown_count = 0;
810                 for(s16 x=-1; x<=1; x++)
811                         for(s16 y=-1; y<=1; y++)
812                                 for(s16 z=-1; z<=1; z++)
813                                 {
814                                         MapBlock *block2 = map->getBlockNoCreateNoEx(
815                                                 block->getPos() + v3s16(x,y,z));
816                                         if(block2==NULL){
817                                                 wider_unknown_count++;
818                                                 continue;
819                                         }
820                                         wider += block2->m_static_objects.m_active.size()
821                                                 + block2->m_static_objects.m_stored.size();
822                                 }
823                 // Extrapolate
824                 u32 active_object_count = block->m_static_objects.m_active.size();
825                 u32 wider_known_count = 3*3*3 - wider_unknown_count;
826                 wider += wider_unknown_count * wider / wider_known_count;
827                 return active_object_count;
828
829         }
830         void apply(MapBlock *block, int &blocks_scanned, int &abms_run, int &blocks_cached)
831         {
832                 if(m_aabms.empty() || block->isDummy())
833                         return;
834
835                 // Check the content type cache first
836                 // to see whether there are any ABMs
837                 // to be run at all for this block.
838                 if (block->contents_cached) {
839                         blocks_cached++;
840                         bool run_abms = false;
841                         for (content_t c : block->contents) {
842                                 if (c < m_aabms.size() && m_aabms[c]) {
843                                         run_abms = true;
844                                         break;
845                                 }
846                         }
847                         if (!run_abms)
848                                 return;
849                 } else {
850                         // Clear any caching
851                         block->contents.clear();
852                 }
853                 blocks_scanned++;
854
855                 ServerMap *map = &m_env->getServerMap();
856
857                 u32 active_object_count_wider;
858                 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
859                 m_env->m_added_objects = 0;
860
861                 v3s16 p0;
862                 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
863                 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
864                 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
865                 {
866                         const MapNode &n = block->getNodeUnsafe(p0);
867                         content_t c = n.getContent();
868                         // Cache content types as we go
869                         if (!block->contents_cached && !block->do_not_cache_contents) {
870                                 block->contents.insert(c);
871                                 if (block->contents.size() > 64) {
872                                         // Too many different nodes... don't try to cache
873                                         block->do_not_cache_contents = true;
874                                         block->contents.clear();
875                                 }
876                         }
877
878                         if (c >= m_aabms.size() || !m_aabms[c])
879                                 continue;
880
881                         v3s16 p = p0 + block->getPosRelative();
882                         for (ActiveABM &aabm : *m_aabms[c]) {
883                                 if (myrand() % aabm.chance != 0)
884                                         continue;
885
886                                 // Check neighbors
887                                 if (aabm.check_required_neighbors) {
888                                         v3s16 p1;
889                                         for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
890                                         for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
891                                         for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
892                                         {
893                                                 if(p1 == p0)
894                                                         continue;
895                                                 content_t c;
896                                                 if (block->isValidPosition(p1)) {
897                                                         // if the neighbor is found on the same map block
898                                                         // get it straight from there
899                                                         const MapNode &n = block->getNodeUnsafe(p1);
900                                                         c = n.getContent();
901                                                 } else {
902                                                         // otherwise consult the map
903                                                         MapNode n = map->getNode(p1 + block->getPosRelative());
904                                                         c = n.getContent();
905                                                 }
906                                                 if (CONTAINS(aabm.required_neighbors, c))
907                                                         goto neighbor_found;
908                                         }
909                                         // No required neighbor found
910                                         continue;
911                                 }
912                                 neighbor_found:
913
914                                 abms_run++;
915                                 // Call all the trigger variations
916                                 aabm.abm->trigger(m_env, p, n);
917                                 aabm.abm->trigger(m_env, p, n,
918                                         active_object_count, active_object_count_wider);
919
920                                 // Count surrounding objects again if the abms added any
921                                 if(m_env->m_added_objects > 0) {
922                                         active_object_count = countObjects(block, map, active_object_count_wider);
923                                         m_env->m_added_objects = 0;
924                                 }
925                         }
926                 }
927                 block->contents_cached = !block->do_not_cache_contents;
928         }
929 };
930
931 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
932 {
933         // Reset usage timer immediately, otherwise a block that becomes active
934         // again at around the same time as it would normally be unloaded will
935         // get unloaded incorrectly. (I think this still leaves a small possibility
936         // of a race condition between this and server::AsyncRunStep, which only
937         // some kind of synchronisation will fix, but it at least reduces the window
938         // of opportunity for it to break from seconds to nanoseconds)
939         block->resetUsageTimer();
940
941         // Get time difference
942         u32 dtime_s = 0;
943         u32 stamp = block->getTimestamp();
944         if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
945                 dtime_s = m_game_time - stamp;
946         dtime_s += additional_dtime;
947
948         /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
949                         <<stamp<<", game time: "<<m_game_time<<std::endl;*/
950
951         // Remove stored static objects if clearObjects was called since block's timestamp
952         if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
953                 block->m_static_objects.m_stored.clear();
954                 // do not set changed flag to avoid unnecessary mapblock writes
955         }
956
957         // Set current time as timestamp
958         block->setTimestampNoChangedFlag(m_game_time);
959
960         /*infostream<<"ServerEnvironment::activateBlock(): block is "
961                         <<dtime_s<<" seconds old."<<std::endl;*/
962
963         // Activate stored objects
964         activateObjects(block, dtime_s);
965
966         /* Handle LoadingBlockModifiers */
967         m_lbm_mgr.applyLBMs(this, block, stamp);
968
969         // Run node timers
970         std::vector<NodeTimer> elapsed_timers =
971                 block->m_node_timers.step((float)dtime_s);
972         if (!elapsed_timers.empty()) {
973                 MapNode n;
974                 for (const NodeTimer &elapsed_timer : elapsed_timers) {
975                         n = block->getNodeNoEx(elapsed_timer.position);
976                         v3s16 p = elapsed_timer.position + block->getPosRelative();
977                         if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
978                                 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
979                                         elapsed_timer.position));
980                 }
981         }
982 }
983
984 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
985 {
986         m_abms.emplace_back(abm);
987 }
988
989 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
990 {
991         m_lbm_mgr.addLBMDef(lbm);
992 }
993
994 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
995 {
996         const NodeDefManager *ndef = m_server->ndef();
997         MapNode n_old = m_map->getNode(p);
998
999         const ContentFeatures &cf_old = ndef->get(n_old);
1000
1001         // Call destructor
1002         if (cf_old.has_on_destruct)
1003                 m_script->node_on_destruct(p, n_old);
1004
1005         // Replace node
1006         if (!m_map->addNodeWithEvent(p, n))
1007                 return false;
1008
1009         // Update active VoxelManipulator if a mapgen thread
1010         m_map->updateVManip(p);
1011
1012         // Call post-destructor
1013         if (cf_old.has_after_destruct)
1014                 m_script->node_after_destruct(p, n_old);
1015
1016         // Retrieve node content features
1017         // if new node is same as old, reuse old definition to prevent a lookup
1018         const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
1019
1020         // Call constructor
1021         if (cf_new.has_on_construct)
1022                 m_script->node_on_construct(p, n);
1023
1024         return true;
1025 }
1026
1027 bool ServerEnvironment::removeNode(v3s16 p)
1028 {
1029         const NodeDefManager *ndef = m_server->ndef();
1030         MapNode n_old = m_map->getNode(p);
1031
1032         // Call destructor
1033         if (ndef->get(n_old).has_on_destruct)
1034                 m_script->node_on_destruct(p, n_old);
1035
1036         // Replace with air
1037         // This is slightly optimized compared to addNodeWithEvent(air)
1038         if (!m_map->removeNodeWithEvent(p))
1039                 return false;
1040
1041         // Update active VoxelManipulator if a mapgen thread
1042         m_map->updateVManip(p);
1043
1044         // Call post-destructor
1045         if (ndef->get(n_old).has_after_destruct)
1046                 m_script->node_after_destruct(p, n_old);
1047
1048         // Air doesn't require constructor
1049         return true;
1050 }
1051
1052 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1053 {
1054         if (!m_map->addNodeWithEvent(p, n, false))
1055                 return false;
1056
1057         // Update active VoxelManipulator if a mapgen thread
1058         m_map->updateVManip(p);
1059
1060         return true;
1061 }
1062
1063 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1064 {
1065         infostream << "ServerEnvironment::clearObjects(): "
1066                 << "Removing all active objects" << std::endl;
1067         auto cb_removal = [this] (ServerActiveObject *obj, u16 id) {
1068                 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1069                         return false;
1070
1071                 // Delete static object if block is loaded
1072                 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1073
1074                 // If known by some client, don't delete immediately
1075                 if (obj->m_known_by_count > 0) {
1076                         obj->m_pending_removal = true;
1077                         return false;
1078                 }
1079
1080                 // Tell the object about removal
1081                 obj->removingFromEnvironment();
1082                 // Deregister in scripting api
1083                 m_script->removeObjectReference(obj);
1084
1085                 // Delete active object
1086                 if (obj->environmentDeletes())
1087                         delete obj;
1088
1089                 return true;
1090         };
1091
1092         m_ao_manager.clear(cb_removal);
1093
1094         // Get list of loaded blocks
1095         std::vector<v3s16> loaded_blocks;
1096         infostream << "ServerEnvironment::clearObjects(): "
1097                 << "Listing all loaded blocks" << std::endl;
1098         m_map->listAllLoadedBlocks(loaded_blocks);
1099         infostream << "ServerEnvironment::clearObjects(): "
1100                 << "Done listing all loaded blocks: "
1101                 << loaded_blocks.size()<<std::endl;
1102
1103         // Get list of loadable blocks
1104         std::vector<v3s16> loadable_blocks;
1105         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1106                 infostream << "ServerEnvironment::clearObjects(): "
1107                         << "Listing all loadable blocks" << std::endl;
1108                 m_map->listAllLoadableBlocks(loadable_blocks);
1109                 infostream << "ServerEnvironment::clearObjects(): "
1110                         << "Done listing all loadable blocks: "
1111                         << loadable_blocks.size() << std::endl;
1112         } else {
1113                 loadable_blocks = loaded_blocks;
1114         }
1115
1116         actionstream << "ServerEnvironment::clearObjects(): "
1117                 << "Now clearing objects in " << loadable_blocks.size()
1118                 << " blocks" << std::endl;
1119
1120         // Grab a reference on each loaded block to avoid unloading it
1121         for (v3s16 p : loaded_blocks) {
1122                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1123                 assert(block != NULL);
1124                 block->refGrab();
1125         }
1126
1127         // Remove objects in all loadable blocks
1128         u32 unload_interval = U32_MAX;
1129         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1130                 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1131                 unload_interval = MYMAX(unload_interval, 1);
1132         }
1133         u32 report_interval = loadable_blocks.size() / 10;
1134         u32 num_blocks_checked = 0;
1135         u32 num_blocks_cleared = 0;
1136         u32 num_objs_cleared = 0;
1137         for (auto i = loadable_blocks.begin();
1138                 i != loadable_blocks.end(); ++i) {
1139                 v3s16 p = *i;
1140                 MapBlock *block = m_map->emergeBlock(p, false);
1141                 if (!block) {
1142                         errorstream << "ServerEnvironment::clearObjects(): "
1143                                 << "Failed to emerge block " << PP(p) << std::endl;
1144                         continue;
1145                 }
1146                 u32 num_stored = block->m_static_objects.m_stored.size();
1147                 u32 num_active = block->m_static_objects.m_active.size();
1148                 if (num_stored != 0 || num_active != 0) {
1149                         block->m_static_objects.m_stored.clear();
1150                         block->m_static_objects.m_active.clear();
1151                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1152                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1153                         num_objs_cleared += num_stored + num_active;
1154                         num_blocks_cleared++;
1155                 }
1156                 num_blocks_checked++;
1157
1158                 if (report_interval != 0 &&
1159                         num_blocks_checked % report_interval == 0) {
1160                         float percent = 100.0 * (float)num_blocks_checked /
1161                                 loadable_blocks.size();
1162                         actionstream << "ServerEnvironment::clearObjects(): "
1163                                 << "Cleared " << num_objs_cleared << " objects"
1164                                 << " in " << num_blocks_cleared << " blocks ("
1165                                 << percent << "%)" << std::endl;
1166                 }
1167                 if (num_blocks_checked % unload_interval == 0) {
1168                         m_map->unloadUnreferencedBlocks();
1169                 }
1170         }
1171         m_map->unloadUnreferencedBlocks();
1172
1173         // Drop references that were added above
1174         for (v3s16 p : loaded_blocks) {
1175                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1176                 assert(block);
1177                 block->refDrop();
1178         }
1179
1180         m_last_clear_objects_time = m_game_time;
1181
1182         actionstream << "ServerEnvironment::clearObjects(): "
1183                 << "Finished: Cleared " << num_objs_cleared << " objects"
1184                 << " in " << num_blocks_cleared << " blocks" << std::endl;
1185 }
1186
1187 void ServerEnvironment::step(float dtime)
1188 {
1189         ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
1190         /* Step time of day */
1191         stepTimeOfDay(dtime);
1192
1193         // Update this one
1194         // NOTE: This is kind of funny on a singleplayer game, but doesn't
1195         // really matter that much.
1196         static thread_local const float server_step =
1197                         g_settings->getFloat("dedicated_server_step");
1198         m_recommended_send_interval = server_step;
1199
1200         /*
1201                 Increment game time
1202         */
1203         {
1204                 m_game_time_fraction_counter += dtime;
1205                 u32 inc_i = (u32)m_game_time_fraction_counter;
1206                 m_game_time += inc_i;
1207                 m_game_time_fraction_counter -= (float)inc_i;
1208         }
1209
1210         /*
1211                 Handle players
1212         */
1213         {
1214                 ScopeProfiler sp(g_profiler, "ServerEnv: move players", SPT_AVG);
1215                 for (RemotePlayer *player : m_players) {
1216                         // Ignore disconnected players
1217                         if (player->getPeerId() == PEER_ID_INEXISTENT)
1218                                 continue;
1219
1220                         // Move
1221                         player->move(dtime, this, 100 * BS);
1222                 }
1223         }
1224
1225         if (m_database_check_interval.step(dtime, 10.0f)) {
1226                 m_auth_database->pingDatabase();
1227                 m_player_database->pingDatabase();
1228                 m_map->pingDatabase();
1229         }
1230         /*
1231                 Manage active block list
1232         */
1233         if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1234                 ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
1235                 /*
1236                         Get player block positions
1237                 */
1238                 std::vector<PlayerSAO*> players;
1239                 for (RemotePlayer *player: m_players) {
1240                         // Ignore disconnected players
1241                         if (player->getPeerId() == PEER_ID_INEXISTENT)
1242                                 continue;
1243
1244                         PlayerSAO *playersao = player->getPlayerSAO();
1245                         assert(playersao);
1246
1247                         players.push_back(playersao);
1248                 }
1249
1250                 /*
1251                         Update list of active blocks, collecting changes
1252                 */
1253                 // use active_object_send_range_blocks since that is max distance
1254                 // for active objects sent the client anyway
1255                 static thread_local const s16 active_object_range =
1256                                 g_settings->getS16("active_object_send_range_blocks");
1257                 static thread_local const s16 active_block_range =
1258                                 g_settings->getS16("active_block_range");
1259                 std::set<v3s16> blocks_removed;
1260                 std::set<v3s16> blocks_added;
1261                 m_active_blocks.update(players, active_block_range, active_object_range,
1262                         blocks_removed, blocks_added);
1263
1264                 /*
1265                         Handle removed blocks
1266                 */
1267
1268                 // Convert active objects that are no more in active blocks to static
1269                 deactivateFarObjects(false);
1270
1271                 for (const v3s16 &p: blocks_removed) {
1272                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1273                         if (!block)
1274                                 continue;
1275
1276                         // Set current time as timestamp (and let it set ChangedFlag)
1277                         block->setTimestamp(m_game_time);
1278                 }
1279
1280                 /*
1281                         Handle added blocks
1282                 */
1283
1284                 for (const v3s16 &p: blocks_added) {
1285                         MapBlock *block = m_map->getBlockOrEmerge(p);
1286                         if (!block) {
1287                                 m_active_blocks.m_list.erase(p);
1288                                 m_active_blocks.m_abm_list.erase(p);
1289                                 continue;
1290                         }
1291
1292                         activateBlock(block);
1293                 }
1294         }
1295
1296         /*
1297                 Mess around in active blocks
1298         */
1299         if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1300                 ScopeProfiler sp(g_profiler, "ServerEnv: Run node timers", SPT_AVG);
1301
1302                 float dtime = m_cache_nodetimer_interval;
1303
1304                 for (const v3s16 &p: m_active_blocks.m_list) {
1305                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1306                         if (!block)
1307                                 continue;
1308
1309                         // Reset block usage timer
1310                         block->resetUsageTimer();
1311
1312                         // Set current time as timestamp
1313                         block->setTimestampNoChangedFlag(m_game_time);
1314                         // If time has changed much from the one on disk,
1315                         // set block to be saved when it is unloaded
1316                         if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1317                                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1318                                         MOD_REASON_BLOCK_EXPIRED);
1319
1320                         // Run node timers
1321                         std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1322                         if (!elapsed_timers.empty()) {
1323                                 MapNode n;
1324                                 v3s16 p2;
1325                                 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1326                                         n = block->getNodeNoEx(elapsed_timer.position);
1327                                         p2 = elapsed_timer.position + block->getPosRelative();
1328                                         if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1329                                                 block->setNodeTimer(NodeTimer(
1330                                                         elapsed_timer.timeout, 0, elapsed_timer.position));
1331                                         }
1332                                 }
1333                         }
1334                 }
1335         }
1336
1337         if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) {
1338                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1339                 TimeTaker timer("modify in active blocks per interval");
1340
1341                 // Initialize handling of ActiveBlockModifiers
1342                 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1343
1344                 int blocks_scanned = 0;
1345                 int abms_run = 0;
1346                 int blocks_cached = 0;
1347
1348                 std::vector<v3s16> output(m_active_blocks.m_abm_list.size());
1349
1350                 // Shuffle the active blocks so that each block gets an equal chance
1351                 // of having its ABMs run.
1352                 std::copy(m_active_blocks.m_abm_list.begin(), m_active_blocks.m_abm_list.end(), output.begin());
1353                 std::shuffle(output.begin(), output.end(), m_rgen);
1354
1355                 int i = 0;
1356                 // The time budget for ABMs is 20%.
1357                 u32 max_time_ms = m_cache_abm_interval * 1000 / 5;
1358                 for (const v3s16 &p : output) {
1359                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1360                         if (!block)
1361                                 continue;
1362
1363                         i++;
1364
1365                         // Set current time as timestamp
1366                         block->setTimestampNoChangedFlag(m_game_time);
1367
1368                         /* Handle ActiveBlockModifiers */
1369                         abmhandler.apply(block, blocks_scanned, abms_run, blocks_cached);
1370
1371                         u32 time_ms = timer.getTimerTime();
1372
1373                         if (time_ms > max_time_ms) {
1374                                 warningstream << "active block modifiers took "
1375                                           << time_ms << "ms (processed " << i << " of "
1376                                           << output.size() << " active blocks)" << std::endl;
1377                                 break;
1378                         }
1379                 }
1380                 g_profiler->avg("ServerEnv: active blocks", m_active_blocks.m_abm_list.size());
1381                 g_profiler->avg("ServerEnv: active blocks cached", blocks_cached);
1382                 g_profiler->avg("ServerEnv: active blocks scanned for ABMs", blocks_scanned);
1383                 g_profiler->avg("ServerEnv: ABMs run", abms_run);
1384
1385                 timer.stop(true);
1386         }
1387
1388         /*
1389                 Step script environment (run global on_step())
1390         */
1391         m_script->environment_Step(dtime);
1392
1393         /*
1394                 Step active objects
1395         */
1396         {
1397                 ScopeProfiler sp(g_profiler, "ServerEnv: Run SAO::step()", SPT_AVG);
1398
1399                 // This helps the objects to send data at the same time
1400                 bool send_recommended = false;
1401                 m_send_recommended_timer += dtime;
1402                 if (m_send_recommended_timer > getSendRecommendedInterval()) {
1403                         m_send_recommended_timer -= getSendRecommendedInterval();
1404                         send_recommended = true;
1405                 }
1406
1407                 auto cb_state = [this, dtime, send_recommended] (ServerActiveObject *obj) {
1408                         if (obj->isGone())
1409                                 return;
1410
1411                         // Step object
1412                         obj->step(dtime, send_recommended);
1413                         // Read messages from object
1414                         obj->dumpAOMessagesToQueue(m_active_object_messages);
1415                 };
1416                 m_ao_manager.step(dtime, cb_state);
1417         }
1418
1419         /*
1420                 Manage active objects
1421         */
1422         if (m_object_management_interval.step(dtime, 0.5)) {
1423                 removeRemovedObjects();
1424         }
1425
1426         /*
1427                 Manage particle spawner expiration
1428         */
1429         if (m_particle_management_interval.step(dtime, 1.0)) {
1430                 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1431                         i != m_particle_spawners.end(); ) {
1432                         //non expiring spawners
1433                         if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1434                                 ++i;
1435                                 continue;
1436                         }
1437
1438                         i->second -= 1.0f;
1439                         if (i->second <= 0.f)
1440                                 m_particle_spawners.erase(i++);
1441                         else
1442                                 ++i;
1443                 }
1444         }
1445
1446         // Send outdated player inventories
1447         for (RemotePlayer *player : m_players) {
1448                 if (player->getPeerId() == PEER_ID_INEXISTENT)
1449                         continue;
1450
1451                 PlayerSAO *sao = player->getPlayerSAO();
1452                 if (sao && player->inventory.checkModified())
1453                         m_server->SendInventory(sao, true);
1454         }
1455
1456         // Send outdated detached inventories
1457         m_server->sendDetachedInventories(PEER_ID_INEXISTENT, true);
1458 }
1459
1460 u32 ServerEnvironment::addParticleSpawner(float exptime)
1461 {
1462         // Timers with lifetime 0 do not expire
1463         float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1464
1465         u32 id = 0;
1466         for (;;) { // look for unused particlespawner id
1467                 id++;
1468                 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1469                 if (f == m_particle_spawners.end()) {
1470                         m_particle_spawners[id] = time;
1471                         break;
1472                 }
1473         }
1474         return id;
1475 }
1476
1477 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1478 {
1479         u32 id = addParticleSpawner(exptime);
1480         m_particle_spawner_attachments[id] = attached_id;
1481         if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1482                 obj->attachParticleSpawner(id);
1483         }
1484         return id;
1485 }
1486
1487 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1488 {
1489         m_particle_spawners.erase(id);
1490         const auto &it = m_particle_spawner_attachments.find(id);
1491         if (it != m_particle_spawner_attachments.end()) {
1492                 u16 obj_id = it->second;
1493                 ServerActiveObject *sao = getActiveObject(obj_id);
1494                 if (sao != NULL && remove_from_object) {
1495                         sao->detachParticleSpawner(id);
1496                 }
1497                 m_particle_spawner_attachments.erase(id);
1498         }
1499 }
1500
1501 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1502 {
1503         assert(object); // Pre-condition
1504         m_added_objects++;
1505         u16 id = addActiveObjectRaw(object, true, 0);
1506         return id;
1507 }
1508
1509 /*
1510         Finds out what new objects have been added to
1511         inside a radius around a position
1512 */
1513 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1514         s16 player_radius,
1515         std::set<u16> &current_objects,
1516         std::queue<u16> &added_objects)
1517 {
1518         f32 radius_f = radius * BS;
1519         f32 player_radius_f = player_radius * BS;
1520
1521         if (player_radius_f < 0.0f)
1522                 player_radius_f = 0.0f;
1523
1524         m_ao_manager.getAddedActiveObjectsAroundPos(playersao->getBasePosition(), radius_f,
1525                 player_radius_f, current_objects, added_objects);
1526 }
1527
1528 /*
1529         Finds out what objects have been removed from
1530         inside a radius around a position
1531 */
1532 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1533         s16 player_radius,
1534         std::set<u16> &current_objects,
1535         std::queue<u16> &removed_objects)
1536 {
1537         f32 radius_f = radius * BS;
1538         f32 player_radius_f = player_radius * BS;
1539
1540         if (player_radius_f < 0)
1541                 player_radius_f = 0;
1542         /*
1543                 Go through current_objects; object is removed if:
1544                 - object is not found in m_active_objects (this is actually an
1545                   error condition; objects should be removed only after all clients
1546                   have been informed about removal), or
1547                 - object is to be removed or deactivated, or
1548                 - object is too far away
1549         */
1550         for (u16 id : current_objects) {
1551                 ServerActiveObject *object = getActiveObject(id);
1552
1553                 if (object == NULL) {
1554                         infostream << "ServerEnvironment::getRemovedActiveObjects():"
1555                                 << " object in current_objects is NULL" << std::endl;
1556                         removed_objects.push(id);
1557                         continue;
1558                 }
1559
1560                 if (object->isGone()) {
1561                         removed_objects.push(id);
1562                         continue;
1563                 }
1564
1565                 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1566                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1567                         if (distance_f <= player_radius_f || player_radius_f == 0)
1568                                 continue;
1569                 } else if (distance_f <= radius_f)
1570                         continue;
1571
1572                 // Object is no longer visible
1573                 removed_objects.push(id);
1574         }
1575 }
1576
1577 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1578         v3s16 blockpos, bool static_exists, v3s16 static_block)
1579 {
1580         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1581         if (!block)
1582                 return;
1583
1584         for (auto &so_it : block->m_static_objects.m_active) {
1585                 // Get the ServerActiveObject counterpart to this StaticObject
1586                 ServerActiveObject *sao = m_ao_manager.getActiveObject(so_it.first);
1587                 if (!sao) {
1588                         // If this ever happens, there must be some kind of nasty bug.
1589                         errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1590                                 "Object from MapBlock::m_static_objects::m_active not found "
1591                                 "in m_active_objects";
1592                         continue;
1593                 }
1594
1595                 sao->m_static_exists = static_exists;
1596                 sao->m_static_block  = static_block;
1597         }
1598 }
1599
1600 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1601 {
1602         if(m_active_object_messages.empty())
1603                 return ActiveObjectMessage(0);
1604
1605         ActiveObjectMessage message = m_active_object_messages.front();
1606         m_active_object_messages.pop();
1607         return message;
1608 }
1609
1610 void ServerEnvironment::getSelectedActiveObjects(
1611         const core::line3d<f32> &shootline_on_map,
1612         std::vector<PointedThing> &objects)
1613 {
1614         std::vector<ServerActiveObject *> objs;
1615         getObjectsInsideRadius(objs, shootline_on_map.start,
1616                 shootline_on_map.getLength() + 10.0f, nullptr);
1617         const v3f line_vector = shootline_on_map.getVector();
1618
1619         for (auto obj : objs) {
1620                 aabb3f selection_box;
1621                 if (!obj->getSelectionBox(&selection_box))
1622                         continue;
1623
1624                 v3f pos = obj->getBasePosition();
1625
1626                 aabb3f offsetted_box(selection_box.MinEdge + pos,
1627                         selection_box.MaxEdge + pos);
1628
1629                 v3f current_intersection;
1630                 v3s16 current_normal;
1631                 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1632                                 &current_intersection, &current_normal)) {
1633                         objects.emplace_back(
1634                                 (s16) obj->getId(), current_intersection, current_normal,
1635                                 (current_intersection - shootline_on_map.start).getLengthSQ());
1636                 }
1637         }
1638 }
1639
1640 /*
1641         ************ Private methods *************
1642 */
1643
1644 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1645         bool set_changed, u32 dtime_s)
1646 {
1647         if (!m_ao_manager.registerObject(object)) {
1648                 return 0;
1649         }
1650
1651         // Register reference in scripting api (must be done before post-init)
1652         m_script->addObjectReference(object);
1653         // Post-initialize object
1654         object->addedToEnvironment(dtime_s);
1655
1656         // Add static data to block
1657         if (object->isStaticAllowed()) {
1658                 // Add static object to active static list of the block
1659                 v3f objectpos = object->getBasePosition();
1660                 StaticObject s_obj(object, objectpos);
1661                 // Add to the block where the object is located in
1662                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1663                 MapBlock *block = m_map->emergeBlock(blockpos);
1664                 if(block){
1665                         block->m_static_objects.m_active[object->getId()] = s_obj;
1666                         object->m_static_exists = true;
1667                         object->m_static_block = blockpos;
1668
1669                         if(set_changed)
1670                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1671                                         MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1672                 } else {
1673                         v3s16 p = floatToInt(objectpos, BS);
1674                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1675                                 <<"could not emerge block for storing id="<<object->getId()
1676                                 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1677                 }
1678         }
1679
1680         return object->getId();
1681 }
1682
1683 /*
1684         Remove objects that satisfy (isGone() && m_known_by_count==0)
1685 */
1686 void ServerEnvironment::removeRemovedObjects()
1687 {
1688         ScopeProfiler sp(g_profiler, "ServerEnvironment::removeRemovedObjects()", SPT_AVG);
1689
1690         auto clear_cb = [this] (ServerActiveObject *obj, u16 id) {
1691                 // This shouldn't happen but check it
1692                 if (!obj) {
1693                         errorstream << "ServerEnvironment::removeRemovedObjects(): "
1694                                         << "NULL object found. id=" << id << std::endl;
1695                         return true;
1696                 }
1697
1698                 /*
1699                         We will handle objects marked for removal or deactivation
1700                 */
1701                 if (!obj->isGone())
1702                         return false;
1703
1704                 /*
1705                         Delete static data from block if removed
1706                 */
1707                 if (obj->m_pending_removal)
1708                         deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1709
1710                 // If still known by clients, don't actually remove. On some future
1711                 // invocation this will be 0, which is when removal will continue.
1712                 if(obj->m_known_by_count > 0)
1713                         return false;
1714
1715                 /*
1716                         Move static data from active to stored if deactivated
1717                 */
1718                 if (!obj->m_pending_removal && obj->m_static_exists) {
1719                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1720                         if (block) {
1721                                 const auto i = block->m_static_objects.m_active.find(id);
1722                                 if (i != block->m_static_objects.m_active.end()) {
1723                                         block->m_static_objects.m_stored.push_back(i->second);
1724                                         block->m_static_objects.m_active.erase(id);
1725                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1726                                                 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1727                                 } else {
1728                                         warningstream << "ServerEnvironment::removeRemovedObjects(): "
1729                                                         << "id=" << id << " m_static_exists=true but "
1730                                                         << "static data doesn't actually exist in "
1731                                                         << PP(obj->m_static_block) << std::endl;
1732                                 }
1733                         } else {
1734                                 infostream << "Failed to emerge block from which an object to "
1735                                                 << "be deactivated was loaded from. id=" << id << std::endl;
1736                         }
1737                 }
1738
1739                 // Tell the object about removal
1740                 obj->removingFromEnvironment();
1741                 // Deregister in scripting api
1742                 m_script->removeObjectReference(obj);
1743
1744                 // Delete
1745                 if (obj->environmentDeletes())
1746                         delete obj;
1747
1748                 return true;
1749         };
1750
1751         m_ao_manager.clear(clear_cb);
1752 }
1753
1754 static void print_hexdump(std::ostream &o, const std::string &data)
1755 {
1756         const int linelength = 16;
1757         for(int l=0; ; l++){
1758                 int i0 = linelength * l;
1759                 bool at_end = false;
1760                 int thislinelength = linelength;
1761                 if(i0 + thislinelength > (int)data.size()){
1762                         thislinelength = data.size() - i0;
1763                         at_end = true;
1764                 }
1765                 for(int di=0; di<linelength; di++){
1766                         int i = i0 + di;
1767                         char buf[4];
1768                         if(di<thislinelength)
1769                                 porting::mt_snprintf(buf, sizeof(buf), "%.2x ", data[i]);
1770                         else
1771                                 porting::mt_snprintf(buf, sizeof(buf), "   ");
1772                         o<<buf;
1773                 }
1774                 o<<" ";
1775                 for(int di=0; di<thislinelength; di++){
1776                         int i = i0 + di;
1777                         if(data[i] >= 32)
1778                                 o<<data[i];
1779                         else
1780                                 o<<".";
1781                 }
1782                 o<<std::endl;
1783                 if(at_end)
1784                         break;
1785         }
1786 }
1787
1788 ServerActiveObject* ServerEnvironment::createSAO(ActiveObjectType type, v3f pos,
1789                 const std::string &data)
1790 {
1791         switch (type) {
1792                 case ACTIVEOBJECT_TYPE_LUAENTITY:
1793                         return new LuaEntitySAO(this, pos, data);
1794                 default:
1795                         warningstream << "ServerActiveObject: No factory for type=" << type << std::endl;
1796         }
1797         return nullptr;
1798 }
1799
1800 /*
1801         Convert stored objects from blocks near the players to active.
1802 */
1803 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1804 {
1805         if(block == NULL)
1806                 return;
1807
1808         // Ignore if no stored objects (to not set changed flag)
1809         if(block->m_static_objects.m_stored.empty())
1810                 return;
1811
1812         verbosestream<<"ServerEnvironment::activateObjects(): "
1813                 <<"activating objects of block "<<PP(block->getPos())
1814                 <<" ("<<block->m_static_objects.m_stored.size()
1815                 <<" objects)"<<std::endl;
1816         bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1817         if (large_amount) {
1818                 errorstream<<"suspiciously large amount of objects detected: "
1819                         <<block->m_static_objects.m_stored.size()<<" in "
1820                         <<PP(block->getPos())
1821                         <<"; removing all of them."<<std::endl;
1822                 // Clear stored list
1823                 block->m_static_objects.m_stored.clear();
1824                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1825                         MOD_REASON_TOO_MANY_OBJECTS);
1826                 return;
1827         }
1828
1829         // Activate stored objects
1830         std::vector<StaticObject> new_stored;
1831         for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1832                 // Create an active object from the data
1833                 ServerActiveObject *obj = createSAO((ActiveObjectType) s_obj.type, s_obj.pos,
1834                         s_obj.data);
1835                 // If couldn't create object, store static data back.
1836                 if (!obj) {
1837                         errorstream<<"ServerEnvironment::activateObjects(): "
1838                                 <<"failed to create active object from static object "
1839                                 <<"in block "<<PP(s_obj.pos/BS)
1840                                 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1841                         print_hexdump(verbosestream, s_obj.data);
1842
1843                         new_stored.push_back(s_obj);
1844                         continue;
1845                 }
1846                 verbosestream<<"ServerEnvironment::activateObjects(): "
1847                         <<"activated static object pos="<<PP(s_obj.pos/BS)
1848                         <<" type="<<(int)s_obj.type<<std::endl;
1849                 // This will also add the object to the active static list
1850                 addActiveObjectRaw(obj, false, dtime_s);
1851         }
1852
1853         // Clear stored list
1854         block->m_static_objects.m_stored.clear();
1855         // Add leftover failed stuff to stored list
1856         for (const StaticObject &s_obj : new_stored) {
1857                 block->m_static_objects.m_stored.push_back(s_obj);
1858         }
1859
1860         /*
1861                 Note: Block hasn't really been modified here.
1862                 The objects have just been activated and moved from the stored
1863                 static list to the active static list.
1864                 As such, the block is essentially the same.
1865                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1866                 Otherwise there would be a huge amount of unnecessary I/O.
1867         */
1868 }
1869
1870 /*
1871         Convert objects that are not standing inside active blocks to static.
1872
1873         If m_known_by_count != 0, active object is not deleted, but static
1874         data is still updated.
1875
1876         If force_delete is set, active object is deleted nevertheless. It
1877         shall only be set so in the destructor of the environment.
1878
1879         If block wasn't generated (not in memory or on disk),
1880 */
1881 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1882 {
1883         auto cb_deactivate = [this, _force_delete] (ServerActiveObject *obj, u16 id) {
1884                 // force_delete might be overriden per object
1885                 bool force_delete = _force_delete;
1886
1887                 // Do not deactivate if static data creation not allowed
1888                 if (!force_delete && !obj->isStaticAllowed())
1889                         return false;
1890
1891                 // removeRemovedObjects() is responsible for these
1892                 if (!force_delete && obj->isGone())
1893                         return false;
1894
1895                 const v3f &objectpos = obj->getBasePosition();
1896
1897                 // The block in which the object resides in
1898                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1899
1900                 // If object's static data is stored in a deactivated block and object
1901                 // is actually located in an active block, re-save to the block in
1902                 // which the object is actually located in.
1903                 if (!force_delete && obj->m_static_exists &&
1904                    !m_active_blocks.contains(obj->m_static_block) &&
1905                    m_active_blocks.contains(blockpos_o)) {
1906                         // Delete from block where object was located
1907                         deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
1908
1909                         StaticObject s_obj(obj, objectpos);
1910                         // Save to block where object is located
1911                         saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
1912
1913                         return false;
1914                 }
1915
1916                 // If block is still active, don't remove
1917                 if (!force_delete && m_active_blocks.contains(blockpos_o))
1918                         return false;
1919
1920                 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
1921                                           << "deactivating object id=" << id << " on inactive block "
1922                                           << PP(blockpos_o) << std::endl;
1923
1924                 // If known by some client, don't immediately delete.
1925                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1926
1927                 /*
1928                         Update the static data
1929                 */
1930                 if (obj->isStaticAllowed()) {
1931                         // Create new static object
1932                         StaticObject s_obj(obj, objectpos);
1933
1934                         bool stays_in_same_block = false;
1935                         bool data_changed = true;
1936
1937                         // Check if static data has changed considerably
1938                         if (obj->m_static_exists) {
1939                                 if (obj->m_static_block == blockpos_o)
1940                                         stays_in_same_block = true;
1941
1942                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1943
1944                                 if (block) {
1945                                         const auto n = block->m_static_objects.m_active.find(id);
1946                                         if (n != block->m_static_objects.m_active.end()) {
1947                                                 StaticObject static_old = n->second;
1948
1949                                                 float save_movem = obj->getMinimumSavedMovement();
1950
1951                                                 if (static_old.data == s_obj.data &&
1952                                                         (static_old.pos - objectpos).getLength() < save_movem)
1953                                                         data_changed = false;
1954                                         } else {
1955                                                 warningstream << "ServerEnvironment::deactivateFarObjects(): "
1956                                                                 << "id=" << id << " m_static_exists=true but "
1957                                                                 << "static data doesn't actually exist in "
1958                                                                 << PP(obj->m_static_block) << std::endl;
1959                                         }
1960                                 }
1961                         }
1962
1963                         /*
1964                                 While changes are always saved, blocks are only marked as modified
1965                                 if the object has moved or different staticdata. (see above)
1966                         */
1967                         bool shall_be_written = (!stays_in_same_block || data_changed);
1968                         u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
1969
1970                         // Delete old static object
1971                         deleteStaticFromBlock(obj, id, reason, false);
1972
1973                         // Add to the block where the object is located in
1974                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1975                         u16 store_id = pending_delete ? id : 0;
1976                         if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
1977                                 force_delete = true;
1978                 }
1979
1980                 /*
1981                         If known by some client, set pending deactivation.
1982                         Otherwise delete it immediately.
1983                 */
1984                 if (pending_delete && !force_delete) {
1985                         verbosestream << "ServerEnvironment::deactivateFarObjects(): "
1986                                                   << "object id=" << id << " is known by clients"
1987                                                   << "; not deleting yet" << std::endl;
1988
1989                         obj->m_pending_deactivation = true;
1990                         return false;
1991                 }
1992
1993                 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
1994                                           << "object id=" << id << " is not known by clients"
1995                                           << "; deleting" << std::endl;
1996
1997                 // Tell the object about removal
1998                 obj->removingFromEnvironment();
1999                 // Deregister in scripting api
2000                 m_script->removeObjectReference(obj);
2001
2002                 // Delete active object
2003                 if (obj->environmentDeletes())
2004                         delete obj;
2005
2006                 return true;
2007         };
2008
2009         m_ao_manager.clear(cb_deactivate);
2010 }
2011
2012 void ServerEnvironment::deleteStaticFromBlock(
2013                 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2014 {
2015         if (!obj->m_static_exists)
2016                 return;
2017
2018         MapBlock *block;
2019         if (no_emerge)
2020                 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2021         else
2022                 block = m_map->emergeBlock(obj->m_static_block, false);
2023         if (!block) {
2024                 if (!no_emerge)
2025                         errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2026                                         << " when deleting static data of object from it. id=" << id << std::endl;
2027                 return;
2028         }
2029
2030         block->m_static_objects.remove(id);
2031         if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2032                 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2033
2034         obj->m_static_exists = false;
2035 }
2036
2037 bool ServerEnvironment::saveStaticToBlock(
2038                 v3s16 blockpos, u16 store_id,
2039                 ServerActiveObject *obj, const StaticObject &s_obj,
2040                 u32 mod_reason)
2041 {
2042         MapBlock *block = nullptr;
2043         try {
2044                 block = m_map->emergeBlock(blockpos);
2045         } catch (InvalidPositionException &e) {
2046                 // Handled via NULL pointer
2047                 // NOTE: emergeBlock's failure is usually determined by it
2048                 //       actually returning NULL
2049         }
2050
2051         if (!block) {
2052                 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2053                                 << " when saving static data of object to it. id=" << store_id << std::endl;
2054                 return false;
2055         }
2056         if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2057                 warningstream << "ServerEnv: Trying to store id = " << store_id
2058                                 << " statically but block " << PP(blockpos)
2059                                 << " already contains "
2060                                 << block->m_static_objects.m_stored.size()
2061                                 << " objects." << std::endl;
2062                 return false;
2063         }
2064
2065         block->m_static_objects.insert(store_id, s_obj);
2066         if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2067                 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2068
2069         obj->m_static_exists = true;
2070         obj->m_static_block = blockpos;
2071
2072         return true;
2073 }
2074
2075 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2076                 const std::string &savedir, const Settings &conf)
2077 {
2078
2079         if (name == "sqlite3")
2080                 return new PlayerDatabaseSQLite3(savedir);
2081
2082         if (name == "dummy")
2083                 return new Database_Dummy();
2084 #if USE_POSTGRESQL
2085         if (name == "postgresql") {
2086                 std::string connect_string;
2087                 conf.getNoEx("pgsql_player_connection", connect_string);
2088                 return new PlayerDatabasePostgreSQL(connect_string);
2089         }
2090 #endif
2091         if (name == "files")
2092                 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2093
2094         throw BaseException(std::string("Database backend ") + name + " not supported.");
2095 }
2096
2097 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2098                 const Settings &cmd_args)
2099 {
2100         std::string migrate_to = cmd_args.get("migrate-players");
2101         Settings world_mt;
2102         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2103         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2104                 errorstream << "Cannot read world.mt!" << std::endl;
2105                 return false;
2106         }
2107
2108         if (!world_mt.exists("player_backend")) {
2109                 errorstream << "Please specify your current backend in world.mt:"
2110                         << std::endl
2111                         << "    player_backend = {files|sqlite3|postgresql}"
2112                         << std::endl;
2113                 return false;
2114         }
2115
2116         std::string backend = world_mt.get("player_backend");
2117         if (backend == migrate_to) {
2118                 errorstream << "Cannot migrate: new backend is same"
2119                         << " as the old one" << std::endl;
2120                 return false;
2121         }
2122
2123         const std::string players_backup_path = game_params.world_path + DIR_DELIM
2124                 + "players.bak";
2125
2126         if (backend == "files") {
2127                 // Create backup directory
2128                 fs::CreateDir(players_backup_path);
2129         }
2130
2131         try {
2132                 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2133                         game_params.world_path, world_mt);
2134                 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2135                         game_params.world_path, world_mt);
2136
2137                 std::vector<std::string> player_list;
2138                 srcdb->listPlayers(player_list);
2139                 for (std::vector<std::string>::const_iterator it = player_list.begin();
2140                         it != player_list.end(); ++it) {
2141                         actionstream << "Migrating player " << it->c_str() << std::endl;
2142                         RemotePlayer player(it->c_str(), NULL);
2143                         PlayerSAO playerSAO(NULL, &player, 15000, false);
2144
2145                         srcdb->loadPlayer(&player, &playerSAO);
2146
2147                         playerSAO.finalize(&player, std::set<std::string>());
2148                         player.setPlayerSAO(&playerSAO);
2149
2150                         dstdb->savePlayer(&player);
2151
2152                         // For files source, move player files to backup dir
2153                         if (backend == "files") {
2154                                 fs::Rename(
2155                                         game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2156                                         players_backup_path + DIR_DELIM + (*it));
2157                         }
2158                 }
2159
2160                 actionstream << "Successfully migrated " << player_list.size() << " players"
2161                         << std::endl;
2162                 world_mt.set("player_backend", migrate_to);
2163                 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2164                         errorstream << "Failed to update world.mt!" << std::endl;
2165                 else
2166                         actionstream << "world.mt updated" << std::endl;
2167
2168                 // When migration is finished from file backend, remove players directory if empty
2169                 if (backend == "files") {
2170                         fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2171                                 + "players");
2172                 }
2173
2174                 delete srcdb;
2175                 delete dstdb;
2176
2177         } catch (BaseException &e) {
2178                 errorstream << "An error occurred during migration: " << e.what() << std::endl;
2179                 return false;
2180         }
2181         return true;
2182 }
2183
2184 AuthDatabase *ServerEnvironment::openAuthDatabase(
2185                 const std::string &name, const std::string &savedir, const Settings &conf)
2186 {
2187         if (name == "sqlite3")
2188                 return new AuthDatabaseSQLite3(savedir);
2189
2190 #if USE_POSTGRESQL
2191         if (name == "postgresql") {
2192                 std::string connect_string;
2193                 conf.getNoEx("pgsql_auth_connection", connect_string);
2194                 return new AuthDatabasePostgreSQL(connect_string);
2195         }
2196 #endif
2197
2198         if (name == "files")
2199                 return new AuthDatabaseFiles(savedir);
2200
2201 #if USE_LEVELDB
2202         if (name == "leveldb")
2203                 return new AuthDatabaseLevelDB(savedir);
2204 #endif
2205
2206         throw BaseException(std::string("Database backend ") + name + " not supported.");
2207 }
2208
2209 bool ServerEnvironment::migrateAuthDatabase(
2210                 const GameParams &game_params, const Settings &cmd_args)
2211 {
2212         std::string migrate_to = cmd_args.get("migrate-auth");
2213         Settings world_mt;
2214         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2215         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2216                 errorstream << "Cannot read world.mt!" << std::endl;
2217                 return false;
2218         }
2219
2220         std::string backend = "files";
2221         if (world_mt.exists("auth_backend"))
2222                 backend = world_mt.get("auth_backend");
2223         else
2224                 warningstream << "No auth_backend found in world.mt, "
2225                                 "assuming \"files\"." << std::endl;
2226
2227         if (backend == migrate_to) {
2228                 errorstream << "Cannot migrate: new backend is same"
2229                                 << " as the old one" << std::endl;
2230                 return false;
2231         }
2232
2233         try {
2234                 const std::unique_ptr<AuthDatabase> srcdb(ServerEnvironment::openAuthDatabase(
2235                                 backend, game_params.world_path, world_mt));
2236                 const std::unique_ptr<AuthDatabase> dstdb(ServerEnvironment::openAuthDatabase(
2237                                 migrate_to, game_params.world_path, world_mt));
2238
2239                 std::vector<std::string> names_list;
2240                 srcdb->listNames(names_list);
2241                 for (const std::string &name : names_list) {
2242                         actionstream << "Migrating auth entry for " << name << std::endl;
2243                         bool success;
2244                         AuthEntry authEntry;
2245                         success = srcdb->getAuth(name, authEntry);
2246                         success = success && dstdb->createAuth(authEntry);
2247                         if (!success)
2248                                 errorstream << "Failed to migrate " << name << std::endl;
2249                 }
2250
2251                 actionstream << "Successfully migrated " << names_list.size()
2252                                 << " auth entries" << std::endl;
2253                 world_mt.set("auth_backend", migrate_to);
2254                 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2255                         errorstream << "Failed to update world.mt!" << std::endl;
2256                 else
2257                         actionstream << "world.mt updated" << std::endl;
2258
2259                 if (backend == "files") {
2260                         // special-case files migration:
2261                         // move auth.txt to auth.txt.bak if possible
2262                         std::string auth_txt_path =
2263                                         game_params.world_path + DIR_DELIM + "auth.txt";
2264                         std::string auth_bak_path = auth_txt_path + ".bak";
2265                         if (!fs::PathExists(auth_bak_path))
2266                                 if (fs::Rename(auth_txt_path, auth_bak_path))
2267                                         actionstream << "Renamed auth.txt to auth.txt.bak"
2268                                                         << std::endl;
2269                                 else
2270                                         errorstream << "Could not rename auth.txt to "
2271                                                         "auth.txt.bak" << std::endl;
2272                         else
2273                                 warningstream << "auth.txt.bak already exists, auth.txt "
2274                                                 "not renamed" << std::endl;
2275                 }
2276
2277         } catch (BaseException &e) {
2278                 errorstream << "An error occurred during migration: " << e.what()
2279                             << std::endl;
2280                 return false;
2281         }
2282         return true;
2283 }