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