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