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