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