ecda1b6a445e12709bf549aa4c33ce2cc5563df9
[oweals/minetest.git] / src / environment.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 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 <fstream>
21 #include "environment.h"
22 #include "filesys.h"
23 #include "porting.h"
24 #include "collision.h"
25 #include "content_mapnode.h"
26 #include "mapblock.h"
27 #include "serverobject.h"
28 #include "content_sao.h"
29 #include "settings.h"
30 #include "log.h"
31 #include "profiler.h"
32 #include "scripting_game.h"
33 #include "nodedef.h"
34 #include "nodemetadata.h"
35 #include "gamedef.h"
36 #ifndef SERVER
37 #include "clientmap.h"
38 #include "localplayer.h"
39 #include "mapblock_mesh.h"
40 #include "event.h"
41 #endif
42 #include "server.h"
43 #include "daynightratio.h"
44 #include "map.h"
45 #include "emerge.h"
46 #include "util/serialize.h"
47 #include "threading/mutex_auto_lock.h"
48
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50
51 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
52
53 // A number that is much smaller than the timeout for particle spawners should/could ever be
54 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
55
56 Environment::Environment():
57         m_time_of_day_speed(0),
58         m_time_of_day(9000),
59         m_time_of_day_f(9000./24000),
60         m_time_conversion_skew(0.0f),
61         m_enable_day_night_ratio_override(false),
62         m_day_night_ratio_override(0.0f)
63 {
64         m_cache_enable_shaders = g_settings->getBool("enable_shaders");
65         m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval");
66         m_cache_abm_interval = g_settings->getFloat("abm_interval");
67         m_cache_nodetimer_interval = g_settings->getFloat("nodetimer_interval");
68 }
69
70 Environment::~Environment()
71 {
72 }
73
74 u32 Environment::getDayNightRatio()
75 {
76         MutexAutoLock lock(this->m_time_lock);
77         if (m_enable_day_night_ratio_override)
78                 return m_day_night_ratio_override;
79         return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
80 }
81
82 void Environment::setTimeOfDaySpeed(float speed)
83 {
84         m_time_of_day_speed = speed;
85 }
86
87 void Environment::setDayNightRatioOverride(bool enable, u32 value)
88 {
89         MutexAutoLock lock(this->m_time_lock);
90         m_enable_day_night_ratio_override = enable;
91         m_day_night_ratio_override = value;
92 }
93
94 void Environment::setTimeOfDay(u32 time)
95 {
96         MutexAutoLock lock(this->m_time_lock);
97         if (m_time_of_day > time)
98                 m_day_count++;
99         m_time_of_day = time;
100         m_time_of_day_f = (float)time / 24000.0;
101 }
102
103 u32 Environment::getTimeOfDay()
104 {
105         MutexAutoLock lock(this->m_time_lock);
106         return m_time_of_day;
107 }
108
109 float Environment::getTimeOfDayF()
110 {
111         MutexAutoLock lock(this->m_time_lock);
112         return m_time_of_day_f;
113 }
114
115 void Environment::stepTimeOfDay(float dtime)
116 {
117         MutexAutoLock lock(this->m_time_lock);
118
119         // Cached in order to prevent the two reads we do to give
120         // different results (can be written by code not under the lock)
121         f32 cached_time_of_day_speed = m_time_of_day_speed;
122
123         f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
124         m_time_conversion_skew += dtime;
125         u32 units = (u32)(m_time_conversion_skew * speed);
126         bool sync_f = false;
127         if (units > 0) {
128                 // Sync at overflow
129                 if (m_time_of_day + units >= 24000) {
130                         sync_f = true;
131                         m_day_count++;
132                 }
133                 m_time_of_day = (m_time_of_day + units) % 24000;
134                 if (sync_f)
135                         m_time_of_day_f = (float)m_time_of_day / 24000.0;
136         }
137         if (speed > 0) {
138                 m_time_conversion_skew -= (f32)units / speed;
139         }
140         if (!sync_f) {
141                 m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
142                 if (m_time_of_day_f > 1.0)
143                         m_time_of_day_f -= 1.0;
144                 if (m_time_of_day_f < 0.0)
145                         m_time_of_day_f += 1.0;
146         }
147 }
148
149 u32 Environment::getDayCount()
150 {
151         // Atomic<u32> counter
152         return m_day_count;
153 }
154
155
156 /*
157         ABMWithState
158 */
159
160 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
161         abm(abm_),
162         timer(0)
163 {
164         // Initialize timer to random value to spread processing
165         float itv = abm->getTriggerInterval();
166         itv = MYMAX(0.001, itv); // No less than 1ms
167         int minval = MYMAX(-0.51*itv, -60); // Clamp to
168         int maxval = MYMIN(0.51*itv, 60);   // +-60 seconds
169         timer = myrand_range(minval, maxval);
170 }
171
172 /*
173         LBMManager
174 */
175
176 void LBMContentMapping::deleteContents()
177 {
178         for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
179                         it != lbm_list.end(); ++it) {
180                 delete *it;
181         }
182 }
183
184 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
185 {
186         // Add the lbm_def to the LBMContentMapping.
187         // Unknown names get added to the global NameIdMapping.
188         INodeDefManager *nodedef = gamedef->ndef();
189
190         lbm_list.push_back(lbm_def);
191
192         for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
193                         it != lbm_def->trigger_contents.end(); ++it) {
194                 std::set<content_t> c_ids;
195                 bool found = nodedef->getIds(*it, c_ids);
196                 if (!found) {
197                         content_t c_id = gamedef->allocateUnknownNodeId(*it);
198                         if (c_id == CONTENT_IGNORE) {
199                                 // Seems it can't be allocated.
200                                 warningstream << "Could not internalize node name \"" << *it
201                                         << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
202                                 continue;
203                         }
204                         c_ids.insert(c_id);
205                 }
206
207                 for (std::set<content_t>::const_iterator iit =
208                                 c_ids.begin(); iit != c_ids.end(); ++iit) {
209                         content_t c_id = *iit;
210                         map[c_id].push_back(lbm_def);
211                 }
212         }
213 }
214
215 const std::vector<LoadingBlockModifierDef *> *
216                 LBMContentMapping::lookup(content_t c) const
217 {
218         container_map::const_iterator it = map.find(c);
219         if (it == map.end())
220                 return NULL;
221         // This first dereferences the iterator, returning
222         // a std::vector<LoadingBlockModifierDef *>
223         // reference, then we convert it to a pointer.
224         return &(it->second);
225 }
226
227 LBMManager::~LBMManager()
228 {
229         for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
230                         m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
231                 delete it->second;
232         }
233         for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
234                         it != m_lbm_lookup.end(); ++it) {
235                 (it->second).deleteContents();
236         }
237 }
238
239 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
240 {
241         // Precondition, in query mode the map isn't used anymore
242         FATAL_ERROR_IF(m_query_mode == true,
243                 "attempted to modify LBMManager in query mode");
244
245         if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
246                 throw ModError("Error adding LBM \"" + lbm_def->name +
247                         "\": Does not follow naming conventions: "
248                         "Only chararacters [a-z0-9_:] are allowed.");
249         }
250
251         m_lbm_defs[lbm_def->name] = lbm_def;
252 }
253
254 void LBMManager::loadIntroductionTimes(const std::string &times,
255                 IGameDef *gamedef, u32 now)
256 {
257         m_query_mode = true;
258
259         // name -> time map.
260         // Storing it in a map first instead of
261         // handling the stuff directly in the loop
262         // removes all duplicate entries.
263         // TODO make this std::unordered_map
264         std::map<std::string, u32> introduction_times;
265
266         /*
267         The introduction times string consists of name~time entries,
268         with each entry terminated by a semicolon. The time is decimal.
269          */
270
271         size_t idx = 0;
272         size_t idx_new;
273         while ((idx_new = times.find(";", idx)) != std::string::npos) {
274                 std::string entry = times.substr(idx, idx_new - idx);
275                 std::vector<std::string> components = str_split(entry, '~');
276                 if (components.size() != 2)
277                         throw SerializationError("Introduction times entry \""
278                                 + entry + "\" requires exactly one '~'!");
279                 const std::string &name = components[0];
280                 u32 time = from_string<u32>(components[1]);
281                 introduction_times[name] = time;
282                 idx = idx_new + 1;
283         }
284
285         // Put stuff from introduction_times into m_lbm_lookup
286         for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
287                         it != introduction_times.end(); ++it) {
288                 const std::string &name = it->first;
289                 u32 time = it->second;
290
291                 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
292                         m_lbm_defs.find(name);
293                 if (def_it == m_lbm_defs.end()) {
294                         // This seems to be an LBM entry for
295                         // an LBM we haven't loaded. Discard it.
296                         continue;
297                 }
298                 LoadingBlockModifierDef *lbm_def = def_it->second;
299                 if (lbm_def->run_at_every_load) {
300                         // This seems to be an LBM entry for
301                         // an LBM that runs at every load.
302                         // Don't add it just yet.
303                         continue;
304                 }
305
306                 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
307
308                 // Erase the entry so that we know later
309                 // what elements didn't get put into m_lbm_lookup
310                 m_lbm_defs.erase(name);
311         }
312
313         // Now also add the elements from m_lbm_defs to m_lbm_lookup
314         // that weren't added in the previous step.
315         // They are introduced first time to this world,
316         // or are run at every load (introducement time hardcoded to U32_MAX).
317
318         LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
319         LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
320
321         for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
322                         m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
323                 if (it->second->run_at_every_load) {
324                         lbms_running_always.addLBM(it->second, gamedef);
325                 } else {
326                         lbms_we_introduce_now.addLBM(it->second, gamedef);
327                 }
328         }
329
330         // Clear the list, so that we don't delete remaining elements
331         // twice in the destructor
332         m_lbm_defs.clear();
333 }
334
335 std::string LBMManager::createIntroductionTimesString()
336 {
337         // Precondition, we must be in query mode
338         FATAL_ERROR_IF(m_query_mode == false,
339                 "attempted to query on non fully set up LBMManager");
340
341         std::ostringstream oss;
342         for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
343                         it != m_lbm_lookup.end(); ++it) {
344                 u32 time = it->first;
345                 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
346                 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
347                                 iit != lbm_list.end(); ++iit) {
348                         // Don't add if the LBM runs at every load,
349                         // then introducement time is hardcoded
350                         // and doesn't need to be stored
351                         if ((*iit)->run_at_every_load)
352                                 continue;
353                         oss << (*iit)->name << "~" << time << ";";
354                 }
355         }
356         return oss.str();
357 }
358
359 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
360 {
361         // Precondition, we need m_lbm_lookup to be initialized
362         FATAL_ERROR_IF(m_query_mode == false,
363                 "attempted to query on non fully set up LBMManager");
364         v3s16 pos_of_block = block->getPosRelative();
365         v3s16 pos;
366         MapNode n;
367         content_t c;
368         lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
369         for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
370         for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
371         for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
372         {
373                 n = block->getNodeNoEx(pos);
374                 c = n.getContent();
375                 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
376                                 iit != m_lbm_lookup.end(); ++iit) {
377                         const std::vector<LoadingBlockModifierDef *> *lbm_list =
378                                 iit->second.lookup(c);
379                         if (!lbm_list)
380                                 continue;
381                         for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
382                                         lbm_list->begin(); iit != lbm_list->end(); ++iit) {
383                                 (*iit)->trigger(env, pos + pos_of_block, n);
384                         }
385                 }
386         }
387 }
388
389 /*
390         ActiveBlockList
391 */
392
393 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
394 {
395         v3s16 p;
396         for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
397         for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
398         for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
399         {
400                 // Set in list
401                 list.insert(p);
402         }
403 }
404
405 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
406                 s16 radius,
407                 std::set<v3s16> &blocks_removed,
408                 std::set<v3s16> &blocks_added)
409 {
410         /*
411                 Create the new list
412         */
413         std::set<v3s16> newlist = m_forceloaded_list;
414         for(std::vector<v3s16>::iterator i = active_positions.begin();
415                         i != active_positions.end(); ++i)
416         {
417                 fillRadiusBlock(*i, radius, newlist);
418         }
419
420         /*
421                 Find out which blocks on the old list are not on the new list
422         */
423         // Go through old list
424         for(std::set<v3s16>::iterator i = m_list.begin();
425                         i != m_list.end(); ++i)
426         {
427                 v3s16 p = *i;
428                 // If not on new list, it's been removed
429                 if(newlist.find(p) == newlist.end())
430                         blocks_removed.insert(p);
431         }
432
433         /*
434                 Find out which blocks on the new list are not on the old list
435         */
436         // Go through new list
437         for(std::set<v3s16>::iterator i = newlist.begin();
438                         i != newlist.end(); ++i)
439         {
440                 v3s16 p = *i;
441                 // If not on old list, it's been added
442                 if(m_list.find(p) == m_list.end())
443                         blocks_added.insert(p);
444         }
445
446         /*
447                 Update m_list
448         */
449         m_list.clear();
450         for(std::set<v3s16>::iterator i = newlist.begin();
451                         i != newlist.end(); ++i)
452         {
453                 v3s16 p = *i;
454                 m_list.insert(p);
455         }
456 }
457
458 /*
459         ServerEnvironment
460 */
461
462 ServerEnvironment::ServerEnvironment(ServerMap *map,
463                 GameScripting *scriptIface, IGameDef *gamedef,
464                 const std::string &path_world) :
465         m_map(map),
466         m_script(scriptIface),
467         m_gamedef(gamedef),
468         m_path_world(path_world),
469         m_send_recommended_timer(0),
470         m_active_block_interval_overload_skip(0),
471         m_game_time(0),
472         m_game_time_fraction_counter(0),
473         m_last_clear_objects_time(0),
474         m_recommended_send_interval(0.1),
475         m_max_lag_estimate(0.1)
476 {
477 }
478
479 ServerEnvironment::~ServerEnvironment()
480 {
481         // Clear active block list.
482         // This makes the next one delete all active objects.
483         m_active_blocks.clear();
484
485         // Convert all objects to static and delete the active objects
486         deactivateFarObjects(true);
487
488         // Drop/delete map
489         m_map->drop();
490
491         // Delete ActiveBlockModifiers
492         for (std::vector<ABMWithState>::iterator
493                         i = m_abms.begin(); i != m_abms.end(); ++i){
494                 delete i->abm;
495         }
496
497         // Deallocate players
498         for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
499                         i != m_players.end(); ++i) {
500                 delete (*i);
501         }
502 }
503
504 Map & ServerEnvironment::getMap()
505 {
506         return *m_map;
507 }
508
509 ServerMap & ServerEnvironment::getServerMap()
510 {
511         return *m_map;
512 }
513
514 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
515 {
516         for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
517                         i != m_players.end(); ++i) {
518                 RemotePlayer *player = *i;
519                 if (player->peer_id == peer_id)
520                         return player;
521         }
522         return NULL;
523 }
524
525 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
526 {
527         for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
528                         i != m_players.end(); ++i) {
529                 RemotePlayer *player = *i;
530                 if (strcmp(player->getName(), name) == 0)
531                         return player;
532         }
533         return NULL;
534 }
535
536 void ServerEnvironment::addPlayer(RemotePlayer *player)
537 {
538         DSTACK(FUNCTION_NAME);
539         /*
540                 Check that peer_ids are unique.
541                 Also check that names are unique.
542                 Exception: there can be multiple players with peer_id=0
543         */
544         // If peer id is non-zero, it has to be unique.
545         if (player->peer_id != 0)
546                 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
547         // Name has to be unique.
548         FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
549         // Add.
550         m_players.push_back(player);
551 }
552
553 void ServerEnvironment::removePlayer(RemotePlayer *player)
554 {
555         for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
556                         it != m_players.end(); ++it) {
557                 if ((*it) == player) {
558                         delete *it;
559                         m_players.erase(it);
560                         return;
561                 }
562         }
563 }
564
565 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
566 {
567         float distance = pos1.getDistanceFrom(pos2);
568
569         //calculate normalized direction vector
570         v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
571                                 (pos2.Y - pos1.Y)/distance,
572                                 (pos2.Z - pos1.Z)/distance);
573
574         //find out if there's a node on path between pos1 and pos2
575         for (float i = 1; i < distance; i += stepsize) {
576                 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
577                                 normalized_vector.Y * i,
578                                 normalized_vector.Z * i) +pos1,BS);
579
580                 MapNode n = getMap().getNodeNoEx(pos);
581
582                 if(n.param0 != CONTENT_AIR) {
583                         if (p) {
584                                 *p = pos;
585                         }
586                         return false;
587                 }
588         }
589         return true;
590 }
591
592 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
593                 const std::string &str_reason, bool reconnect)
594 {
595         for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
596                         it != m_players.end(); ++it) {
597                 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
598                 ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id,
599                                 player->protocol_version, reason, str_reason, reconnect);
600         }
601 }
602
603 void ServerEnvironment::saveLoadedPlayers()
604 {
605         std::string players_path = m_path_world + DIR_DELIM "players";
606         fs::CreateDir(players_path);
607
608         for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
609                         it != m_players.end();
610                         ++it) {
611                 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
612                 if (player->checkModified()) {
613                         player->save(players_path, m_gamedef);
614                 }
615         }
616 }
617
618 void ServerEnvironment::savePlayer(RemotePlayer *player)
619 {
620         std::string players_path = m_path_world + DIR_DELIM "players";
621         fs::CreateDir(players_path);
622
623         player->save(players_path, m_gamedef);
624 }
625
626 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername)
627 {
628         bool newplayer = false;
629         bool found = false;
630         std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
631         std::string path = players_path + playername;
632
633         RemotePlayer *player = getPlayer(playername.c_str());
634         if (!player) {
635                 player = new RemotePlayer("", m_gamedef->idef());
636                 newplayer = true;
637         }
638
639         for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
640                 //// Open file and deserialize
641                 std::ifstream is(path.c_str(), std::ios_base::binary);
642                 if (!is.good())
643                         continue;
644                 player->deSerialize(is, path);
645                 is.close();
646
647                 if (player->getName() == playername) {
648                         found = true;
649                         break;
650                 }
651
652                 path = players_path + playername + itos(i);
653         }
654
655         if (!found) {
656                 infostream << "Player file for player " << playername
657                                 << " not found" << std::endl;
658                 if (newplayer)
659                         delete player;
660                 return NULL;
661         }
662
663         if (newplayer)
664                 addPlayer(player);
665         player->setModified(false);
666         return player;
667 }
668
669 void ServerEnvironment::saveMeta()
670 {
671         std::string path = m_path_world + DIR_DELIM "env_meta.txt";
672
673         // Open file and serialize
674         std::ostringstream ss(std::ios_base::binary);
675
676         Settings args;
677         args.setU64("game_time", m_game_time);
678         args.setU64("time_of_day", getTimeOfDay());
679         args.setU64("last_clear_objects_time", m_last_clear_objects_time);
680         args.setU64("lbm_introduction_times_version", 1);
681         args.set("lbm_introduction_times",
682                 m_lbm_mgr.createIntroductionTimesString());
683         args.setU64("day_count", m_day_count);
684         args.writeLines(ss);
685         ss<<"EnvArgsEnd\n";
686
687         if(!fs::safeWriteToFile(path, ss.str()))
688         {
689                 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
690                                 <<path<<std::endl;
691                 throw SerializationError("Couldn't save env meta");
692         }
693 }
694
695 void ServerEnvironment::loadMeta()
696 {
697         std::string path = m_path_world + DIR_DELIM "env_meta.txt";
698
699         // Open file and deserialize
700         std::ifstream is(path.c_str(), std::ios_base::binary);
701         if (!is.good()) {
702                 infostream << "ServerEnvironment::loadMeta(): Failed to open "
703                                 << path << std::endl;
704                 throw SerializationError("Couldn't load env meta");
705         }
706
707         Settings args;
708
709         if (!args.parseConfigLines(is, "EnvArgsEnd")) {
710                 throw SerializationError("ServerEnvironment::loadMeta(): "
711                                 "EnvArgsEnd not found!");
712         }
713
714         try {
715                 m_game_time = args.getU64("game_time");
716         } catch (SettingNotFoundException &e) {
717                 // Getting this is crucial, otherwise timestamps are useless
718                 throw SerializationError("Couldn't load env meta game_time");
719         }
720
721         setTimeOfDay(args.exists("time_of_day") ?
722                 // set day to morning by default
723                 args.getU64("time_of_day") : 9000);
724
725         m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
726                 // If missing, do as if clearObjects was never called
727                 args.getU64("last_clear_objects_time") : 0;
728
729         std::string lbm_introduction_times = "";
730         try {
731                 u64 ver = args.getU64("lbm_introduction_times_version");
732                 if (ver == 1) {
733                         lbm_introduction_times = args.get("lbm_introduction_times");
734                 } else {
735                         infostream << "ServerEnvironment::loadMeta(): Non-supported"
736                                 << " introduction time version " << ver << std::endl;
737                 }
738         } catch (SettingNotFoundException &e) {
739                 // No problem, this is expected. Just continue with an empty string
740         }
741         m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
742
743         m_day_count = args.exists("day_count") ?
744                 args.getU64("day_count") : 0;
745 }
746
747 void ServerEnvironment::loadDefaultMeta()
748 {
749         m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
750 }
751
752 struct ActiveABM
753 {
754         ActiveBlockModifier *abm;
755         int chance;
756         std::set<content_t> required_neighbors;
757 };
758
759 class ABMHandler
760 {
761 private:
762         ServerEnvironment *m_env;
763         std::map<content_t, std::vector<ActiveABM> > m_aabms;
764 public:
765         ABMHandler(std::vector<ABMWithState> &abms,
766                         float dtime_s, ServerEnvironment *env,
767                         bool use_timers):
768                 m_env(env)
769         {
770                 if(dtime_s < 0.001)
771                         return;
772                 INodeDefManager *ndef = env->getGameDef()->ndef();
773                 for(std::vector<ABMWithState>::iterator
774                                 i = abms.begin(); i != abms.end(); ++i) {
775                         ActiveBlockModifier *abm = i->abm;
776                         float trigger_interval = abm->getTriggerInterval();
777                         if(trigger_interval < 0.001)
778                                 trigger_interval = 0.001;
779                         float actual_interval = dtime_s;
780                         if(use_timers){
781                                 i->timer += dtime_s;
782                                 if(i->timer < trigger_interval)
783                                         continue;
784                                 i->timer -= trigger_interval;
785                                 actual_interval = trigger_interval;
786                         }
787                         float chance = abm->getTriggerChance();
788                         if(chance == 0)
789                                 chance = 1;
790                         ActiveABM aabm;
791                         aabm.abm = abm;
792                         if(abm->getSimpleCatchUp()) {
793                                 float intervals = actual_interval / trigger_interval;
794                                 if(intervals == 0)
795                                         continue;
796                                 aabm.chance = chance / intervals;
797                                 if(aabm.chance == 0)
798                                         aabm.chance = 1;
799                         } else {
800                                 aabm.chance = chance;
801                         }
802                         // Trigger neighbors
803                         std::set<std::string> required_neighbors_s
804                                         = abm->getRequiredNeighbors();
805                         for(std::set<std::string>::iterator
806                                         i = required_neighbors_s.begin();
807                                         i != required_neighbors_s.end(); ++i)
808                         {
809                                 ndef->getIds(*i, aabm.required_neighbors);
810                         }
811                         // Trigger contents
812                         std::set<std::string> contents_s = abm->getTriggerContents();
813                         for(std::set<std::string>::iterator
814                                         i = contents_s.begin(); i != contents_s.end(); ++i)
815                         {
816                                 std::set<content_t> ids;
817                                 ndef->getIds(*i, ids);
818                                 for(std::set<content_t>::const_iterator k = ids.begin();
819                                                 k != ids.end(); ++k)
820                                 {
821                                         content_t c = *k;
822                                         std::map<content_t, std::vector<ActiveABM> >::iterator j;
823                                         j = m_aabms.find(c);
824                                         if(j == m_aabms.end()){
825                                                 std::vector<ActiveABM> aabmlist;
826                                                 m_aabms[c] = aabmlist;
827                                                 j = m_aabms.find(c);
828                                         }
829                                         j->second.push_back(aabm);
830                                 }
831                         }
832                 }
833         }
834         // Find out how many objects the given block and its neighbours contain.
835         // Returns the number of objects in the block, and also in 'wider' the
836         // number of objects in the block and all its neighbours. The latter
837         // may an estimate if any neighbours are unloaded.
838         u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
839         {
840                 wider = 0;
841                 u32 wider_unknown_count = 0;
842                 for(s16 x=-1; x<=1; x++)
843                 for(s16 y=-1; y<=1; y++)
844                 for(s16 z=-1; z<=1; z++)
845                 {
846                         MapBlock *block2 = map->getBlockNoCreateNoEx(
847                                         block->getPos() + v3s16(x,y,z));
848                         if(block2==NULL){
849                                 wider_unknown_count++;
850                                 continue;
851                         }
852                         wider += block2->m_static_objects.m_active.size()
853                                         + block2->m_static_objects.m_stored.size();
854                 }
855                 // Extrapolate
856                 u32 active_object_count = block->m_static_objects.m_active.size();
857                 u32 wider_known_count = 3*3*3 - wider_unknown_count;
858                 wider += wider_unknown_count * wider / wider_known_count;
859                 return active_object_count;
860
861         }
862         void apply(MapBlock *block)
863         {
864                 if(m_aabms.empty())
865                         return;
866
867                 ServerMap *map = &m_env->getServerMap();
868
869                 u32 active_object_count_wider;
870                 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
871                 m_env->m_added_objects = 0;
872
873                 v3s16 p0;
874                 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
875                 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
876                 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
877                 {
878                         MapNode n = block->getNodeNoEx(p0);
879                         content_t c = n.getContent();
880                         v3s16 p = p0 + block->getPosRelative();
881
882                         std::map<content_t, std::vector<ActiveABM> >::iterator j;
883                         j = m_aabms.find(c);
884                         if(j == m_aabms.end())
885                                 continue;
886
887                         for(std::vector<ActiveABM>::iterator
888                                         i = j->second.begin(); i != j->second.end(); ++i) {
889                                 if(myrand() % i->chance != 0)
890                                         continue;
891
892                                 // Check neighbors
893                                 if(!i->required_neighbors.empty())
894                                 {
895                                         v3s16 p1;
896                                         for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
897                                         for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
898                                         for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
899                                         {
900                                                 if(p1 == p)
901                                                         continue;
902                                                 MapNode n = map->getNodeNoEx(p1);
903                                                 content_t c = n.getContent();
904                                                 std::set<content_t>::const_iterator k;
905                                                 k = i->required_neighbors.find(c);
906                                                 if(k != i->required_neighbors.end()){
907                                                         goto neighbor_found;
908                                                 }
909                                         }
910                                         // No required neighbor found
911                                         continue;
912                                 }
913 neighbor_found:
914
915                                 // Call all the trigger variations
916                                 i->abm->trigger(m_env, p, n);
917                                 i->abm->trigger(m_env, p, n,
918                                                 active_object_count, active_object_count_wider);
919
920                                 // Count surrounding objects again if the abms added any
921                                 if(m_env->m_added_objects > 0) {
922                                         active_object_count = countObjects(block, map, active_object_count_wider);
923                                         m_env->m_added_objects = 0;
924                                 }
925                         }
926                 }
927         }
928 };
929
930 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
931 {
932         // Reset usage timer immediately, otherwise a block that becomes active
933         // again at around the same time as it would normally be unloaded will
934         // get unloaded incorrectly. (I think this still leaves a small possibility
935         // of a race condition between this and server::AsyncRunStep, which only
936         // some kind of synchronisation will fix, but it at least reduces the window
937         // of opportunity for it to break from seconds to nanoseconds)
938         block->resetUsageTimer();
939
940         // Get time difference
941         u32 dtime_s = 0;
942         u32 stamp = block->getTimestamp();
943         if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
944                 dtime_s = m_game_time - stamp;
945         dtime_s += additional_dtime;
946
947         /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
948                         <<stamp<<", game time: "<<m_game_time<<std::endl;*/
949
950         // Remove stored static objects if clearObjects was called since block's timestamp
951         if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
952                 block->m_static_objects.m_stored.clear();
953                 // do not set changed flag to avoid unnecessary mapblock writes
954         }
955
956         // Set current time as timestamp
957         block->setTimestampNoChangedFlag(m_game_time);
958
959         /*infostream<<"ServerEnvironment::activateBlock(): block is "
960                         <<dtime_s<<" seconds old."<<std::endl;*/
961
962         // Activate stored objects
963         activateObjects(block, dtime_s);
964
965         /* Handle LoadingBlockModifiers */
966         m_lbm_mgr.applyLBMs(this, block, stamp);
967
968         // Run node timers
969         std::vector<NodeTimer> elapsed_timers =
970                 block->m_node_timers.step((float)dtime_s);
971         if (!elapsed_timers.empty()) {
972                 MapNode n;
973                 for (std::vector<NodeTimer>::iterator
974                                 i = elapsed_timers.begin();
975                                 i != elapsed_timers.end(); ++i){
976                         n = block->getNodeNoEx(i->position);
977                         v3s16 p = i->position + block->getPosRelative();
978                         if (m_script->node_on_timer(p, n, i->elapsed))
979                                 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
980                 }
981         }
982
983         /* Handle ActiveBlockModifiers */
984         ABMHandler abmhandler(m_abms, dtime_s, this, false);
985         abmhandler.apply(block);
986 }
987
988 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
989 {
990         m_abms.push_back(ABMWithState(abm));
991 }
992
993 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
994 {
995         m_lbm_mgr.addLBMDef(lbm);
996 }
997
998 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
999 {
1000         INodeDefManager *ndef = m_gamedef->ndef();
1001         MapNode n_old = m_map->getNodeNoEx(p);
1002
1003         // Call destructor
1004         if (ndef->get(n_old).has_on_destruct)
1005                 m_script->node_on_destruct(p, n_old);
1006
1007         // Replace node
1008         if (!m_map->addNodeWithEvent(p, n))
1009                 return false;
1010
1011         // Update active VoxelManipulator if a mapgen thread
1012         m_map->updateVManip(p);
1013
1014         // Call post-destructor
1015         if (ndef->get(n_old).has_after_destruct)
1016                 m_script->node_after_destruct(p, n_old);
1017
1018         // Call constructor
1019         if (ndef->get(n).has_on_construct)
1020                 m_script->node_on_construct(p, n);
1021
1022         return true;
1023 }
1024
1025 bool ServerEnvironment::removeNode(v3s16 p)
1026 {
1027         INodeDefManager *ndef = m_gamedef->ndef();
1028         MapNode n_old = m_map->getNodeNoEx(p);
1029
1030         // Call destructor
1031         if (ndef->get(n_old).has_on_destruct)
1032                 m_script->node_on_destruct(p, n_old);
1033
1034         // Replace with air
1035         // This is slightly optimized compared to addNodeWithEvent(air)
1036         if (!m_map->removeNodeWithEvent(p))
1037                 return false;
1038
1039         // Update active VoxelManipulator if a mapgen thread
1040         m_map->updateVManip(p);
1041
1042         // Call post-destructor
1043         if (ndef->get(n_old).has_after_destruct)
1044                 m_script->node_after_destruct(p, n_old);
1045
1046         // Air doesn't require constructor
1047         return true;
1048 }
1049
1050 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1051 {
1052         if (!m_map->addNodeWithEvent(p, n, false))
1053                 return false;
1054
1055         // Update active VoxelManipulator if a mapgen thread
1056         m_map->updateVManip(p);
1057
1058         return true;
1059 }
1060
1061 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1062 {
1063         for (ActiveObjectMap::iterator i = m_active_objects.begin();
1064                         i != m_active_objects.end(); ++i) {
1065                 ServerActiveObject* obj = i->second;
1066                 u16 id = i->first;
1067                 v3f objectpos = obj->getBasePosition();
1068                 if (objectpos.getDistanceFrom(pos) > radius)
1069                         continue;
1070                 objects.push_back(id);
1071         }
1072 }
1073
1074 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1075 {
1076         infostream << "ServerEnvironment::clearObjects(): "
1077                 << "Removing all active objects" << std::endl;
1078         std::vector<u16> objects_to_remove;
1079         for (ActiveObjectMap::iterator i = m_active_objects.begin();
1080                         i != m_active_objects.end(); ++i) {
1081                 ServerActiveObject* obj = i->second;
1082                 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1083                         continue;
1084                 u16 id = i->first;
1085                 // Delete static object if block is loaded
1086                 if (obj->m_static_exists) {
1087                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1088                         if (block) {
1089                                 block->m_static_objects.remove(id);
1090                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1091                                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1092                                 obj->m_static_exists = false;
1093                         }
1094                 }
1095                 // If known by some client, don't delete immediately
1096                 if (obj->m_known_by_count > 0) {
1097                         obj->m_pending_deactivation = true;
1098                         obj->m_removed = true;
1099                         continue;
1100                 }
1101
1102                 // Tell the object about removal
1103                 obj->removingFromEnvironment();
1104                 // Deregister in scripting api
1105                 m_script->removeObjectReference(obj);
1106
1107                 // Delete active object
1108                 if (obj->environmentDeletes())
1109                         delete obj;
1110                 // Id to be removed from m_active_objects
1111                 objects_to_remove.push_back(id);
1112         }
1113
1114         // Remove references from m_active_objects
1115         for (std::vector<u16>::iterator i = objects_to_remove.begin();
1116                         i != objects_to_remove.end(); ++i) {
1117                 m_active_objects.erase(*i);
1118         }
1119
1120         // Get list of loaded blocks
1121         std::vector<v3s16> loaded_blocks;
1122         infostream << "ServerEnvironment::clearObjects(): "
1123                 << "Listing all loaded blocks" << std::endl;
1124         m_map->listAllLoadedBlocks(loaded_blocks);
1125         infostream << "ServerEnvironment::clearObjects(): "
1126                 << "Done listing all loaded blocks: "
1127                 << loaded_blocks.size()<<std::endl;
1128
1129         // Get list of loadable blocks
1130         std::vector<v3s16> loadable_blocks;
1131         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1132                 infostream << "ServerEnvironment::clearObjects(): "
1133                         << "Listing all loadable blocks" << std::endl;
1134                 m_map->listAllLoadableBlocks(loadable_blocks);
1135                 infostream << "ServerEnvironment::clearObjects(): "
1136                         << "Done listing all loadable blocks: "
1137                         << loadable_blocks.size() << std::endl;
1138         } else {
1139                 loadable_blocks = loaded_blocks;
1140         }
1141
1142         infostream << "ServerEnvironment::clearObjects(): "
1143                 << "Now clearing objects in " << loadable_blocks.size()
1144                 << " blocks" << std::endl;
1145
1146         // Grab a reference on each loaded block to avoid unloading it
1147         for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1148                         i != loaded_blocks.end(); ++i) {
1149                 v3s16 p = *i;
1150                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1151                 assert(block != NULL);
1152                 block->refGrab();
1153         }
1154
1155         // Remove objects in all loadable blocks
1156         u32 unload_interval = U32_MAX;
1157         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1158                 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1159                 unload_interval = MYMAX(unload_interval, 1);
1160         }
1161         u32 report_interval = loadable_blocks.size() / 10;
1162         u32 num_blocks_checked = 0;
1163         u32 num_blocks_cleared = 0;
1164         u32 num_objs_cleared = 0;
1165         for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1166                         i != loadable_blocks.end(); ++i) {
1167                 v3s16 p = *i;
1168                 MapBlock *block = m_map->emergeBlock(p, false);
1169                 if (!block) {
1170                         errorstream << "ServerEnvironment::clearObjects(): "
1171                                 << "Failed to emerge block " << PP(p) << std::endl;
1172                         continue;
1173                 }
1174                 u32 num_stored = block->m_static_objects.m_stored.size();
1175                 u32 num_active = block->m_static_objects.m_active.size();
1176                 if (num_stored != 0 || num_active != 0) {
1177                         block->m_static_objects.m_stored.clear();
1178                         block->m_static_objects.m_active.clear();
1179                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1180                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1181                         num_objs_cleared += num_stored + num_active;
1182                         num_blocks_cleared++;
1183                 }
1184                 num_blocks_checked++;
1185
1186                 if (report_interval != 0 &&
1187                                 num_blocks_checked % report_interval == 0) {
1188                         float percent = 100.0 * (float)num_blocks_checked /
1189                                 loadable_blocks.size();
1190                         infostream << "ServerEnvironment::clearObjects(): "
1191                                 << "Cleared " << num_objs_cleared << " objects"
1192                                 << " in " << num_blocks_cleared << " blocks ("
1193                                 << percent << "%)" << std::endl;
1194                 }
1195                 if (num_blocks_checked % unload_interval == 0) {
1196                         m_map->unloadUnreferencedBlocks();
1197                 }
1198         }
1199         m_map->unloadUnreferencedBlocks();
1200
1201         // Drop references that were added above
1202         for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1203                         i != loaded_blocks.end(); ++i) {
1204                 v3s16 p = *i;
1205                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1206                 assert(block);
1207                 block->refDrop();
1208         }
1209
1210         m_last_clear_objects_time = m_game_time;
1211
1212         infostream << "ServerEnvironment::clearObjects(): "
1213                 << "Finished: Cleared " << num_objs_cleared << " objects"
1214                 << " in " << num_blocks_cleared << " blocks" << std::endl;
1215 }
1216
1217 void ServerEnvironment::step(float dtime)
1218 {
1219         DSTACK(FUNCTION_NAME);
1220
1221         //TimeTaker timer("ServerEnv step");
1222
1223         /* Step time of day */
1224         stepTimeOfDay(dtime);
1225
1226         // Update this one
1227         // NOTE: This is kind of funny on a singleplayer game, but doesn't
1228         // really matter that much.
1229         static const float server_step = g_settings->getFloat("dedicated_server_step");
1230         m_recommended_send_interval = server_step;
1231
1232         /*
1233                 Increment game time
1234         */
1235         {
1236                 m_game_time_fraction_counter += dtime;
1237                 u32 inc_i = (u32)m_game_time_fraction_counter;
1238                 m_game_time += inc_i;
1239                 m_game_time_fraction_counter -= (float)inc_i;
1240         }
1241
1242         /*
1243                 Handle players
1244         */
1245         {
1246                 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1247                 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1248                                 i != m_players.end(); ++i) {
1249                         RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1250                         assert(player);
1251
1252                         // Ignore disconnected players
1253                         if(player->peer_id == 0)
1254                                 continue;
1255
1256                         // Move
1257                         player->move(dtime, this, 100*BS);
1258                 }
1259         }
1260
1261         /*
1262                 Manage active block list
1263         */
1264         if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1265                 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1266                 /*
1267                         Get player block positions
1268                 */
1269                 std::vector<v3s16> players_blockpos;
1270                 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1271                                 i != m_players.end(); ++i) {
1272                         RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1273                         assert(player);
1274                         // Ignore disconnected players
1275                         if (player->peer_id == 0)
1276                                 continue;
1277
1278                         v3s16 blockpos = getNodeBlockPos(
1279                                         floatToInt(player->getPosition(), BS));
1280                         players_blockpos.push_back(blockpos);
1281                 }
1282
1283                 /*
1284                         Update list of active blocks, collecting changes
1285                 */
1286                 static const s16 active_block_range = g_settings->getS16("active_block_range");
1287                 std::set<v3s16> blocks_removed;
1288                 std::set<v3s16> blocks_added;
1289                 m_active_blocks.update(players_blockpos, active_block_range,
1290                                 blocks_removed, blocks_added);
1291
1292                 /*
1293                         Handle removed blocks
1294                 */
1295
1296                 // Convert active objects that are no more in active blocks to static
1297                 deactivateFarObjects(false);
1298
1299                 for(std::set<v3s16>::iterator
1300                                 i = blocks_removed.begin();
1301                                 i != blocks_removed.end(); ++i) {
1302                         v3s16 p = *i;
1303
1304                         /* infostream<<"Server: Block " << PP(p)
1305                                 << " became inactive"<<std::endl; */
1306
1307                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1308                         if(block==NULL)
1309                                 continue;
1310
1311                         // Set current time as timestamp (and let it set ChangedFlag)
1312                         block->setTimestamp(m_game_time);
1313                 }
1314
1315                 /*
1316                         Handle added blocks
1317                 */
1318
1319                 for(std::set<v3s16>::iterator
1320                                 i = blocks_added.begin();
1321                                 i != blocks_added.end(); ++i)
1322                 {
1323                         v3s16 p = *i;
1324
1325                         MapBlock *block = m_map->getBlockOrEmerge(p);
1326                         if(block==NULL){
1327                                 m_active_blocks.m_list.erase(p);
1328                                 continue;
1329                         }
1330
1331                         activateBlock(block);
1332                         /* infostream<<"Server: Block " << PP(p)
1333                                 << " became active"<<std::endl; */
1334                 }
1335         }
1336
1337         /*
1338                 Mess around in active blocks
1339         */
1340         if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1341                 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1342
1343                 float dtime = m_cache_nodetimer_interval;
1344
1345                 for(std::set<v3s16>::iterator
1346                                 i = m_active_blocks.m_list.begin();
1347                                 i != m_active_blocks.m_list.end(); ++i)
1348                 {
1349                         v3s16 p = *i;
1350
1351                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1352                                         <<") being handled"<<std::endl;*/
1353
1354                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1355                         if(block==NULL)
1356                                 continue;
1357
1358                         // Reset block usage timer
1359                         block->resetUsageTimer();
1360
1361                         // Set current time as timestamp
1362                         block->setTimestampNoChangedFlag(m_game_time);
1363                         // If time has changed much from the one on disk,
1364                         // set block to be saved when it is unloaded
1365                         if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1366                                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1367                                         MOD_REASON_BLOCK_EXPIRED);
1368
1369                         // Run node timers
1370                         std::vector<NodeTimer> elapsed_timers =
1371                                 block->m_node_timers.step((float)dtime);
1372                         if (!elapsed_timers.empty()) {
1373                                 MapNode n;
1374                                 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1375                                                 i != elapsed_timers.end(); ++i) {
1376                                         n = block->getNodeNoEx(i->position);
1377                                         p = i->position + block->getPosRelative();
1378                                         if (m_script->node_on_timer(p, n, i->elapsed)) {
1379                                                 block->setNodeTimer(NodeTimer(
1380                                                         i->timeout, 0, i->position));
1381                                         }
1382                                 }
1383                         }
1384                 }
1385         }
1386
1387         if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1388         do{ // breakable
1389                 if(m_active_block_interval_overload_skip > 0){
1390                         ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1391                         m_active_block_interval_overload_skip--;
1392                         break;
1393                 }
1394                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1395                 TimeTaker timer("modify in active blocks per interval");
1396
1397                 // Initialize handling of ActiveBlockModifiers
1398                 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1399
1400                 for(std::set<v3s16>::iterator
1401                                 i = m_active_blocks.m_list.begin();
1402                                 i != m_active_blocks.m_list.end(); ++i)
1403                 {
1404                         v3s16 p = *i;
1405
1406                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1407                                         <<") being handled"<<std::endl;*/
1408
1409                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1410                         if(block == NULL)
1411                                 continue;
1412
1413                         // Set current time as timestamp
1414                         block->setTimestampNoChangedFlag(m_game_time);
1415
1416                         /* Handle ActiveBlockModifiers */
1417                         abmhandler.apply(block);
1418                 }
1419
1420                 u32 time_ms = timer.stop(true);
1421                 u32 max_time_ms = 200;
1422                 if(time_ms > max_time_ms){
1423                         warningstream<<"active block modifiers took "
1424                                         <<time_ms<<"ms (longer than "
1425                                         <<max_time_ms<<"ms)"<<std::endl;
1426                         m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1427                 }
1428         }while(0);
1429
1430         /*
1431                 Step script environment (run global on_step())
1432         */
1433         m_script->environment_Step(dtime);
1434
1435         /*
1436                 Step active objects
1437         */
1438         {
1439                 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1440                 //TimeTaker timer("Step active objects");
1441
1442                 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1443
1444                 // This helps the objects to send data at the same time
1445                 bool send_recommended = false;
1446                 m_send_recommended_timer += dtime;
1447                 if(m_send_recommended_timer > getSendRecommendedInterval())
1448                 {
1449                         m_send_recommended_timer -= getSendRecommendedInterval();
1450                         send_recommended = true;
1451                 }
1452
1453                 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1454                                 i != m_active_objects.end(); ++i) {
1455                         ServerActiveObject* obj = i->second;
1456                         // Don't step if is to be removed or stored statically
1457                         if(obj->m_removed || obj->m_pending_deactivation)
1458                                 continue;
1459                         // Step object
1460                         obj->step(dtime, send_recommended);
1461                         // Read messages from object
1462                         while(!obj->m_messages_out.empty())
1463                         {
1464                                 m_active_object_messages.push(
1465                                                 obj->m_messages_out.front());
1466                                 obj->m_messages_out.pop();
1467                         }
1468                 }
1469         }
1470
1471         /*
1472                 Manage active objects
1473         */
1474         if(m_object_management_interval.step(dtime, 0.5))
1475         {
1476                 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1477                 /*
1478                         Remove objects that satisfy (m_removed && m_known_by_count==0)
1479                 */
1480                 removeRemovedObjects();
1481         }
1482
1483         /*
1484                 Manage particle spawner expiration
1485         */
1486         if (m_particle_management_interval.step(dtime, 1.0)) {
1487                 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1488                                 i != m_particle_spawners.end(); ) {
1489                         //non expiring spawners
1490                         if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1491                                 ++i;
1492                                 continue;
1493                         }
1494
1495                         i->second -= 1.0f;
1496                         if (i->second <= 0.f)
1497                                 m_particle_spawners.erase(i++);
1498                         else
1499                                 ++i;
1500                 }
1501         }
1502 }
1503
1504 u32 ServerEnvironment::addParticleSpawner(float exptime)
1505 {
1506         // Timers with lifetime 0 do not expire
1507         float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1508
1509         u32 id = 0;
1510         for (;;) { // look for unused particlespawner id
1511                 id++;
1512                 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1513                 if (f == m_particle_spawners.end()) {
1514                         m_particle_spawners[id] = time;
1515                         break;
1516                 }
1517         }
1518         return id;
1519 }
1520
1521 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1522 {
1523         u32 id = addParticleSpawner(exptime);
1524         m_particle_spawner_attachments[id] = attached_id;
1525         if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1526                 obj->attachParticleSpawner(id);
1527         }
1528         return id;
1529 }
1530
1531 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1532 {
1533         m_particle_spawners.erase(id);
1534         UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1535         if (it != m_particle_spawner_attachments.end()) {
1536                 u16 obj_id = (*it).second;
1537                 ServerActiveObject *sao = getActiveObject(obj_id);
1538                 if (sao != NULL && remove_from_object) {
1539                         sao->detachParticleSpawner(id);
1540                 }
1541                 m_particle_spawner_attachments.erase(id);
1542         }
1543 }
1544
1545 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1546 {
1547         ActiveObjectMap::iterator n = m_active_objects.find(id);
1548         return (n != m_active_objects.end() ? n->second : NULL);
1549 }
1550
1551 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1552 {
1553         if (id == 0)
1554                 return false;
1555
1556         return objects.find(id) == objects.end();
1557 }
1558
1559 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1560 {
1561         //try to reuse id's as late as possible
1562         static u16 last_used_id = 0;
1563         u16 startid = last_used_id;
1564         for(;;)
1565         {
1566                 last_used_id ++;
1567                 if(isFreeServerActiveObjectId(last_used_id, objects))
1568                         return last_used_id;
1569
1570                 if(last_used_id == startid)
1571                         return 0;
1572         }
1573 }
1574
1575 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1576 {
1577         assert(object); // Pre-condition
1578         m_added_objects++;
1579         u16 id = addActiveObjectRaw(object, true, 0);
1580         return id;
1581 }
1582
1583 /*
1584         Finds out what new objects have been added to
1585         inside a radius around a position
1586 */
1587 void ServerEnvironment::getAddedActiveObjects(RemotePlayer *player, s16 radius,
1588                 s16 player_radius,
1589                 std::set<u16> &current_objects,
1590                 std::queue<u16> &added_objects)
1591 {
1592         f32 radius_f = radius * BS;
1593         f32 player_radius_f = player_radius * BS;
1594
1595         if (player_radius_f < 0)
1596                 player_radius_f = 0;
1597
1598         /*
1599                 Go through the object list,
1600                 - discard m_removed objects,
1601                 - discard objects that are too far away,
1602                 - discard objects that are found in current_objects.
1603                 - add remaining objects to added_objects
1604         */
1605         for(ActiveObjectMap::iterator i = m_active_objects.begin();
1606                         i != m_active_objects.end(); ++i) {
1607                 u16 id = i->first;
1608
1609                 // Get object
1610                 ServerActiveObject *object = i->second;
1611                 if(object == NULL)
1612                         continue;
1613
1614                 // Discard if removed or deactivating
1615                 if(object->m_removed || object->m_pending_deactivation)
1616                         continue;
1617
1618                 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1619                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1620                         // Discard if too far
1621                         if (distance_f > player_radius_f && player_radius_f != 0)
1622                                 continue;
1623                 } else if (distance_f > radius_f)
1624                         continue;
1625
1626                 // Discard if already on current_objects
1627                 std::set<u16>::iterator n;
1628                 n = current_objects.find(id);
1629                 if(n != current_objects.end())
1630                         continue;
1631                 // Add to added_objects
1632                 added_objects.push(id);
1633         }
1634 }
1635
1636 /*
1637         Finds out what objects have been removed from
1638         inside a radius around a position
1639 */
1640 void ServerEnvironment::getRemovedActiveObjects(RemotePlayer *player, s16 radius,
1641                 s16 player_radius,
1642                 std::set<u16> &current_objects,
1643                 std::queue<u16> &removed_objects)
1644 {
1645         f32 radius_f = radius * BS;
1646         f32 player_radius_f = player_radius * BS;
1647
1648         if (player_radius_f < 0)
1649                 player_radius_f = 0;
1650
1651         /*
1652                 Go through current_objects; object is removed if:
1653                 - object is not found in m_active_objects (this is actually an
1654                   error condition; objects should be set m_removed=true and removed
1655                   only after all clients have been informed about removal), or
1656                 - object has m_removed=true, or
1657                 - object is too far away
1658         */
1659         for(std::set<u16>::iterator
1660                         i = current_objects.begin();
1661                         i != current_objects.end(); ++i)
1662         {
1663                 u16 id = *i;
1664                 ServerActiveObject *object = getActiveObject(id);
1665
1666                 if (object == NULL) {
1667                         infostream << "ServerEnvironment::getRemovedActiveObjects():"
1668                                 << " object in current_objects is NULL" << std::endl;
1669                         removed_objects.push(id);
1670                         continue;
1671                 }
1672
1673                 if (object->m_removed || object->m_pending_deactivation) {
1674                         removed_objects.push(id);
1675                         continue;
1676                 }
1677
1678                 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1679                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1680                         if (distance_f <= player_radius_f || player_radius_f == 0)
1681                                 continue;
1682                 } else if (distance_f <= radius_f)
1683                         continue;
1684
1685                 // Object is no longer visible
1686                 removed_objects.push(id);
1687         }
1688 }
1689
1690 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1691         v3s16 blockpos, bool static_exists, v3s16 static_block)
1692 {
1693         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1694         if (!block)
1695                 return;
1696
1697         for (std::map<u16, StaticObject>::iterator
1698                         so_it = block->m_static_objects.m_active.begin();
1699                         so_it != block->m_static_objects.m_active.end(); ++so_it) {
1700                 // Get the ServerActiveObject counterpart to this StaticObject
1701                 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1702                 if (ao_it == m_active_objects.end()) {
1703                         // If this ever happens, there must be some kind of nasty bug.
1704                         errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1705                                 "Object from MapBlock::m_static_objects::m_active not found "
1706                                 "in m_active_objects";
1707                         continue;
1708                 }
1709
1710                 ServerActiveObject *sao = ao_it->second;
1711                 sao->m_static_exists = static_exists;
1712                 sao->m_static_block  = static_block;
1713         }
1714 }
1715
1716 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1717 {
1718         if(m_active_object_messages.empty())
1719                 return ActiveObjectMessage(0);
1720
1721         ActiveObjectMessage message = m_active_object_messages.front();
1722         m_active_object_messages.pop();
1723         return message;
1724 }
1725
1726 /*
1727         ************ Private methods *************
1728 */
1729
1730 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1731                 bool set_changed, u32 dtime_s)
1732 {
1733         assert(object); // Pre-condition
1734         if(object->getId() == 0){
1735                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1736                 if(new_id == 0)
1737                 {
1738                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1739                                         <<"no free ids available"<<std::endl;
1740                         if(object->environmentDeletes())
1741                                 delete object;
1742                         return 0;
1743                 }
1744                 object->setId(new_id);
1745         }
1746         else{
1747                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1748                                 <<"supplied with id "<<object->getId()<<std::endl;
1749         }
1750
1751         if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1752                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1753                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1754                 if(object->environmentDeletes())
1755                         delete object;
1756                 return 0;
1757         }
1758
1759         if (objectpos_over_limit(object->getBasePosition())) {
1760                 v3f p = object->getBasePosition();
1761                 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1762                         << "object position (" << p.X << "," << p.Y << "," << p.Z
1763                         << ") outside maximum range" << std::endl;
1764                 if (object->environmentDeletes())
1765                         delete object;
1766                 return 0;
1767         }
1768
1769         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1770                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1771
1772         m_active_objects[object->getId()] = object;
1773
1774         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1775                         <<"Added id="<<object->getId()<<"; there are now "
1776                         <<m_active_objects.size()<<" active objects."
1777                         <<std::endl;
1778
1779         // Register reference in scripting api (must be done before post-init)
1780         m_script->addObjectReference(object);
1781         // Post-initialize object
1782         object->addedToEnvironment(dtime_s);
1783
1784         // Add static data to block
1785         if(object->isStaticAllowed())
1786         {
1787                 // Add static object to active static list of the block
1788                 v3f objectpos = object->getBasePosition();
1789                 std::string staticdata = object->getStaticData();
1790                 StaticObject s_obj(object->getType(), objectpos, staticdata);
1791                 // Add to the block where the object is located in
1792                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1793                 MapBlock *block = m_map->emergeBlock(blockpos);
1794                 if(block){
1795                         block->m_static_objects.m_active[object->getId()] = s_obj;
1796                         object->m_static_exists = true;
1797                         object->m_static_block = blockpos;
1798
1799                         if(set_changed)
1800                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1801                                         MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1802                 } else {
1803                         v3s16 p = floatToInt(objectpos, BS);
1804                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1805                                         <<"could not emerge block for storing id="<<object->getId()
1806                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
1807                 }
1808         }
1809
1810         return object->getId();
1811 }
1812
1813 /*
1814         Remove objects that satisfy (m_removed && m_known_by_count==0)
1815 */
1816 void ServerEnvironment::removeRemovedObjects()
1817 {
1818         std::vector<u16> objects_to_remove;
1819         for(ActiveObjectMap::iterator i = m_active_objects.begin();
1820                         i != m_active_objects.end(); ++i) {
1821                 u16 id = i->first;
1822                 ServerActiveObject* obj = i->second;
1823                 // This shouldn't happen but check it
1824                 if(obj == NULL)
1825                 {
1826                         infostream<<"NULL object found in ServerEnvironment"
1827                                         <<" while finding removed objects. id="<<id<<std::endl;
1828                         // Id to be removed from m_active_objects
1829                         objects_to_remove.push_back(id);
1830                         continue;
1831                 }
1832
1833                 /*
1834                         We will delete objects that are marked as removed or thatare
1835                         waiting for deletion after deactivation
1836                 */
1837                 if (!obj->m_removed && !obj->m_pending_deactivation)
1838                         continue;
1839
1840                 /*
1841                         Delete static data from block if is marked as removed
1842                 */
1843                 if(obj->m_static_exists && obj->m_removed)
1844                 {
1845                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1846                         if (block) {
1847                                 block->m_static_objects.remove(id);
1848                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1849                                         MOD_REASON_REMOVE_OBJECTS_REMOVE);
1850                                 obj->m_static_exists = false;
1851                         } else {
1852                                 infostream<<"Failed to emerge block from which an object to "
1853                                                 <<"be removed was loaded from. id="<<id<<std::endl;
1854                         }
1855                 }
1856
1857                 // If m_known_by_count > 0, don't actually remove. On some future
1858                 // invocation this will be 0, which is when removal will continue.
1859                 if(obj->m_known_by_count > 0)
1860                         continue;
1861
1862                 /*
1863                         Move static data from active to stored if not marked as removed
1864                 */
1865                 if(obj->m_static_exists && !obj->m_removed){
1866                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1867                         if (block) {
1868                                 std::map<u16, StaticObject>::iterator i =
1869                                                 block->m_static_objects.m_active.find(id);
1870                                 if(i != block->m_static_objects.m_active.end()){
1871                                         block->m_static_objects.m_stored.push_back(i->second);
1872                                         block->m_static_objects.m_active.erase(id);
1873                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1874                                                 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1875                                 }
1876                         } else {
1877                                 infostream<<"Failed to emerge block from which an object to "
1878                                                 <<"be deactivated was loaded from. id="<<id<<std::endl;
1879                         }
1880                 }
1881
1882                 // Tell the object about removal
1883                 obj->removingFromEnvironment();
1884                 // Deregister in scripting api
1885                 m_script->removeObjectReference(obj);
1886
1887                 // Delete
1888                 if(obj->environmentDeletes())
1889                         delete obj;
1890
1891                 // Id to be removed from m_active_objects
1892                 objects_to_remove.push_back(id);
1893         }
1894         // Remove references from m_active_objects
1895         for(std::vector<u16>::iterator i = objects_to_remove.begin();
1896                         i != objects_to_remove.end(); ++i) {
1897                 m_active_objects.erase(*i);
1898         }
1899 }
1900
1901 static void print_hexdump(std::ostream &o, const std::string &data)
1902 {
1903         const int linelength = 16;
1904         for(int l=0; ; l++){
1905                 int i0 = linelength * l;
1906                 bool at_end = false;
1907                 int thislinelength = linelength;
1908                 if(i0 + thislinelength > (int)data.size()){
1909                         thislinelength = data.size() - i0;
1910                         at_end = true;
1911                 }
1912                 for(int di=0; di<linelength; di++){
1913                         int i = i0 + di;
1914                         char buf[4];
1915                         if(di<thislinelength)
1916                                 snprintf(buf, 4, "%.2x ", data[i]);
1917                         else
1918                                 snprintf(buf, 4, "   ");
1919                         o<<buf;
1920                 }
1921                 o<<" ";
1922                 for(int di=0; di<thislinelength; di++){
1923                         int i = i0 + di;
1924                         if(data[i] >= 32)
1925                                 o<<data[i];
1926                         else
1927                                 o<<".";
1928                 }
1929                 o<<std::endl;
1930                 if(at_end)
1931                         break;
1932         }
1933 }
1934
1935 /*
1936         Convert stored objects from blocks near the players to active.
1937 */
1938 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1939 {
1940         if(block == NULL)
1941                 return;
1942
1943         // Ignore if no stored objects (to not set changed flag)
1944         if(block->m_static_objects.m_stored.empty())
1945                 return;
1946
1947         verbosestream<<"ServerEnvironment::activateObjects(): "
1948                         <<"activating objects of block "<<PP(block->getPos())
1949                         <<" ("<<block->m_static_objects.m_stored.size()
1950                         <<" objects)"<<std::endl;
1951         bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1952         if (large_amount) {
1953                 errorstream<<"suspiciously large amount of objects detected: "
1954                                 <<block->m_static_objects.m_stored.size()<<" in "
1955                                 <<PP(block->getPos())
1956                                 <<"; removing all of them."<<std::endl;
1957                 // Clear stored list
1958                 block->m_static_objects.m_stored.clear();
1959                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1960                         MOD_REASON_TOO_MANY_OBJECTS);
1961                 return;
1962         }
1963
1964         // Activate stored objects
1965         std::vector<StaticObject> new_stored;
1966         for (std::vector<StaticObject>::iterator
1967                         i = block->m_static_objects.m_stored.begin();
1968                         i != block->m_static_objects.m_stored.end(); ++i) {
1969                 StaticObject &s_obj = *i;
1970
1971                 // Create an active object from the data
1972                 ServerActiveObject *obj = ServerActiveObject::create
1973                                 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1974                 // If couldn't create object, store static data back.
1975                 if(obj == NULL) {
1976                         errorstream<<"ServerEnvironment::activateObjects(): "
1977                                         <<"failed to create active object from static object "
1978                                         <<"in block "<<PP(s_obj.pos/BS)
1979                                         <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1980                         print_hexdump(verbosestream, s_obj.data);
1981
1982                         new_stored.push_back(s_obj);
1983                         continue;
1984                 }
1985                 verbosestream<<"ServerEnvironment::activateObjects(): "
1986                                 <<"activated static object pos="<<PP(s_obj.pos/BS)
1987                                 <<" type="<<(int)s_obj.type<<std::endl;
1988                 // This will also add the object to the active static list
1989                 addActiveObjectRaw(obj, false, dtime_s);
1990         }
1991         // Clear stored list
1992         block->m_static_objects.m_stored.clear();
1993         // Add leftover failed stuff to stored list
1994         for(std::vector<StaticObject>::iterator
1995                         i = new_stored.begin();
1996                         i != new_stored.end(); ++i) {
1997                 StaticObject &s_obj = *i;
1998                 block->m_static_objects.m_stored.push_back(s_obj);
1999         }
2000
2001         // Turn the active counterparts of activated objects not pending for
2002         // deactivation
2003         for(std::map<u16, StaticObject>::iterator
2004                         i = block->m_static_objects.m_active.begin();
2005                         i != block->m_static_objects.m_active.end(); ++i)
2006         {
2007                 u16 id = i->first;
2008                 ServerActiveObject *object = getActiveObject(id);
2009                 assert(object);
2010                 object->m_pending_deactivation = false;
2011         }
2012
2013         /*
2014                 Note: Block hasn't really been modified here.
2015                 The objects have just been activated and moved from the stored
2016                 static list to the active static list.
2017                 As such, the block is essentially the same.
2018                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2019                 Otherwise there would be a huge amount of unnecessary I/O.
2020         */
2021 }
2022
2023 /*
2024         Convert objects that are not standing inside active blocks to static.
2025
2026         If m_known_by_count != 0, active object is not deleted, but static
2027         data is still updated.
2028
2029         If force_delete is set, active object is deleted nevertheless. It
2030         shall only be set so in the destructor of the environment.
2031
2032         If block wasn't generated (not in memory or on disk),
2033 */
2034 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2035 {
2036         std::vector<u16> objects_to_remove;
2037         for(ActiveObjectMap::iterator i = m_active_objects.begin();
2038                         i != m_active_objects.end(); ++i) {
2039                 ServerActiveObject* obj = i->second;
2040                 assert(obj);
2041
2042                 // Do not deactivate if static data creation not allowed
2043                 if(!force_delete && !obj->isStaticAllowed())
2044                         continue;
2045
2046                 // If pending deactivation, let removeRemovedObjects() do it
2047                 if(!force_delete && obj->m_pending_deactivation)
2048                         continue;
2049
2050                 u16 id = i->first;
2051                 v3f objectpos = obj->getBasePosition();
2052
2053                 // The block in which the object resides in
2054                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2055
2056                 // If object's static data is stored in a deactivated block and object
2057                 // is actually located in an active block, re-save to the block in
2058                 // which the object is actually located in.
2059                 if(!force_delete &&
2060                                 obj->m_static_exists &&
2061                                 !m_active_blocks.contains(obj->m_static_block) &&
2062                                  m_active_blocks.contains(blockpos_o))
2063                 {
2064                         v3s16 old_static_block = obj->m_static_block;
2065
2066                         // Save to block where object is located
2067                         MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2068                         if(!block){
2069                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2070                                                 <<"Could not save object id="<<id
2071                                                 <<" to it's current block "<<PP(blockpos_o)
2072                                                 <<std::endl;
2073                                 continue;
2074                         }
2075                         std::string staticdata_new = obj->getStaticData();
2076                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2077                         block->m_static_objects.insert(id, s_obj);
2078                         obj->m_static_block = blockpos_o;
2079                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2080                                 MOD_REASON_STATIC_DATA_ADDED);
2081
2082                         // Delete from block where object was located
2083                         block = m_map->emergeBlock(old_static_block, false);
2084                         if(!block){
2085                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2086                                                 <<"Could not delete object id="<<id
2087                                                 <<" from it's previous block "<<PP(old_static_block)
2088                                                 <<std::endl;
2089                                 continue;
2090                         }
2091                         block->m_static_objects.remove(id);
2092                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2093                                 MOD_REASON_STATIC_DATA_REMOVED);
2094                         continue;
2095                 }
2096
2097                 // If block is active, don't remove
2098                 if(!force_delete && m_active_blocks.contains(blockpos_o))
2099                         continue;
2100
2101                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2102                                 <<"deactivating object id="<<id<<" on inactive block "
2103                                 <<PP(blockpos_o)<<std::endl;
2104
2105                 // If known by some client, don't immediately delete.
2106                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2107
2108                 /*
2109                         Update the static data
2110                 */
2111
2112                 if(obj->isStaticAllowed())
2113                 {
2114                         // Create new static object
2115                         std::string staticdata_new = obj->getStaticData();
2116                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2117
2118                         bool stays_in_same_block = false;
2119                         bool data_changed = true;
2120
2121                         if (obj->m_static_exists) {
2122                                 if (obj->m_static_block == blockpos_o)
2123                                         stays_in_same_block = true;
2124
2125                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2126
2127                                 if (block) {
2128                                         std::map<u16, StaticObject>::iterator n =
2129                                                 block->m_static_objects.m_active.find(id);
2130                                         if (n != block->m_static_objects.m_active.end()) {
2131                                                 StaticObject static_old = n->second;
2132
2133                                                 float save_movem = obj->getMinimumSavedMovement();
2134
2135                                                 if (static_old.data == staticdata_new &&
2136                                                                 (static_old.pos - objectpos).getLength() < save_movem)
2137                                                         data_changed = false;
2138                                         } else {
2139                                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2140                                                         <<"id="<<id<<" m_static_exists=true but "
2141                                                         <<"static data doesn't actually exist in "
2142                                                         <<PP(obj->m_static_block)<<std::endl;
2143                                         }
2144                                 }
2145                         }
2146
2147                         bool shall_be_written = (!stays_in_same_block || data_changed);
2148
2149                         // Delete old static object
2150                         if(obj->m_static_exists)
2151                         {
2152                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2153                                 if(block)
2154                                 {
2155                                         block->m_static_objects.remove(id);
2156                                         obj->m_static_exists = false;
2157                                         // Only mark block as modified if data changed considerably
2158                                         if(shall_be_written)
2159                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2160                                                         MOD_REASON_STATIC_DATA_CHANGED);
2161                                 }
2162                         }
2163
2164                         // Add to the block where the object is located in
2165                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2166                         // Get or generate the block
2167                         MapBlock *block = NULL;
2168                         try{
2169                                 block = m_map->emergeBlock(blockpos);
2170                         } catch(InvalidPositionException &e){
2171                                 // Handled via NULL pointer
2172                                 // NOTE: emergeBlock's failure is usually determined by it
2173                                 //       actually returning NULL
2174                         }
2175
2176                         if(block)
2177                         {
2178                                 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2179                                         warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2180                                                         << " statically but block " << PP(blockpos)
2181                                                         << " already contains "
2182                                                         << block->m_static_objects.m_stored.size()
2183                                                         << " objects."
2184                                                         << " Forcing delete." << std::endl;
2185                                         force_delete = true;
2186                                 } else {
2187                                         // If static counterpart already exists in target block,
2188                                         // remove it first.
2189                                         // This shouldn't happen because the object is removed from
2190                                         // the previous block before this according to
2191                                         // obj->m_static_block, but happens rarely for some unknown
2192                                         // reason. Unsuccessful attempts have been made to find
2193                                         // said reason.
2194                                         if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2195                                                 warningstream<<"ServerEnv: Performing hack #83274"
2196                                                                 <<std::endl;
2197                                                 block->m_static_objects.remove(id);
2198                                         }
2199                                         // Store static data
2200                                         u16 store_id = pending_delete ? id : 0;
2201                                         block->m_static_objects.insert(store_id, s_obj);
2202
2203                                         // Only mark block as modified if data changed considerably
2204                                         if(shall_be_written)
2205                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2206                                                         MOD_REASON_STATIC_DATA_CHANGED);
2207
2208                                         obj->m_static_exists = true;
2209                                         obj->m_static_block = block->getPos();
2210                                 }
2211                         }
2212                         else{
2213                                 if(!force_delete){
2214                                         v3s16 p = floatToInt(objectpos, BS);
2215                                         errorstream<<"ServerEnv: Could not find or generate "
2216                                                         <<"a block for storing id="<<obj->getId()
2217                                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
2218                                         continue;
2219                                 }
2220                         }
2221                 }
2222
2223                 /*
2224                         If known by some client, set pending deactivation.
2225                         Otherwise delete it immediately.
2226                 */
2227
2228                 if(pending_delete && !force_delete)
2229                 {
2230                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2231                                         <<"object id="<<id<<" is known by clients"
2232                                         <<"; not deleting yet"<<std::endl;
2233
2234                         obj->m_pending_deactivation = true;
2235                         continue;
2236                 }
2237
2238                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2239                                 <<"object id="<<id<<" is not known by clients"
2240                                 <<"; deleting"<<std::endl;
2241
2242                 // Tell the object about removal
2243                 obj->removingFromEnvironment();
2244                 // Deregister in scripting api
2245                 m_script->removeObjectReference(obj);
2246
2247                 // Delete active object
2248                 if(obj->environmentDeletes())
2249                         delete obj;
2250                 // Id to be removed from m_active_objects
2251                 objects_to_remove.push_back(id);
2252         }
2253
2254         // Remove references from m_active_objects
2255         for(std::vector<u16>::iterator i = objects_to_remove.begin();
2256                         i != objects_to_remove.end(); ++i) {
2257                 m_active_objects.erase(*i);
2258         }
2259 }
2260
2261 #ifndef SERVER
2262
2263 #include "clientsimpleobject.h"
2264
2265 /*
2266         ClientEnvironment
2267 */
2268
2269 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2270                 ITextureSource *texturesource, IGameDef *gamedef,
2271                 IrrlichtDevice *irr):
2272         m_map(map),
2273         m_local_player(NULL),
2274         m_smgr(smgr),
2275         m_texturesource(texturesource),
2276         m_gamedef(gamedef),
2277         m_irr(irr)
2278 {
2279         char zero = 0;
2280         memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2281 }
2282
2283 ClientEnvironment::~ClientEnvironment()
2284 {
2285         // delete active objects
2286         for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2287                         i != m_active_objects.end(); ++i) {
2288                 delete i->second;
2289         }
2290
2291         for(std::vector<ClientSimpleObject*>::iterator
2292                         i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2293                 delete *i;
2294         }
2295
2296         // Drop/delete map
2297         m_map->drop();
2298 }
2299
2300 Map & ClientEnvironment::getMap()
2301 {
2302         return *m_map;
2303 }
2304
2305 ClientMap & ClientEnvironment::getClientMap()
2306 {
2307         return *m_map;
2308 }
2309
2310 void ClientEnvironment::setLocalPlayer(LocalPlayer *player)
2311 {
2312         DSTACK(FUNCTION_NAME);
2313         /*
2314                 It is a failure if already is a local player
2315         */
2316         FATAL_ERROR_IF(m_local_player != NULL,
2317                         "Local player already allocated");
2318
2319         m_local_player = player;
2320 }
2321
2322 void ClientEnvironment::step(float dtime)
2323 {
2324         DSTACK(FUNCTION_NAME);
2325
2326         /* Step time of day */
2327         stepTimeOfDay(dtime);
2328
2329         // Get some settings
2330         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2331         bool free_move = fly_allowed && g_settings->getBool("free_move");
2332
2333         // Get local player
2334         LocalPlayer *lplayer = getLocalPlayer();
2335         assert(lplayer);
2336         // collision info queue
2337         std::vector<CollisionInfo> player_collisions;
2338
2339         /*
2340                 Get the speed the player is going
2341         */
2342         bool is_climbing = lplayer->is_climbing;
2343
2344         f32 player_speed = lplayer->getSpeed().getLength();
2345
2346         /*
2347                 Maximum position increment
2348         */
2349         //f32 position_max_increment = 0.05*BS;
2350         f32 position_max_increment = 0.1*BS;
2351
2352         // Maximum time increment (for collision detection etc)
2353         // time = distance / speed
2354         f32 dtime_max_increment = 1;
2355         if(player_speed > 0.001)
2356                 dtime_max_increment = position_max_increment / player_speed;
2357
2358         // Maximum time increment is 10ms or lower
2359         if(dtime_max_increment > 0.01)
2360                 dtime_max_increment = 0.01;
2361
2362         // Don't allow overly huge dtime
2363         if(dtime > 0.5)
2364                 dtime = 0.5;
2365
2366         f32 dtime_downcount = dtime;
2367
2368         /*
2369                 Stuff that has a maximum time increment
2370         */
2371
2372         u32 loopcount = 0;
2373         do
2374         {
2375                 loopcount++;
2376
2377                 f32 dtime_part;
2378                 if(dtime_downcount > dtime_max_increment)
2379                 {
2380                         dtime_part = dtime_max_increment;
2381                         dtime_downcount -= dtime_part;
2382                 }
2383                 else
2384                 {
2385                         dtime_part = dtime_downcount;
2386                         /*
2387                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
2388                                 when dtime_part is so small that dtime_downcount -= dtime_part
2389                                 does nothing
2390                         */
2391                         dtime_downcount = 0;
2392                 }
2393
2394                 /*
2395                         Handle local player
2396                 */
2397
2398                 {
2399                         // Apply physics
2400                         if(!free_move && !is_climbing)
2401                         {
2402                                 // Gravity
2403                                 v3f speed = lplayer->getSpeed();
2404                                 if(!lplayer->in_liquid)
2405                                         speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2406
2407                                 // Liquid floating / sinking
2408                                 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2409                                         speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2410
2411                                 // Liquid resistance
2412                                 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2413                                 {
2414                                         // How much the node's viscosity blocks movement, ranges between 0 and 1
2415                                         // Should match the scale at which viscosity increase affects other liquid attributes
2416                                         const f32 viscosity_factor = 0.3;
2417
2418                                         v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2419                                         f32 dl = d_wanted.getLength();
2420                                         if(dl > lplayer->movement_liquid_fluidity_smooth)
2421                                                 dl = lplayer->movement_liquid_fluidity_smooth;
2422                                         dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2423
2424                                         v3f d = d_wanted.normalize() * dl;
2425                                         speed += d;
2426                                 }
2427
2428                                 lplayer->setSpeed(speed);
2429                         }
2430
2431                         /*
2432                                 Move the lplayer.
2433                                 This also does collision detection.
2434                         */
2435                         lplayer->move(dtime_part, this, position_max_increment,
2436                                         &player_collisions);
2437                 }
2438         }
2439         while(dtime_downcount > 0.001);
2440
2441         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2442
2443         for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2444                         i != player_collisions.end(); ++i) {
2445                 CollisionInfo &info = *i;
2446                 v3f speed_diff = info.new_speed - info.old_speed;;
2447                 // Handle only fall damage
2448                 // (because otherwise walking against something in fast_move kills you)
2449                 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2450                         continue;
2451                 // Get rid of other components
2452                 speed_diff.X = 0;
2453                 speed_diff.Z = 0;
2454                 f32 pre_factor = 1; // 1 hp per node/s
2455                 f32 tolerance = BS*14; // 5 without damage
2456                 f32 post_factor = 1; // 1 hp per node/s
2457                 if(info.type == COLLISION_NODE)
2458                 {
2459                         const ContentFeatures &f = m_gamedef->ndef()->
2460                                         get(m_map->getNodeNoEx(info.node_p));
2461                         // Determine fall damage multiplier
2462                         int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2463                         pre_factor = 1.0 + (float)addp/100.0;
2464                 }
2465                 float speed = pre_factor * speed_diff.getLength();
2466                 if(speed > tolerance)
2467                 {
2468                         f32 damage_f = (speed - tolerance)/BS * post_factor;
2469                         u16 damage = (u16)(damage_f+0.5);
2470                         if(damage != 0){
2471                                 damageLocalPlayer(damage, true);
2472                                 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2473                                 m_gamedef->event()->put(e);
2474                         }
2475                 }
2476         }
2477
2478         /*
2479                 A quick draft of lava damage
2480         */
2481         if(m_lava_hurt_interval.step(dtime, 1.0))
2482         {
2483                 v3f pf = lplayer->getPosition();
2484
2485                 // Feet, middle and head
2486                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2487                 MapNode n1 = m_map->getNodeNoEx(p1);
2488                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2489                 MapNode n2 = m_map->getNodeNoEx(p2);
2490                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2491                 MapNode n3 = m_map->getNodeNoEx(p3);
2492
2493                 u32 damage_per_second = 0;
2494                 damage_per_second = MYMAX(damage_per_second,
2495                                 m_gamedef->ndef()->get(n1).damage_per_second);
2496                 damage_per_second = MYMAX(damage_per_second,
2497                                 m_gamedef->ndef()->get(n2).damage_per_second);
2498                 damage_per_second = MYMAX(damage_per_second,
2499                                 m_gamedef->ndef()->get(n3).damage_per_second);
2500
2501                 if(damage_per_second != 0)
2502                 {
2503                         damageLocalPlayer(damage_per_second, true);
2504                 }
2505         }
2506
2507         /*
2508                 Drowning
2509         */
2510         if(m_drowning_interval.step(dtime, 2.0))
2511         {
2512                 v3f pf = lplayer->getPosition();
2513
2514                 // head
2515                 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2516                 MapNode n = m_map->getNodeNoEx(p);
2517                 ContentFeatures c = m_gamedef->ndef()->get(n);
2518                 u8 drowning_damage = c.drowning;
2519                 if(drowning_damage > 0 && lplayer->hp > 0){
2520                         u16 breath = lplayer->getBreath();
2521                         if(breath > 10){
2522                                 breath = 11;
2523                         }
2524                         if(breath > 0){
2525                                 breath -= 1;
2526                         }
2527                         lplayer->setBreath(breath);
2528                         updateLocalPlayerBreath(breath);
2529                 }
2530
2531                 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2532                         damageLocalPlayer(drowning_damage, true);
2533                 }
2534         }
2535         if(m_breathing_interval.step(dtime, 0.5))
2536         {
2537                 v3f pf = lplayer->getPosition();
2538
2539                 // head
2540                 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2541                 MapNode n = m_map->getNodeNoEx(p);
2542                 ContentFeatures c = m_gamedef->ndef()->get(n);
2543                 if (!lplayer->hp){
2544                         lplayer->setBreath(11);
2545                 }
2546                 else if(c.drowning == 0){
2547                         u16 breath = lplayer->getBreath();
2548                         if(breath <= 10){
2549                                 breath += 1;
2550                                 lplayer->setBreath(breath);
2551                                 updateLocalPlayerBreath(breath);
2552                         }
2553                 }
2554         }
2555
2556         // Update lighting on local player (used for wield item)
2557         u32 day_night_ratio = getDayNightRatio();
2558         {
2559                 // Get node at head
2560
2561                 // On InvalidPositionException, use this as default
2562                 // (day: LIGHT_SUN, night: 0)
2563                 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2564
2565                 v3s16 p = lplayer->getLightPosition();
2566                 node_at_lplayer = m_map->getNodeNoEx(p);
2567
2568                 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2569                 u8 day = light & 0xff;
2570                 u8 night = (light >> 8) & 0xff;
2571                 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2572         }
2573
2574         /*
2575                 Step active objects and update lighting of them
2576         */
2577
2578         g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2579         bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2580         for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2581                         i != m_active_objects.end(); ++i) {
2582                 ClientActiveObject* obj = i->second;
2583                 // Step object
2584                 obj->step(dtime, this);
2585
2586                 if(update_lighting)
2587                 {
2588                         // Update lighting
2589                         u8 light = 0;
2590                         bool pos_ok;
2591
2592                         // Get node at head
2593                         v3s16 p = obj->getLightPosition();
2594                         MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2595                         if (pos_ok)
2596                                 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2597                         else
2598                                 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2599
2600                         obj->updateLight(light);
2601                 }
2602         }
2603
2604         /*
2605                 Step and handle simple objects
2606         */
2607         g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2608         for(std::vector<ClientSimpleObject*>::iterator
2609                         i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2610                 std::vector<ClientSimpleObject*>::iterator cur = i;
2611                 ClientSimpleObject *simple = *cur;
2612
2613                 simple->step(dtime);
2614                 if(simple->m_to_be_removed) {
2615                         delete simple;
2616                         i = m_simple_objects.erase(cur);
2617                 }
2618                 else {
2619                         ++i;
2620                 }
2621         }
2622 }
2623
2624 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2625 {
2626         m_simple_objects.push_back(simple);
2627 }
2628
2629 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2630 {
2631         ClientActiveObject *obj = getActiveObject(id);
2632         if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2633                 return (GenericCAO*) obj;
2634         else
2635                 return NULL;
2636 }
2637
2638 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2639 {
2640         UNORDERED_MAP<u16, ClientActiveObject*>::iterator n = m_active_objects.find(id);
2641         if (n == m_active_objects.end())
2642                 return NULL;
2643         return n->second;
2644 }
2645
2646 bool isFreeClientActiveObjectId(const u16 id,
2647         UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2648 {
2649         if(id == 0)
2650                 return false;
2651
2652         return objects.find(id) == objects.end();
2653 }
2654
2655 u16 getFreeClientActiveObjectId(UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2656 {
2657         //try to reuse id's as late as possible
2658         static u16 last_used_id = 0;
2659         u16 startid = last_used_id;
2660         for(;;) {
2661                 last_used_id ++;
2662                 if (isFreeClientActiveObjectId(last_used_id, objects))
2663                         return last_used_id;
2664
2665                 if (last_used_id == startid)
2666                         return 0;
2667         }
2668 }
2669
2670 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2671 {
2672         assert(object); // Pre-condition
2673         if(object->getId() == 0)
2674         {
2675                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2676                 if(new_id == 0)
2677                 {
2678                         infostream<<"ClientEnvironment::addActiveObject(): "
2679                                         <<"no free ids available"<<std::endl;
2680                         delete object;
2681                         return 0;
2682                 }
2683                 object->setId(new_id);
2684         }
2685         if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) {
2686                 infostream<<"ClientEnvironment::addActiveObject(): "
2687                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2688                 delete object;
2689                 return 0;
2690         }
2691         infostream<<"ClientEnvironment::addActiveObject(): "
2692                         <<"added (id="<<object->getId()<<")"<<std::endl;
2693         m_active_objects[object->getId()] = object;
2694         object->addToScene(m_smgr, m_texturesource, m_irr);
2695         { // Update lighting immediately
2696                 u8 light = 0;
2697                 bool pos_ok;
2698
2699                 // Get node at head
2700                 v3s16 p = object->getLightPosition();
2701                 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2702                 if (pos_ok)
2703                         light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2704                 else
2705                         light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2706
2707                 object->updateLight(light);
2708         }
2709         return object->getId();
2710 }
2711
2712 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2713                 const std::string &init_data)
2714 {
2715         ClientActiveObject* obj =
2716                         ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2717         if(obj == NULL)
2718         {
2719                 infostream<<"ClientEnvironment::addActiveObject(): "
2720                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2721                                 <<std::endl;
2722                 return;
2723         }
2724
2725         obj->setId(id);
2726
2727         try
2728         {
2729                 obj->initialize(init_data);
2730         }
2731         catch(SerializationError &e)
2732         {
2733                 errorstream<<"ClientEnvironment::addActiveObject():"
2734                                 <<" id="<<id<<" type="<<type
2735                                 <<": SerializationError in initialize(): "
2736                                 <<e.what()
2737                                 <<": init_data="<<serializeJsonString(init_data)
2738                                 <<std::endl;
2739         }
2740
2741         addActiveObject(obj);
2742 }
2743
2744 void ClientEnvironment::removeActiveObject(u16 id)
2745 {
2746         verbosestream<<"ClientEnvironment::removeActiveObject(): "
2747                         <<"id="<<id<<std::endl;
2748         ClientActiveObject* obj = getActiveObject(id);
2749         if (obj == NULL) {
2750                 infostream<<"ClientEnvironment::removeActiveObject(): "
2751                                 <<"id="<<id<<" not found"<<std::endl;
2752                 return;
2753         }
2754         obj->removeFromScene(true);
2755         delete obj;
2756         m_active_objects.erase(id);
2757 }
2758
2759 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2760 {
2761         ClientActiveObject *obj = getActiveObject(id);
2762         if (obj == NULL) {
2763                 infostream << "ClientEnvironment::processActiveObjectMessage():"
2764                         << " got message for id=" << id << ", which doesn't exist."
2765                         << std::endl;
2766                 return;
2767         }
2768
2769         try {
2770                 obj->processMessage(data);
2771         } catch (SerializationError &e) {
2772                 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2773                         << " id=" << id << " type=" << obj->getType()
2774                         << " SerializationError in processMessage(): " << e.what()
2775                         << std::endl;
2776         }
2777 }
2778
2779 /*
2780         Callbacks for activeobjects
2781 */
2782
2783 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2784 {
2785         LocalPlayer *lplayer = getLocalPlayer();
2786         assert(lplayer);
2787
2788         if (handle_hp) {
2789                 if (lplayer->hp > damage)
2790                         lplayer->hp -= damage;
2791                 else
2792                         lplayer->hp = 0;
2793         }
2794
2795         ClientEnvEvent event;
2796         event.type = CEE_PLAYER_DAMAGE;
2797         event.player_damage.amount = damage;
2798         event.player_damage.send_to_server = handle_hp;
2799         m_client_event_queue.push(event);
2800 }
2801
2802 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2803 {
2804         ClientEnvEvent event;
2805         event.type = CEE_PLAYER_BREATH;
2806         event.player_breath.amount = breath;
2807         m_client_event_queue.push(event);
2808 }
2809
2810 /*
2811         Client likes to call these
2812 */
2813
2814 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2815                 std::vector<DistanceSortedActiveObject> &dest)
2816 {
2817         for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2818                         i != m_active_objects.end(); ++i) {
2819                 ClientActiveObject* obj = i->second;
2820
2821                 f32 d = (obj->getPosition() - origin).getLength();
2822
2823                 if(d > max_d)
2824                         continue;
2825
2826                 DistanceSortedActiveObject dso(obj, d);
2827
2828                 dest.push_back(dso);
2829         }
2830 }
2831
2832 ClientEnvEvent ClientEnvironment::getClientEvent()
2833 {
2834         ClientEnvEvent event;
2835         if(m_client_event_queue.empty())
2836                 event.type = CEE_NONE;
2837         else {
2838                 event = m_client_event_queue.front();
2839                 m_client_event_queue.pop();
2840         }
2841         return event;
2842 }
2843
2844 #endif // #ifndef SERVER