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