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