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