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