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