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