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