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