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