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