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