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