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