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