Performance fix + SAO factorization
[oweals/minetest.git] / src / serverenvironment.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "serverenvironment.h"
21 #include "content_sao.h"
22 #include "settings.h"
23 #include "log.h"
24 #include "nodedef.h"
25 #include "nodemetadata.h"
26 #include "gamedef.h"
27 #include "map.h"
28 #include "profiler.h"
29 #include "raycast.h"
30 #include "remoteplayer.h"
31 #include "scripting_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 = object->getStaticData();
1698                 StaticObject s_obj(object->getType(), objectpos, staticdata);
1699                 // Add to the block where the object is located in
1700                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1701                 MapBlock *block = m_map->emergeBlock(blockpos);
1702                 if(block){
1703                         block->m_static_objects.m_active[object->getId()] = s_obj;
1704                         object->m_static_exists = true;
1705                         object->m_static_block = blockpos;
1706
1707                         if(set_changed)
1708                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1709                                         MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1710                 } else {
1711                         v3s16 p = floatToInt(objectpos, BS);
1712                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1713                                 <<"could not emerge block for storing id="<<object->getId()
1714                                 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1715                 }
1716         }
1717
1718         return object->getId();
1719 }
1720
1721 /*
1722         Remove objects that satisfy (m_removed && m_known_by_count==0)
1723 */
1724 void ServerEnvironment::removeRemovedObjects()
1725 {
1726         std::vector<u16> objects_to_remove;
1727         for(ActiveObjectMap::iterator i = m_active_objects.begin();
1728                 i != m_active_objects.end(); ++i) {
1729                 u16 id = i->first;
1730                 ServerActiveObject* obj = i->second;
1731                 // This shouldn't happen but check it
1732                 if(obj == NULL)
1733                 {
1734                         infostream<<"NULL object found in ServerEnvironment"
1735                                 <<" while finding removed objects. id="<<id<<std::endl;
1736                         // Id to be removed from m_active_objects
1737                         objects_to_remove.push_back(id);
1738                         continue;
1739                 }
1740
1741                 /*
1742                         We will delete objects that are marked as removed or thatare
1743                         waiting for deletion after deactivation
1744                 */
1745                 if (!obj->m_removed && !obj->m_pending_deactivation)
1746                         continue;
1747
1748                 /*
1749                         Delete static data from block if is marked as removed
1750                 */
1751                 if(obj->m_static_exists && obj->m_removed)
1752                 {
1753                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1754                         if (block) {
1755                                 block->m_static_objects.remove(id);
1756                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1757                                         MOD_REASON_REMOVE_OBJECTS_REMOVE);
1758                                 obj->m_static_exists = false;
1759                         } else {
1760                                 infostream<<"Failed to emerge block from which an object to "
1761                                         <<"be removed was loaded from. id="<<id<<std::endl;
1762                         }
1763                 }
1764
1765                 // If m_known_by_count > 0, don't actually remove. On some future
1766                 // invocation this will be 0, which is when removal will continue.
1767                 if(obj->m_known_by_count > 0)
1768                         continue;
1769
1770                 /*
1771                         Move static data from active to stored if not marked as removed
1772                 */
1773                 if(obj->m_static_exists && !obj->m_removed){
1774                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1775                         if (block) {
1776                                 std::map<u16, StaticObject>::iterator i =
1777                                         block->m_static_objects.m_active.find(id);
1778                                 if(i != block->m_static_objects.m_active.end()){
1779                                         block->m_static_objects.m_stored.push_back(i->second);
1780                                         block->m_static_objects.m_active.erase(id);
1781                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1782                                                 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1783                                 }
1784                         } else {
1785                                 infostream<<"Failed to emerge block from which an object to "
1786                                         <<"be deactivated was loaded from. id="<<id<<std::endl;
1787                         }
1788                 }
1789
1790                 // Tell the object about removal
1791                 obj->removingFromEnvironment();
1792                 // Deregister in scripting api
1793                 m_script->removeObjectReference(obj);
1794
1795                 // Delete
1796                 if(obj->environmentDeletes())
1797                         delete obj;
1798
1799                 // Id to be removed from m_active_objects
1800                 objects_to_remove.push_back(id);
1801         }
1802         // Remove references from m_active_objects
1803         for(std::vector<u16>::iterator i = objects_to_remove.begin();
1804                 i != objects_to_remove.end(); ++i) {
1805                 m_active_objects.erase(*i);
1806         }
1807 }
1808
1809 static void print_hexdump(std::ostream &o, const std::string &data)
1810 {
1811         const int linelength = 16;
1812         for(int l=0; ; l++){
1813                 int i0 = linelength * l;
1814                 bool at_end = false;
1815                 int thislinelength = linelength;
1816                 if(i0 + thislinelength > (int)data.size()){
1817                         thislinelength = data.size() - i0;
1818                         at_end = true;
1819                 }
1820                 for(int di=0; di<linelength; di++){
1821                         int i = i0 + di;
1822                         char buf[4];
1823                         if(di<thislinelength)
1824                                 snprintf(buf, 4, "%.2x ", data[i]);
1825                         else
1826                                 snprintf(buf, 4, "   ");
1827                         o<<buf;
1828                 }
1829                 o<<" ";
1830                 for(int di=0; di<thislinelength; di++){
1831                         int i = i0 + di;
1832                         if(data[i] >= 32)
1833                                 o<<data[i];
1834                         else
1835                                 o<<".";
1836                 }
1837                 o<<std::endl;
1838                 if(at_end)
1839                         break;
1840         }
1841 }
1842
1843 /*
1844         Convert stored objects from blocks near the players to active.
1845 */
1846 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1847 {
1848         if(block == NULL)
1849                 return;
1850
1851         // Ignore if no stored objects (to not set changed flag)
1852         if(block->m_static_objects.m_stored.empty())
1853                 return;
1854
1855         verbosestream<<"ServerEnvironment::activateObjects(): "
1856                 <<"activating objects of block "<<PP(block->getPos())
1857                 <<" ("<<block->m_static_objects.m_stored.size()
1858                 <<" objects)"<<std::endl;
1859         bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1860         if (large_amount) {
1861                 errorstream<<"suspiciously large amount of objects detected: "
1862                         <<block->m_static_objects.m_stored.size()<<" in "
1863                         <<PP(block->getPos())
1864                         <<"; removing all of them."<<std::endl;
1865                 // Clear stored list
1866                 block->m_static_objects.m_stored.clear();
1867                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1868                         MOD_REASON_TOO_MANY_OBJECTS);
1869                 return;
1870         }
1871
1872         // Activate stored objects
1873         std::vector<StaticObject> new_stored;
1874         for (std::vector<StaticObject>::iterator
1875                 i = block->m_static_objects.m_stored.begin();
1876                 i != block->m_static_objects.m_stored.end(); ++i) {
1877                 StaticObject &s_obj = *i;
1878
1879                 // Create an active object from the data
1880                 ServerActiveObject *obj = ServerActiveObject::create
1881                         ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1882                 // If couldn't create object, store static data back.
1883                 if(obj == NULL) {
1884                         errorstream<<"ServerEnvironment::activateObjects(): "
1885                                 <<"failed to create active object from static object "
1886                                 <<"in block "<<PP(s_obj.pos/BS)
1887                                 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1888                         print_hexdump(verbosestream, s_obj.data);
1889
1890                         new_stored.push_back(s_obj);
1891                         continue;
1892                 }
1893                 verbosestream<<"ServerEnvironment::activateObjects(): "
1894                         <<"activated static object pos="<<PP(s_obj.pos/BS)
1895                         <<" type="<<(int)s_obj.type<<std::endl;
1896                 // This will also add the object to the active static list
1897                 addActiveObjectRaw(obj, false, dtime_s);
1898         }
1899         // Clear stored list
1900         block->m_static_objects.m_stored.clear();
1901         // Add leftover failed stuff to stored list
1902         for(std::vector<StaticObject>::iterator
1903                 i = new_stored.begin();
1904                 i != new_stored.end(); ++i) {
1905                 StaticObject &s_obj = *i;
1906                 block->m_static_objects.m_stored.push_back(s_obj);
1907         }
1908
1909         // Turn the active counterparts of activated objects not pending for
1910         // deactivation
1911         for(std::map<u16, StaticObject>::iterator
1912                 i = block->m_static_objects.m_active.begin();
1913                 i != block->m_static_objects.m_active.end(); ++i)
1914         {
1915                 u16 id = i->first;
1916                 ServerActiveObject *object = getActiveObject(id);
1917                 assert(object);
1918                 object->m_pending_deactivation = false;
1919         }
1920
1921         /*
1922                 Note: Block hasn't really been modified here.
1923                 The objects have just been activated and moved from the stored
1924                 static list to the active static list.
1925                 As such, the block is essentially the same.
1926                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1927                 Otherwise there would be a huge amount of unnecessary I/O.
1928         */
1929 }
1930
1931 /*
1932         Convert objects that are not standing inside active blocks to static.
1933
1934         If m_known_by_count != 0, active object is not deleted, but static
1935         data is still updated.
1936
1937         If force_delete is set, active object is deleted nevertheless. It
1938         shall only be set so in the destructor of the environment.
1939
1940         If block wasn't generated (not in memory or on disk),
1941 */
1942 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1943 {
1944         std::vector<u16> objects_to_remove;
1945         for(ActiveObjectMap::iterator i = m_active_objects.begin();
1946                 i != m_active_objects.end(); ++i) {
1947                 ServerActiveObject* obj = i->second;
1948                 assert(obj);
1949
1950                 // Do not deactivate if static data creation not allowed
1951                 if(!force_delete && !obj->isStaticAllowed())
1952                         continue;
1953
1954                 // If pending deactivation, let removeRemovedObjects() do it
1955                 if(!force_delete && obj->m_pending_deactivation)
1956                         continue;
1957
1958                 u16 id = i->first;
1959                 v3f objectpos = obj->getBasePosition();
1960
1961                 // The block in which the object resides in
1962                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1963
1964                 // If object's static data is stored in a deactivated block and object
1965                 // is actually located in an active block, re-save to the block in
1966                 // which the object is actually located in.
1967                 if(!force_delete &&
1968                         obj->m_static_exists &&
1969                         !m_active_blocks.contains(obj->m_static_block) &&
1970                         m_active_blocks.contains(blockpos_o))
1971                 {
1972                         v3s16 old_static_block = obj->m_static_block;
1973
1974                         // Save to block where object is located
1975                         MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1976                         if(!block){
1977                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1978                                         <<"Could not save object id="<<id
1979                                         <<" to it's current block "<<PP(blockpos_o)
1980                                         <<std::endl;
1981                                 continue;
1982                         }
1983                         std::string staticdata_new = obj->getStaticData();
1984                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1985                         block->m_static_objects.insert(id, s_obj);
1986                         obj->m_static_block = blockpos_o;
1987                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1988                                 MOD_REASON_STATIC_DATA_ADDED);
1989
1990                         // Delete from block where object was located
1991                         block = m_map->emergeBlock(old_static_block, false);
1992                         if(!block){
1993                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1994                                         <<"Could not delete object id="<<id
1995                                         <<" from it's previous block "<<PP(old_static_block)
1996                                         <<std::endl;
1997                                 continue;
1998                         }
1999                         block->m_static_objects.remove(id);
2000                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2001                                 MOD_REASON_STATIC_DATA_REMOVED);
2002                         continue;
2003                 }
2004
2005                 // If block is active, don't remove
2006                 if(!force_delete && m_active_blocks.contains(blockpos_o))
2007                         continue;
2008
2009                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2010                         <<"deactivating object id="<<id<<" on inactive block "
2011                         <<PP(blockpos_o)<<std::endl;
2012
2013                 // If known by some client, don't immediately delete.
2014                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2015
2016                 /*
2017                         Update the static data
2018                 */
2019
2020                 if(obj->isStaticAllowed())
2021                 {
2022                         // Create new static object
2023                         std::string staticdata_new = obj->getStaticData();
2024                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2025
2026                         bool stays_in_same_block = false;
2027                         bool data_changed = true;
2028
2029                         if (obj->m_static_exists) {
2030                                 if (obj->m_static_block == blockpos_o)
2031                                         stays_in_same_block = true;
2032
2033                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2034
2035                                 if (block) {
2036                                         std::map<u16, StaticObject>::iterator n =
2037                                                 block->m_static_objects.m_active.find(id);
2038                                         if (n != block->m_static_objects.m_active.end()) {
2039                                                 StaticObject static_old = n->second;
2040
2041                                                 float save_movem = obj->getMinimumSavedMovement();
2042
2043                                                 if (static_old.data == staticdata_new &&
2044                                                         (static_old.pos - objectpos).getLength() < save_movem)
2045                                                         data_changed = false;
2046                                         } else {
2047                                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2048                                                         <<"id="<<id<<" m_static_exists=true but "
2049                                                         <<"static data doesn't actually exist in "
2050                                                         <<PP(obj->m_static_block)<<std::endl;
2051                                         }
2052                                 }
2053                         }
2054
2055                         bool shall_be_written = (!stays_in_same_block || data_changed);
2056
2057                         // Delete old static object
2058                         if(obj->m_static_exists)
2059                         {
2060                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2061                                 if(block)
2062                                 {
2063                                         block->m_static_objects.remove(id);
2064                                         obj->m_static_exists = false;
2065                                         // Only mark block as modified if data changed considerably
2066                                         if(shall_be_written)
2067                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2068                                                         MOD_REASON_STATIC_DATA_CHANGED);
2069                                 }
2070                         }
2071
2072                         // Add to the block where the object is located in
2073                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2074                         // Get or generate the block
2075                         MapBlock *block = NULL;
2076                         try{
2077                                 block = m_map->emergeBlock(blockpos);
2078                         } catch(InvalidPositionException &e){
2079                                 // Handled via NULL pointer
2080                                 // NOTE: emergeBlock's failure is usually determined by it
2081                                 //       actually returning NULL
2082                         }
2083
2084                         if(block)
2085                         {
2086                                 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2087                                         warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2088                                                 << " statically but block " << PP(blockpos)
2089                                                 << " already contains "
2090                                                 << block->m_static_objects.m_stored.size()
2091                                                 << " objects."
2092                                                 << " Forcing delete." << std::endl;
2093                                         force_delete = true;
2094                                 } else {
2095                                         // If static counterpart already exists in target block,
2096                                         // remove it first.
2097                                         // This shouldn't happen because the object is removed from
2098                                         // the previous block before this according to
2099                                         // obj->m_static_block, but happens rarely for some unknown
2100                                         // reason. Unsuccessful attempts have been made to find
2101                                         // said reason.
2102                                         if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2103                                                 warningstream<<"ServerEnv: Performing hack #83274"
2104                                                         <<std::endl;
2105                                                 block->m_static_objects.remove(id);
2106                                         }
2107                                         // Store static data
2108                                         u16 store_id = pending_delete ? id : 0;
2109                                         block->m_static_objects.insert(store_id, s_obj);
2110
2111                                         // Only mark block as modified if data changed considerably
2112                                         if(shall_be_written)
2113                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2114                                                         MOD_REASON_STATIC_DATA_CHANGED);
2115
2116                                         obj->m_static_exists = true;
2117                                         obj->m_static_block = block->getPos();
2118                                 }
2119                         }
2120                         else{
2121                                 if(!force_delete){
2122                                         v3s16 p = floatToInt(objectpos, BS);
2123                                         errorstream<<"ServerEnv: Could not find or generate "
2124                                                 <<"a block for storing id="<<obj->getId()
2125                                                 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2126                                         continue;
2127                                 }
2128                         }
2129                 }
2130
2131                 /*
2132                         If known by some client, set pending deactivation.
2133                         Otherwise delete it immediately.
2134                 */
2135
2136                 if(pending_delete && !force_delete)
2137                 {
2138                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2139                                 <<"object id="<<id<<" is known by clients"
2140                                 <<"; not deleting yet"<<std::endl;
2141
2142                         obj->m_pending_deactivation = true;
2143                         continue;
2144                 }
2145
2146                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2147                         <<"object id="<<id<<" is not known by clients"
2148                         <<"; deleting"<<std::endl;
2149
2150                 // Tell the object about removal
2151                 obj->removingFromEnvironment();
2152                 // Deregister in scripting api
2153                 m_script->removeObjectReference(obj);
2154
2155                 // Delete active object
2156                 if(obj->environmentDeletes())
2157                         delete obj;
2158                 // Id to be removed from m_active_objects
2159                 objects_to_remove.push_back(id);
2160         }
2161
2162         // Remove references from m_active_objects
2163         for(std::vector<u16>::iterator i = objects_to_remove.begin();
2164                 i != objects_to_remove.end(); ++i) {
2165                 m_active_objects.erase(*i);
2166         }
2167 }