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