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