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