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