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