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