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