]> git.lizzy.rs Git - dragonfireclient.git/blob - src/serverenvironment.cpp
1bc7c6eb5216de0df973dff4664c6a94ab07a6d6
[dragonfireclient.git] / src / serverenvironment.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "serverenvironment.h"
21 #include "content_sao.h"
22 #include "settings.h"
23 #include "log.h"
24 #include "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 (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1173                         i != m_players.end(); ++i) {
1174                         RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1175                         assert(player);
1176
1177                         // Ignore disconnected players
1178                         if(player->peer_id == 0)
1179                                 continue;
1180
1181                         // Move
1182                         player->move(dtime, this, 100*BS);
1183                 }
1184         }
1185
1186         /*
1187                 Manage active block list
1188         */
1189         if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1190                 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1191                 /*
1192                         Get player block positions
1193                 */
1194                 std::vector<v3s16> players_blockpos;
1195                 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1196                         i != m_players.end(); ++i) {
1197                         RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1198                         assert(player);
1199
1200                         // Ignore disconnected players
1201                         if (player->peer_id == 0)
1202                                 continue;
1203
1204                         PlayerSAO *playersao = player->getPlayerSAO();
1205                         assert(playersao);
1206
1207                         v3s16 blockpos = getNodeBlockPos(
1208                                 floatToInt(playersao->getBasePosition(), BS));
1209                         players_blockpos.push_back(blockpos);
1210                 }
1211
1212                 /*
1213                         Update list of active blocks, collecting changes
1214                 */
1215                 static thread_local const s16 active_block_range =
1216                                 g_settings->getS16("active_block_range");
1217                 std::set<v3s16> blocks_removed;
1218                 std::set<v3s16> blocks_added;
1219                 m_active_blocks.update(players_blockpos, active_block_range,
1220                         blocks_removed, blocks_added);
1221
1222                 /*
1223                         Handle removed blocks
1224                 */
1225
1226                 // Convert active objects that are no more in active blocks to static
1227                 deactivateFarObjects(false);
1228
1229                 for(std::set<v3s16>::iterator
1230                         i = blocks_removed.begin();
1231                         i != blocks_removed.end(); ++i) {
1232                         v3s16 p = *i;
1233
1234                         /* infostream<<"Server: Block " << PP(p)
1235                                 << " became inactive"<<std::endl; */
1236
1237                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1238                         if(block==NULL)
1239                                 continue;
1240
1241                         // Set current time as timestamp (and let it set ChangedFlag)
1242                         block->setTimestamp(m_game_time);
1243                 }
1244
1245                 /*
1246                         Handle added blocks
1247                 */
1248
1249                 for(std::set<v3s16>::iterator
1250                         i = blocks_added.begin();
1251                         i != blocks_added.end(); ++i)
1252                 {
1253                         v3s16 p = *i;
1254
1255                         MapBlock *block = m_map->getBlockOrEmerge(p);
1256                         if(block==NULL){
1257                                 m_active_blocks.m_list.erase(p);
1258                                 continue;
1259                         }
1260
1261                         activateBlock(block);
1262                         /* infostream<<"Server: Block " << PP(p)
1263                                 << " became active"<<std::endl; */
1264                 }
1265         }
1266
1267         /*
1268                 Mess around in active blocks
1269         */
1270         if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1271                 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1272
1273                 float dtime = m_cache_nodetimer_interval;
1274
1275                 for(std::set<v3s16>::iterator
1276                         i = m_active_blocks.m_list.begin();
1277                         i != m_active_blocks.m_list.end(); ++i)
1278                 {
1279                         v3s16 p = *i;
1280
1281                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1282                                         <<") being handled"<<std::endl;*/
1283
1284                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1285                         if(block==NULL)
1286                                 continue;
1287
1288                         // Reset block usage timer
1289                         block->resetUsageTimer();
1290
1291                         // Set current time as timestamp
1292                         block->setTimestampNoChangedFlag(m_game_time);
1293                         // If time has changed much from the one on disk,
1294                         // set block to be saved when it is unloaded
1295                         if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1296                                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1297                                         MOD_REASON_BLOCK_EXPIRED);
1298
1299                         // Run node timers
1300                         std::vector<NodeTimer> elapsed_timers =
1301                                 block->m_node_timers.step((float)dtime);
1302                         if (!elapsed_timers.empty()) {
1303                                 MapNode n;
1304                                 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1305                                         i != elapsed_timers.end(); ++i) {
1306                                         n = block->getNodeNoEx(i->position);
1307                                         p = i->position + block->getPosRelative();
1308                                         if (m_script->node_on_timer(p, n, i->elapsed)) {
1309                                                 block->setNodeTimer(NodeTimer(
1310                                                         i->timeout, 0, i->position));
1311                                         }
1312                                 }
1313                         }
1314                 }
1315         }
1316
1317         if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1318                 do{ // breakable
1319                         if(m_active_block_interval_overload_skip > 0){
1320                                 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1321                                 m_active_block_interval_overload_skip--;
1322                                 break;
1323                         }
1324                         ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1325                         TimeTaker timer("modify in active blocks per interval");
1326
1327                         // Initialize handling of ActiveBlockModifiers
1328                         ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1329
1330                         for(std::set<v3s16>::iterator
1331                                 i = m_active_blocks.m_list.begin();
1332                                 i != m_active_blocks.m_list.end(); ++i)
1333                         {
1334                                 v3s16 p = *i;
1335
1336                                 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1337                                                 <<") being handled"<<std::endl;*/
1338
1339                                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1340                                 if(block == NULL)
1341                                         continue;
1342
1343                                 // Set current time as timestamp
1344                                 block->setTimestampNoChangedFlag(m_game_time);
1345
1346                                 /* Handle ActiveBlockModifiers */
1347                                 abmhandler.apply(block);
1348                         }
1349
1350                         u32 time_ms = timer.stop(true);
1351                         u32 max_time_ms = 200;
1352                         if(time_ms > max_time_ms){
1353                                 warningstream<<"active block modifiers took "
1354                                         <<time_ms<<"ms (longer than "
1355                                         <<max_time_ms<<"ms)"<<std::endl;
1356                                 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1357                         }
1358                 }while(0);
1359
1360         /*
1361                 Step script environment (run global on_step())
1362         */
1363         m_script->environment_Step(dtime);
1364
1365         /*
1366                 Step active objects
1367         */
1368         {
1369                 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1370                 //TimeTaker timer("Step active objects");
1371
1372                 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1373
1374                 // This helps the objects to send data at the same time
1375                 bool send_recommended = false;
1376                 m_send_recommended_timer += dtime;
1377                 if(m_send_recommended_timer > getSendRecommendedInterval())
1378                 {
1379                         m_send_recommended_timer -= getSendRecommendedInterval();
1380                         send_recommended = true;
1381                 }
1382
1383                 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1384                         i != m_active_objects.end(); ++i) {
1385                         ServerActiveObject* obj = i->second;
1386                         // Don't step if is to be removed or stored statically
1387                         if(obj->m_removed || obj->m_pending_deactivation)
1388                                 continue;
1389                         // Step object
1390                         obj->step(dtime, send_recommended);
1391                         // Read messages from object
1392                         while(!obj->m_messages_out.empty())
1393                         {
1394                                 m_active_object_messages.push(
1395                                         obj->m_messages_out.front());
1396                                 obj->m_messages_out.pop();
1397                         }
1398                 }
1399         }
1400
1401         /*
1402                 Manage active objects
1403         */
1404         if(m_object_management_interval.step(dtime, 0.5))
1405         {
1406                 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1407                 /*
1408                         Remove objects that satisfy (m_removed && m_known_by_count==0)
1409                 */
1410                 removeRemovedObjects();
1411         }
1412
1413         /*
1414                 Manage particle spawner expiration
1415         */
1416         if (m_particle_management_interval.step(dtime, 1.0)) {
1417                 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1418                         i != m_particle_spawners.end(); ) {
1419                         //non expiring spawners
1420                         if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1421                                 ++i;
1422                                 continue;
1423                         }
1424
1425                         i->second -= 1.0f;
1426                         if (i->second <= 0.f)
1427                                 m_particle_spawners.erase(i++);
1428                         else
1429                                 ++i;
1430                 }
1431         }
1432 }
1433
1434 u32 ServerEnvironment::addParticleSpawner(float exptime)
1435 {
1436         // Timers with lifetime 0 do not expire
1437         float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1438
1439         u32 id = 0;
1440         for (;;) { // look for unused particlespawner id
1441                 id++;
1442                 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1443                 if (f == m_particle_spawners.end()) {
1444                         m_particle_spawners[id] = time;
1445                         break;
1446                 }
1447         }
1448         return id;
1449 }
1450
1451 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1452 {
1453         u32 id = addParticleSpawner(exptime);
1454         m_particle_spawner_attachments[id] = attached_id;
1455         if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1456                 obj->attachParticleSpawner(id);
1457         }
1458         return id;
1459 }
1460
1461 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1462 {
1463         m_particle_spawners.erase(id);
1464         std::unordered_map<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1465         if (it != m_particle_spawner_attachments.end()) {
1466                 u16 obj_id = (*it).second;
1467                 ServerActiveObject *sao = getActiveObject(obj_id);
1468                 if (sao != NULL && remove_from_object) {
1469                         sao->detachParticleSpawner(id);
1470                 }
1471                 m_particle_spawner_attachments.erase(id);
1472         }
1473 }
1474
1475 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1476 {
1477         ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1478         return (n != m_active_objects.end() ? n->second : NULL);
1479 }
1480
1481 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1482 {
1483         if (id == 0)
1484                 return false;
1485
1486         return objects.find(id) == objects.end();
1487 }
1488
1489 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1490 {
1491         //try to reuse id's as late as possible
1492         static u16 last_used_id = 0;
1493         u16 startid = last_used_id;
1494         for(;;)
1495         {
1496                 last_used_id ++;
1497                 if(isFreeServerActiveObjectId(last_used_id, objects))
1498                         return last_used_id;
1499
1500                 if(last_used_id == startid)
1501                         return 0;
1502         }
1503 }
1504
1505 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1506 {
1507         assert(object); // Pre-condition
1508         m_added_objects++;
1509         u16 id = addActiveObjectRaw(object, true, 0);
1510         return id;
1511 }
1512
1513 /*
1514         Finds out what new objects have been added to
1515         inside a radius around a position
1516 */
1517 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1518         s16 player_radius,
1519         std::set<u16> &current_objects,
1520         std::queue<u16> &added_objects)
1521 {
1522         f32 radius_f = radius * BS;
1523         f32 player_radius_f = player_radius * BS;
1524
1525         if (player_radius_f < 0)
1526                 player_radius_f = 0;
1527         /*
1528                 Go through the object list,
1529                 - discard m_removed objects,
1530                 - discard objects that are too far away,
1531                 - discard objects that are found in current_objects.
1532                 - add remaining objects to added_objects
1533         */
1534         for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1535                 i != m_active_objects.end(); ++i) {
1536                 u16 id = i->first;
1537
1538                 // Get object
1539                 ServerActiveObject *object = i->second;
1540                 if (object == NULL)
1541                         continue;
1542
1543                 // Discard if removed or deactivating
1544                 if(object->m_removed || object->m_pending_deactivation)
1545                         continue;
1546
1547                 f32 distance_f = object->getBasePosition().
1548                         getDistanceFrom(playersao->getBasePosition());
1549                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1550                         // Discard if too far
1551                         if (distance_f > player_radius_f && player_radius_f != 0)
1552                                 continue;
1553                 } else if (distance_f > radius_f)
1554                         continue;
1555
1556                 // Discard if already on current_objects
1557                 std::set<u16>::iterator n;
1558                 n = current_objects.find(id);
1559                 if(n != current_objects.end())
1560                         continue;
1561                 // Add to added_objects
1562                 added_objects.push(id);
1563         }
1564 }
1565
1566 /*
1567         Finds out what objects have been removed from
1568         inside a radius around a position
1569 */
1570 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1571         s16 player_radius,
1572         std::set<u16> &current_objects,
1573         std::queue<u16> &removed_objects)
1574 {
1575         f32 radius_f = radius * BS;
1576         f32 player_radius_f = player_radius * BS;
1577
1578         if (player_radius_f < 0)
1579                 player_radius_f = 0;
1580         /*
1581                 Go through current_objects; object is removed if:
1582                 - object is not found in m_active_objects (this is actually an
1583                   error condition; objects should be set m_removed=true and removed
1584                   only after all clients have been informed about removal), or
1585                 - object has m_removed=true, or
1586                 - object is too far away
1587         */
1588         for(std::set<u16>::iterator
1589                 i = current_objects.begin();
1590                 i != current_objects.end(); ++i)
1591         {
1592                 u16 id = *i;
1593                 ServerActiveObject *object = getActiveObject(id);
1594
1595                 if (object == NULL) {
1596                         infostream << "ServerEnvironment::getRemovedActiveObjects():"
1597                                 << " object in current_objects is NULL" << std::endl;
1598                         removed_objects.push(id);
1599                         continue;
1600                 }
1601
1602                 if (object->m_removed || object->m_pending_deactivation) {
1603                         removed_objects.push(id);
1604                         continue;
1605                 }
1606
1607                 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1608                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1609                         if (distance_f <= player_radius_f || player_radius_f == 0)
1610                                 continue;
1611                 } else if (distance_f <= radius_f)
1612                         continue;
1613
1614                 // Object is no longer visible
1615                 removed_objects.push(id);
1616         }
1617 }
1618
1619 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1620         v3s16 blockpos, bool static_exists, v3s16 static_block)
1621 {
1622         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1623         if (!block)
1624                 return;
1625
1626         for (std::map<u16, StaticObject>::iterator
1627                 so_it = block->m_static_objects.m_active.begin();
1628                 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1629                 // Get the ServerActiveObject counterpart to this StaticObject
1630                 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it->first);
1631                 if (ao_it == m_active_objects.end()) {
1632                         // If this ever happens, there must be some kind of nasty bug.
1633                         errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1634                                 "Object from MapBlock::m_static_objects::m_active not found "
1635                                 "in m_active_objects";
1636                         continue;
1637                 }
1638
1639                 ServerActiveObject *sao = ao_it->second;
1640                 sao->m_static_exists = static_exists;
1641                 sao->m_static_block  = static_block;
1642         }
1643 }
1644
1645 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1646 {
1647         if(m_active_object_messages.empty())
1648                 return ActiveObjectMessage(0);
1649
1650         ActiveObjectMessage message = m_active_object_messages.front();
1651         m_active_object_messages.pop();
1652         return message;
1653 }
1654
1655 void ServerEnvironment::getSelectedActiveObjects(
1656         const core::line3d<f32> &shootline_on_map,
1657         std::vector<PointedThing> &objects)
1658 {
1659         std::vector<u16> objectIds;
1660         getObjectsInsideRadius(objectIds, shootline_on_map.start,
1661                 shootline_on_map.getLength() + 10.0f);
1662         const v3f line_vector = shootline_on_map.getVector();
1663
1664         for (u32 i = 0; i < objectIds.size(); i++) {
1665                 ServerActiveObject* obj = getActiveObject(objectIds[i]);
1666
1667                 aabb3f selection_box;
1668                 if (!obj->getSelectionBox(&selection_box))
1669                         continue;
1670
1671                 v3f pos = obj->getBasePosition();
1672
1673                 aabb3f offsetted_box(selection_box.MinEdge + pos,
1674                         selection_box.MaxEdge + pos);
1675
1676                 v3f current_intersection;
1677                 v3s16 current_normal;
1678                 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1679                                 &current_intersection, &current_normal)) {
1680                         objects.push_back(PointedThing(
1681                                 (s16) objectIds[i], current_intersection, current_normal,
1682                                 (current_intersection - shootline_on_map.start).getLengthSQ()));
1683                 }
1684         }
1685 }
1686
1687 /*
1688         ************ Private methods *************
1689 */
1690
1691 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1692         bool set_changed, u32 dtime_s)
1693 {
1694         assert(object); // Pre-condition
1695         if(object->getId() == 0){
1696                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1697                 if(new_id == 0)
1698                 {
1699                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1700                                 <<"no free ids available"<<std::endl;
1701                         if(object->environmentDeletes())
1702                                 delete object;
1703                         return 0;
1704                 }
1705                 object->setId(new_id);
1706         }
1707         else{
1708                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1709                         <<"supplied with id "<<object->getId()<<std::endl;
1710         }
1711
1712         if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1713                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1714                         <<"id is not free ("<<object->getId()<<")"<<std::endl;
1715                 if(object->environmentDeletes())
1716                         delete object;
1717                 return 0;
1718         }
1719
1720         if (objectpos_over_limit(object->getBasePosition())) {
1721                 v3f p = object->getBasePosition();
1722                 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1723                         << "object position (" << p.X << "," << p.Y << "," << p.Z
1724                         << ") outside maximum range" << std::endl;
1725                 if (object->environmentDeletes())
1726                         delete object;
1727                 return 0;
1728         }
1729
1730         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1731                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1732
1733         m_active_objects[object->getId()] = object;
1734
1735         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1736                 <<"Added id="<<object->getId()<<"; there are now "
1737                 <<m_active_objects.size()<<" active objects."
1738                 <<std::endl;
1739
1740         // Register reference in scripting api (must be done before post-init)
1741         m_script->addObjectReference(object);
1742         // Post-initialize object
1743         object->addedToEnvironment(dtime_s);
1744
1745         // Add static data to block
1746         if(object->isStaticAllowed())
1747         {
1748                 // Add static object to active static list of the block
1749                 v3f objectpos = object->getBasePosition();
1750                 std::string staticdata = "";
1751                 object->getStaticData(&staticdata);
1752                 StaticObject s_obj(object->getType(), objectpos, staticdata);
1753                 // Add to the block where the object is located in
1754                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1755                 MapBlock *block = m_map->emergeBlock(blockpos);
1756                 if(block){
1757                         block->m_static_objects.m_active[object->getId()] = s_obj;
1758                         object->m_static_exists = true;
1759                         object->m_static_block = blockpos;
1760
1761                         if(set_changed)
1762                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1763                                         MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1764                 } else {
1765                         v3s16 p = floatToInt(objectpos, BS);
1766                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1767                                 <<"could not emerge block for storing id="<<object->getId()
1768                                 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1769                 }
1770         }
1771
1772         return object->getId();
1773 }
1774
1775 /*
1776         Remove objects that satisfy (m_removed && m_known_by_count==0)
1777 */
1778 void ServerEnvironment::removeRemovedObjects()
1779 {
1780         std::vector<u16> objects_to_remove;
1781         for(ServerActiveObjectMap::iterator i = m_active_objects.begin();
1782                 i != m_active_objects.end(); ++i) {
1783                 u16 id = i->first;
1784                 ServerActiveObject* obj = i->second;
1785                 // This shouldn't happen but check it
1786                 if(obj == NULL)
1787                 {
1788                         infostream<<"NULL object found in ServerEnvironment"
1789                                 <<" while finding removed objects. id="<<id<<std::endl;
1790                         // Id to be removed from m_active_objects
1791                         objects_to_remove.push_back(id);
1792                         continue;
1793                 }
1794
1795                 /*
1796                         We will delete objects that are marked as removed or thatare
1797                         waiting for deletion after deactivation
1798                 */
1799                 if (!obj->m_removed && !obj->m_pending_deactivation)
1800                         continue;
1801
1802                 /*
1803                         Delete static data from block if is marked as removed
1804                 */
1805                 if(obj->m_static_exists && obj->m_removed)
1806                 {
1807                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1808                         if (block) {
1809                                 block->m_static_objects.remove(id);
1810                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1811                                         MOD_REASON_REMOVE_OBJECTS_REMOVE);
1812                                 obj->m_static_exists = false;
1813                         } else {
1814                                 infostream<<"Failed to emerge block from which an object to "
1815                                         <<"be removed was loaded from. id="<<id<<std::endl;
1816                         }
1817                 }
1818
1819                 // If m_known_by_count > 0, don't actually remove. On some future
1820                 // invocation this will be 0, which is when removal will continue.
1821                 if(obj->m_known_by_count > 0)
1822                         continue;
1823
1824                 /*
1825                         Move static data from active to stored if not marked as removed
1826                 */
1827                 if(obj->m_static_exists && !obj->m_removed){
1828                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1829                         if (block) {
1830                                 std::map<u16, StaticObject>::iterator i =
1831                                         block->m_static_objects.m_active.find(id);
1832                                 if(i != block->m_static_objects.m_active.end()){
1833                                         block->m_static_objects.m_stored.push_back(i->second);
1834                                         block->m_static_objects.m_active.erase(id);
1835                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1836                                                 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1837                                 }
1838                         } else {
1839                                 infostream<<"Failed to emerge block from which an object to "
1840                                         <<"be deactivated was loaded from. id="<<id<<std::endl;
1841                         }
1842                 }
1843
1844                 // Tell the object about removal
1845                 obj->removingFromEnvironment();
1846                 // Deregister in scripting api
1847                 m_script->removeObjectReference(obj);
1848
1849                 // Delete
1850                 if(obj->environmentDeletes())
1851                         delete obj;
1852
1853                 // Id to be removed from m_active_objects
1854                 objects_to_remove.push_back(id);
1855         }
1856         // Remove references from m_active_objects
1857         for(std::vector<u16>::iterator i = objects_to_remove.begin();
1858                 i != objects_to_remove.end(); ++i) {
1859                 m_active_objects.erase(*i);
1860         }
1861 }
1862
1863 static void print_hexdump(std::ostream &o, const std::string &data)
1864 {
1865         const int linelength = 16;
1866         for(int l=0; ; l++){
1867                 int i0 = linelength * l;
1868                 bool at_end = false;
1869                 int thislinelength = linelength;
1870                 if(i0 + thislinelength > (int)data.size()){
1871                         thislinelength = data.size() - i0;
1872                         at_end = true;
1873                 }
1874                 for(int di=0; di<linelength; di++){
1875                         int i = i0 + di;
1876                         char buf[4];
1877                         if(di<thislinelength)
1878                                 snprintf(buf, 4, "%.2x ", data[i]);
1879                         else
1880                                 snprintf(buf, 4, "   ");
1881                         o<<buf;
1882                 }
1883                 o<<" ";
1884                 for(int di=0; di<thislinelength; di++){
1885                         int i = i0 + di;
1886                         if(data[i] >= 32)
1887                                 o<<data[i];
1888                         else
1889                                 o<<".";
1890                 }
1891                 o<<std::endl;
1892                 if(at_end)
1893                         break;
1894         }
1895 }
1896
1897 /*
1898         Convert stored objects from blocks near the players to active.
1899 */
1900 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1901 {
1902         if(block == NULL)
1903                 return;
1904
1905         // Ignore if no stored objects (to not set changed flag)
1906         if(block->m_static_objects.m_stored.empty())
1907                 return;
1908
1909         verbosestream<<"ServerEnvironment::activateObjects(): "
1910                 <<"activating objects of block "<<PP(block->getPos())
1911                 <<" ("<<block->m_static_objects.m_stored.size()
1912                 <<" objects)"<<std::endl;
1913         bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1914         if (large_amount) {
1915                 errorstream<<"suspiciously large amount of objects detected: "
1916                         <<block->m_static_objects.m_stored.size()<<" in "
1917                         <<PP(block->getPos())
1918                         <<"; removing all of them."<<std::endl;
1919                 // Clear stored list
1920                 block->m_static_objects.m_stored.clear();
1921                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1922                         MOD_REASON_TOO_MANY_OBJECTS);
1923                 return;
1924         }
1925
1926         // Activate stored objects
1927         std::vector<StaticObject> new_stored;
1928         for (std::vector<StaticObject>::iterator
1929                 i = block->m_static_objects.m_stored.begin();
1930                 i != block->m_static_objects.m_stored.end(); ++i) {
1931                 StaticObject &s_obj = *i;
1932
1933                 // Create an active object from the data
1934                 ServerActiveObject *obj = ServerActiveObject::create
1935                         ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1936                 // If couldn't create object, store static data back.
1937                 if(obj == NULL) {
1938                         errorstream<<"ServerEnvironment::activateObjects(): "
1939                                 <<"failed to create active object from static object "
1940                                 <<"in block "<<PP(s_obj.pos/BS)
1941                                 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1942                         print_hexdump(verbosestream, s_obj.data);
1943
1944                         new_stored.push_back(s_obj);
1945                         continue;
1946                 }
1947                 verbosestream<<"ServerEnvironment::activateObjects(): "
1948                         <<"activated static object pos="<<PP(s_obj.pos/BS)
1949                         <<" type="<<(int)s_obj.type<<std::endl;
1950                 // This will also add the object to the active static list
1951                 addActiveObjectRaw(obj, false, dtime_s);
1952         }
1953         // Clear stored list
1954         block->m_static_objects.m_stored.clear();
1955         // Add leftover failed stuff to stored list
1956         for(std::vector<StaticObject>::iterator
1957                 i = new_stored.begin();
1958                 i != new_stored.end(); ++i) {
1959                 StaticObject &s_obj = *i;
1960                 block->m_static_objects.m_stored.push_back(s_obj);
1961         }
1962
1963         // Turn the active counterparts of activated objects not pending for
1964         // deactivation
1965         for(std::map<u16, StaticObject>::iterator
1966                 i = block->m_static_objects.m_active.begin();
1967                 i != block->m_static_objects.m_active.end(); ++i)
1968         {
1969                 u16 id = i->first;
1970                 ServerActiveObject *object = getActiveObject(id);
1971                 assert(object);
1972                 object->m_pending_deactivation = false;
1973         }
1974
1975         /*
1976                 Note: Block hasn't really been modified here.
1977                 The objects have just been activated and moved from the stored
1978                 static list to the active static list.
1979                 As such, the block is essentially the same.
1980                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1981                 Otherwise there would be a huge amount of unnecessary I/O.
1982         */
1983 }
1984
1985 /*
1986         Convert objects that are not standing inside active blocks to static.
1987
1988         If m_known_by_count != 0, active object is not deleted, but static
1989         data is still updated.
1990
1991         If force_delete is set, active object is deleted nevertheless. It
1992         shall only be set so in the destructor of the environment.
1993
1994         If block wasn't generated (not in memory or on disk),
1995 */
1996 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1997 {
1998         std::vector<u16> objects_to_remove;
1999         for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
2000                 i != m_active_objects.end(); ++i) {
2001                 // force_delete might be overriden per object
2002                 bool force_delete = _force_delete;
2003
2004                 ServerActiveObject* obj = i->second;
2005                 assert(obj);
2006
2007                 // Do not deactivate if static data creation not allowed
2008                 if(!force_delete && !obj->isStaticAllowed())
2009                         continue;
2010
2011                 // If pending deactivation, let removeRemovedObjects() do it
2012                 if(!force_delete && obj->m_pending_deactivation)
2013                         continue;
2014
2015                 u16 id = i->first;
2016                 v3f objectpos = obj->getBasePosition();
2017
2018                 // The block in which the object resides in
2019                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2020
2021                 // If object's static data is stored in a deactivated block and object
2022                 // is actually located in an active block, re-save to the block in
2023                 // which the object is actually located in.
2024                 if(!force_delete &&
2025                         obj->m_static_exists &&
2026                         !m_active_blocks.contains(obj->m_static_block) &&
2027                         m_active_blocks.contains(blockpos_o))
2028                 {
2029                         v3s16 old_static_block = obj->m_static_block;
2030
2031                         // Save to block where object is located
2032                         MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2033                         if(!block){
2034                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2035                                         <<"Could not save object id="<<id
2036                                         <<" to it's current block "<<PP(blockpos_o)
2037                                         <<std::endl;
2038                                 continue;
2039                         }
2040                         std::string staticdata_new = "";
2041                         obj->getStaticData(&staticdata_new);
2042                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2043                         block->m_static_objects.insert(id, s_obj);
2044                         obj->m_static_block = blockpos_o;
2045                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2046                                 MOD_REASON_STATIC_DATA_ADDED);
2047
2048                         // Delete from block where object was located
2049                         block = m_map->emergeBlock(old_static_block, false);
2050                         if(!block){
2051                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2052                                         <<"Could not delete object id="<<id
2053                                         <<" from it's previous block "<<PP(old_static_block)
2054                                         <<std::endl;
2055                                 continue;
2056                         }
2057                         block->m_static_objects.remove(id);
2058                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2059                                 MOD_REASON_STATIC_DATA_REMOVED);
2060                         continue;
2061                 }
2062
2063                 // If block is active, don't remove
2064                 if(!force_delete && m_active_blocks.contains(blockpos_o))
2065                         continue;
2066
2067                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2068                         <<"deactivating object id="<<id<<" on inactive block "
2069                         <<PP(blockpos_o)<<std::endl;
2070
2071                 // If known by some client, don't immediately delete.
2072                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2073
2074                 /*
2075                         Update the static data
2076                 */
2077
2078                 if(obj->isStaticAllowed())
2079                 {
2080                         // Create new static object
2081                         std::string staticdata_new = "";
2082                         obj->getStaticData(&staticdata_new);
2083                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2084
2085                         bool stays_in_same_block = false;
2086                         bool data_changed = true;
2087
2088                         if (obj->m_static_exists) {
2089                                 if (obj->m_static_block == blockpos_o)
2090                                         stays_in_same_block = true;
2091
2092                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2093
2094                                 if (block) {
2095                                         std::map<u16, StaticObject>::iterator n =
2096                                                 block->m_static_objects.m_active.find(id);
2097                                         if (n != block->m_static_objects.m_active.end()) {
2098                                                 StaticObject static_old = n->second;
2099
2100                                                 float save_movem = obj->getMinimumSavedMovement();
2101
2102                                                 if (static_old.data == staticdata_new &&
2103                                                         (static_old.pos - objectpos).getLength() < save_movem)
2104                                                         data_changed = false;
2105                                         } else {
2106                                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2107                                                         <<"id="<<id<<" m_static_exists=true but "
2108                                                         <<"static data doesn't actually exist in "
2109                                                         <<PP(obj->m_static_block)<<std::endl;
2110                                         }
2111                                 }
2112                         }
2113
2114                         bool shall_be_written = (!stays_in_same_block || data_changed);
2115
2116                         // Delete old static object
2117                         if(obj->m_static_exists)
2118                         {
2119                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2120                                 if(block)
2121                                 {
2122                                         block->m_static_objects.remove(id);
2123                                         obj->m_static_exists = false;
2124                                         // Only mark block as modified if data changed considerably
2125                                         if(shall_be_written)
2126                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2127                                                         MOD_REASON_STATIC_DATA_CHANGED);
2128                                 }
2129                         }
2130
2131                         // Add to the block where the object is located in
2132                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2133                         // Get or generate the block
2134                         MapBlock *block = NULL;
2135                         try{
2136                                 block = m_map->emergeBlock(blockpos);
2137                         } catch(InvalidPositionException &e){
2138                                 // Handled via NULL pointer
2139                                 // NOTE: emergeBlock's failure is usually determined by it
2140                                 //       actually returning NULL
2141                         }
2142
2143                         if(block)
2144                         {
2145                                 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2146                                         warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2147                                                 << " statically but block " << PP(blockpos)
2148                                                 << " already contains "
2149                                                 << block->m_static_objects.m_stored.size()
2150                                                 << " objects."
2151                                                 << " Forcing delete." << std::endl;
2152                                         force_delete = true;
2153                                 } else {
2154                                         // If static counterpart already exists in target block,
2155                                         // remove it first.
2156                                         // This shouldn't happen because the object is removed from
2157                                         // the previous block before this according to
2158                                         // obj->m_static_block, but happens rarely for some unknown
2159                                         // reason. Unsuccessful attempts have been made to find
2160                                         // said reason.
2161                                         if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2162                                                 warningstream<<"ServerEnv: Performing hack #83274"
2163                                                         <<std::endl;
2164                                                 block->m_static_objects.remove(id);
2165                                         }
2166                                         // Store static data
2167                                         u16 store_id = pending_delete ? id : 0;
2168                                         block->m_static_objects.insert(store_id, s_obj);
2169
2170                                         // Only mark block as modified if data changed considerably
2171                                         if(shall_be_written)
2172                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2173                                                         MOD_REASON_STATIC_DATA_CHANGED);
2174
2175                                         obj->m_static_exists = true;
2176                                         obj->m_static_block = block->getPos();
2177                                 }
2178                         }
2179                         else{
2180                                 if(!force_delete){
2181                                         v3s16 p = floatToInt(objectpos, BS);
2182                                         errorstream<<"ServerEnv: Could not find or generate "
2183                                                 <<"a block for storing id="<<obj->getId()
2184                                                 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2185                                         continue;
2186                                 }
2187                         }
2188                 }
2189
2190                 /*
2191                         If known by some client, set pending deactivation.
2192                         Otherwise delete it immediately.
2193                 */
2194
2195                 if(pending_delete && !force_delete)
2196                 {
2197                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2198                                 <<"object id="<<id<<" is known by clients"
2199                                 <<"; not deleting yet"<<std::endl;
2200
2201                         obj->m_pending_deactivation = true;
2202                         continue;
2203                 }
2204
2205                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2206                         <<"object id="<<id<<" is not known by clients"
2207                         <<"; deleting"<<std::endl;
2208
2209                 // Tell the object about removal
2210                 obj->removingFromEnvironment();
2211                 // Deregister in scripting api
2212                 m_script->removeObjectReference(obj);
2213
2214                 // Delete active object
2215                 if(obj->environmentDeletes())
2216                         delete obj;
2217                 // Id to be removed from m_active_objects
2218                 objects_to_remove.push_back(id);
2219         }
2220
2221         // Remove references from m_active_objects
2222         for(std::vector<u16>::iterator i = objects_to_remove.begin();
2223                 i != objects_to_remove.end(); ++i) {
2224                 m_active_objects.erase(*i);
2225         }
2226 }
2227
2228 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2229                 const std::string &savedir, const Settings &conf)
2230 {
2231
2232         if (name == "sqlite3")
2233                 return new PlayerDatabaseSQLite3(savedir);
2234         else if (name == "dummy")
2235                 return new Database_Dummy();
2236 #if USE_POSTGRESQL
2237         else if (name == "postgresql") {
2238                 std::string connect_string = "";
2239                 conf.getNoEx("pgsql_player_connection", connect_string);
2240                 return new PlayerDatabasePostgreSQL(connect_string);
2241         }
2242 #endif
2243         else if (name == "files")
2244                 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2245         else
2246                 throw BaseException(std::string("Database backend ") + name + " not supported.");
2247 }
2248
2249 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2250                 const Settings &cmd_args)
2251 {
2252         std::string migrate_to = cmd_args.get("migrate-players");
2253         Settings world_mt;
2254         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2255         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2256                 errorstream << "Cannot read world.mt!" << std::endl;
2257                 return false;
2258         }
2259
2260         if (!world_mt.exists("player_backend")) {
2261                 errorstream << "Please specify your current backend in world.mt:"
2262                         << std::endl
2263                         << "    player_backend = {files|sqlite3|postgresql}"
2264                         << std::endl;
2265                 return false;
2266         }
2267
2268         std::string backend = world_mt.get("player_backend");
2269         if (backend == migrate_to) {
2270                 errorstream << "Cannot migrate: new backend is same"
2271                         << " as the old one" << std::endl;
2272                 return false;
2273         }
2274
2275         const std::string players_backup_path = game_params.world_path + DIR_DELIM
2276                 + "players.bak";
2277
2278         if (backend == "files") {
2279                 // Create backup directory
2280                 fs::CreateDir(players_backup_path);
2281         }
2282
2283         try {
2284                 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2285                         game_params.world_path, world_mt);
2286                 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2287                         game_params.world_path, world_mt);
2288
2289                 std::vector<std::string> player_list;
2290                 srcdb->listPlayers(player_list);
2291                 for (std::vector<std::string>::const_iterator it = player_list.begin();
2292                         it != player_list.end(); ++it) {
2293                         actionstream << "Migrating player " << it->c_str() << std::endl;
2294                         RemotePlayer player(it->c_str(), NULL);
2295                         PlayerSAO playerSAO(NULL, &player, 15000, false);
2296
2297                         srcdb->loadPlayer(&player, &playerSAO);
2298
2299                         playerSAO.finalize(&player, std::set<std::string>());
2300                         player.setPlayerSAO(&playerSAO);
2301
2302                         dstdb->savePlayer(&player);
2303
2304                         // For files source, move player files to backup dir
2305                         if (backend == "files") {
2306                                 fs::Rename(
2307                                         game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2308                                         players_backup_path + DIR_DELIM + (*it));
2309                         }
2310                 }
2311
2312                 actionstream << "Successfully migrated " << player_list.size() << " players"
2313                         << std::endl;
2314                 world_mt.set("player_backend", migrate_to);
2315                 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2316                         errorstream << "Failed to update world.mt!" << std::endl;
2317                 else
2318                         actionstream << "world.mt updated" << std::endl;
2319
2320                 // When migration is finished from file backend, remove players directory if empty
2321                 if (backend == "files") {
2322                         fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2323                                 + "players");
2324                 }
2325
2326                 delete srcdb;
2327                 delete dstdb;
2328
2329         } catch (BaseException &e) {
2330                 errorstream << "An error occured during migration: " << e.what() << std::endl;
2331                 return false;
2332         }
2333         return true;
2334 }