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