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