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