]> git.lizzy.rs Git - minetest.git/blob - src/environment.cpp
1de44e755011fe448894d937a179fbae7f402f29
[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         setTimeOfDay(args.exists("time_of_day") ?
780                 // set day to morning by default
781                 args.getU64("time_of_day") : 9000);
782
783         m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
784                 // If missing, do as if clearObjects was never called
785                 args.getU64("last_clear_objects_time") : 0;
786
787         std::string lbm_introduction_times = "";
788         try {
789                 u64 ver = args.getU64("lbm_introduction_times_version");
790                 if (ver == 1) {
791                         lbm_introduction_times = args.get("lbm_introduction_times");
792                 } else {
793                         infostream << "ServerEnvironment::loadMeta(): Non-supported"
794                                 << " introduction time version " << ver << std::endl;
795                 }
796         } catch (SettingNotFoundException &e) {
797                 // No problem, this is expected. Just continue with an empty string
798         }
799         m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
800
801         m_day_count = args.exists("day_count") ?
802                 args.getU64("day_count") : 0;
803 }
804
805 void ServerEnvironment::loadDefaultMeta()
806 {
807         m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
808 }
809
810 struct ActiveABM
811 {
812         ActiveBlockModifier *abm;
813         int chance;
814         std::set<content_t> required_neighbors;
815 };
816
817 class ABMHandler
818 {
819 private:
820         ServerEnvironment *m_env;
821         std::map<content_t, std::vector<ActiveABM> > m_aabms;
822 public:
823         ABMHandler(std::vector<ABMWithState> &abms,
824                         float dtime_s, ServerEnvironment *env,
825                         bool use_timers):
826                 m_env(env)
827         {
828                 if(dtime_s < 0.001)
829                         return;
830                 INodeDefManager *ndef = env->getGameDef()->ndef();
831                 for(std::vector<ABMWithState>::iterator
832                                 i = abms.begin(); i != abms.end(); ++i) {
833                         ActiveBlockModifier *abm = i->abm;
834                         float trigger_interval = abm->getTriggerInterval();
835                         if(trigger_interval < 0.001)
836                                 trigger_interval = 0.001;
837                         float actual_interval = dtime_s;
838                         if(use_timers){
839                                 i->timer += dtime_s;
840                                 if(i->timer < trigger_interval)
841                                         continue;
842                                 i->timer -= trigger_interval;
843                                 actual_interval = trigger_interval;
844                         }
845                         float chance = abm->getTriggerChance();
846                         if(chance == 0)
847                                 chance = 1;
848                         ActiveABM aabm;
849                         aabm.abm = abm;
850                         if(abm->getSimpleCatchUp()) {
851                                 float intervals = actual_interval / trigger_interval;
852                                 if(intervals == 0)
853                                         continue;
854                                 aabm.chance = chance / intervals;
855                                 if(aabm.chance == 0)
856                                         aabm.chance = 1;
857                         } else {
858                                 aabm.chance = chance;
859                         }
860                         // Trigger neighbors
861                         std::set<std::string> required_neighbors_s
862                                         = abm->getRequiredNeighbors();
863                         for(std::set<std::string>::iterator
864                                         i = required_neighbors_s.begin();
865                                         i != required_neighbors_s.end(); ++i)
866                         {
867                                 ndef->getIds(*i, aabm.required_neighbors);
868                         }
869                         // Trigger contents
870                         std::set<std::string> contents_s = abm->getTriggerContents();
871                         for(std::set<std::string>::iterator
872                                         i = contents_s.begin(); i != contents_s.end(); ++i)
873                         {
874                                 std::set<content_t> ids;
875                                 ndef->getIds(*i, ids);
876                                 for(std::set<content_t>::const_iterator k = ids.begin();
877                                                 k != ids.end(); ++k)
878                                 {
879                                         content_t c = *k;
880                                         std::map<content_t, std::vector<ActiveABM> >::iterator j;
881                                         j = m_aabms.find(c);
882                                         if(j == m_aabms.end()){
883                                                 std::vector<ActiveABM> aabmlist;
884                                                 m_aabms[c] = aabmlist;
885                                                 j = m_aabms.find(c);
886                                         }
887                                         j->second.push_back(aabm);
888                                 }
889                         }
890                 }
891         }
892         // Find out how many objects the given block and its neighbours contain.
893         // Returns the number of objects in the block, and also in 'wider' the
894         // number of objects in the block and all its neighbours. The latter
895         // may an estimate if any neighbours are unloaded.
896         u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
897         {
898                 wider = 0;
899                 u32 wider_unknown_count = 0;
900                 for(s16 x=-1; x<=1; x++)
901                 for(s16 y=-1; y<=1; y++)
902                 for(s16 z=-1; z<=1; z++)
903                 {
904                         MapBlock *block2 = map->getBlockNoCreateNoEx(
905                                         block->getPos() + v3s16(x,y,z));
906                         if(block2==NULL){
907                                 wider_unknown_count++;
908                                 continue;
909                         }
910                         wider += block2->m_static_objects.m_active.size()
911                                         + block2->m_static_objects.m_stored.size();
912                 }
913                 // Extrapolate
914                 u32 active_object_count = block->m_static_objects.m_active.size();
915                 u32 wider_known_count = 3*3*3 - wider_unknown_count;
916                 wider += wider_unknown_count * wider / wider_known_count;
917                 return active_object_count;
918
919         }
920         void apply(MapBlock *block)
921         {
922                 if(m_aabms.empty())
923                         return;
924
925                 ServerMap *map = &m_env->getServerMap();
926
927                 u32 active_object_count_wider;
928                 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
929                 m_env->m_added_objects = 0;
930
931                 v3s16 p0;
932                 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
933                 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
934                 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
935                 {
936                         MapNode n = block->getNodeNoEx(p0);
937                         content_t c = n.getContent();
938                         v3s16 p = p0 + block->getPosRelative();
939
940                         std::map<content_t, std::vector<ActiveABM> >::iterator j;
941                         j = m_aabms.find(c);
942                         if(j == m_aabms.end())
943                                 continue;
944
945                         for(std::vector<ActiveABM>::iterator
946                                         i = j->second.begin(); i != j->second.end(); ++i) {
947                                 if(myrand() % i->chance != 0)
948                                         continue;
949
950                                 // Check neighbors
951                                 if(!i->required_neighbors.empty())
952                                 {
953                                         v3s16 p1;
954                                         for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
955                                         for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
956                                         for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
957                                         {
958                                                 if(p1 == p)
959                                                         continue;
960                                                 MapNode n = map->getNodeNoEx(p1);
961                                                 content_t c = n.getContent();
962                                                 std::set<content_t>::const_iterator k;
963                                                 k = i->required_neighbors.find(c);
964                                                 if(k != i->required_neighbors.end()){
965                                                         goto neighbor_found;
966                                                 }
967                                         }
968                                         // No required neighbor found
969                                         continue;
970                                 }
971 neighbor_found:
972
973                                 // Call all the trigger variations
974                                 i->abm->trigger(m_env, p, n);
975                                 i->abm->trigger(m_env, p, n,
976                                                 active_object_count, active_object_count_wider);
977
978                                 // Count surrounding objects again if the abms added any
979                                 if(m_env->m_added_objects > 0) {
980                                         active_object_count = countObjects(block, map, active_object_count_wider);
981                                         m_env->m_added_objects = 0;
982                                 }
983                         }
984                 }
985         }
986 };
987
988 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
989 {
990         // Reset usage timer immediately, otherwise a block that becomes active
991         // again at around the same time as it would normally be unloaded will
992         // get unloaded incorrectly. (I think this still leaves a small possibility
993         // of a race condition between this and server::AsyncRunStep, which only
994         // some kind of synchronisation will fix, but it at least reduces the window
995         // of opportunity for it to break from seconds to nanoseconds)
996         block->resetUsageTimer();
997
998         // Get time difference
999         u32 dtime_s = 0;
1000         u32 stamp = block->getTimestamp();
1001         if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
1002                 dtime_s = m_game_time - stamp;
1003         dtime_s += additional_dtime;
1004
1005         /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
1006                         <<stamp<<", game time: "<<m_game_time<<std::endl;*/
1007
1008         // Remove stored static objects if clearObjects was called since block's timestamp
1009         if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
1010                 block->m_static_objects.m_stored.clear();
1011                 // do not set changed flag to avoid unnecessary mapblock writes
1012         }
1013
1014         // Set current time as timestamp
1015         block->setTimestampNoChangedFlag(m_game_time);
1016
1017         /*infostream<<"ServerEnvironment::activateBlock(): block is "
1018                         <<dtime_s<<" seconds old."<<std::endl;*/
1019
1020         // Activate stored objects
1021         activateObjects(block, dtime_s);
1022
1023         /* Handle LoadingBlockModifiers */
1024         m_lbm_mgr.applyLBMs(this, block, stamp);
1025
1026         // Run node timers
1027         std::map<v3s16, NodeTimer> elapsed_timers =
1028                 block->m_node_timers.step((float)dtime_s);
1029         if(!elapsed_timers.empty()){
1030                 MapNode n;
1031                 for(std::map<v3s16, NodeTimer>::iterator
1032                                 i = elapsed_timers.begin();
1033                                 i != elapsed_timers.end(); ++i){
1034                         n = block->getNodeNoEx(i->first);
1035                         v3s16 p = i->first + block->getPosRelative();
1036                         if(m_script->node_on_timer(p,n,i->second.elapsed))
1037                                 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1038                 }
1039         }
1040
1041         /* Handle ActiveBlockModifiers */
1042         ABMHandler abmhandler(m_abms, dtime_s, this, false);
1043         abmhandler.apply(block);
1044 }
1045
1046 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
1047 {
1048         m_abms.push_back(ABMWithState(abm));
1049 }
1050
1051 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1052 {
1053         m_lbm_mgr.addLBMDef(lbm);
1054 }
1055
1056 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1057 {
1058         INodeDefManager *ndef = m_gamedef->ndef();
1059         MapNode n_old = m_map->getNodeNoEx(p);
1060
1061         // Call destructor
1062         if (ndef->get(n_old).has_on_destruct)
1063                 m_script->node_on_destruct(p, n_old);
1064
1065         // Replace node
1066         if (!m_map->addNodeWithEvent(p, n))
1067                 return false;
1068
1069         // Update active VoxelManipulator if a mapgen thread
1070         m_map->updateVManip(p);
1071
1072         // Call post-destructor
1073         if (ndef->get(n_old).has_after_destruct)
1074                 m_script->node_after_destruct(p, n_old);
1075
1076         // Call constructor
1077         if (ndef->get(n).has_on_construct)
1078                 m_script->node_on_construct(p, n);
1079
1080         return true;
1081 }
1082
1083 bool ServerEnvironment::removeNode(v3s16 p)
1084 {
1085         INodeDefManager *ndef = m_gamedef->ndef();
1086         MapNode n_old = m_map->getNodeNoEx(p);
1087
1088         // Call destructor
1089         if (ndef->get(n_old).has_on_destruct)
1090                 m_script->node_on_destruct(p, n_old);
1091
1092         // Replace with air
1093         // This is slightly optimized compared to addNodeWithEvent(air)
1094         if (!m_map->removeNodeWithEvent(p))
1095                 return false;
1096
1097         // Update active VoxelManipulator if a mapgen thread
1098         m_map->updateVManip(p);
1099
1100         // Call post-destructor
1101         if (ndef->get(n_old).has_after_destruct)
1102                 m_script->node_after_destruct(p, n_old);
1103
1104         // Air doesn't require constructor
1105         return true;
1106 }
1107
1108 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1109 {
1110         if (!m_map->addNodeWithEvent(p, n, false))
1111                 return false;
1112
1113         // Update active VoxelManipulator if a mapgen thread
1114         m_map->updateVManip(p);
1115
1116         return true;
1117 }
1118
1119 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1120 {
1121         for(std::map<u16, ServerActiveObject*>::iterator
1122                         i = m_active_objects.begin();
1123                         i != m_active_objects.end(); ++i)
1124         {
1125                 ServerActiveObject* obj = i->second;
1126                 u16 id = i->first;
1127                 v3f objectpos = obj->getBasePosition();
1128                 if(objectpos.getDistanceFrom(pos) > radius)
1129                         continue;
1130                 objects.push_back(id);
1131         }
1132 }
1133
1134 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1135 {
1136         infostream << "ServerEnvironment::clearObjects(): "
1137                 << "Removing all active objects" << std::endl;
1138         std::vector<u16> objects_to_remove;
1139         for (std::map<u16, ServerActiveObject*>::iterator
1140                         i = m_active_objects.begin();
1141                         i != m_active_objects.end(); ++i) {
1142                 ServerActiveObject* obj = i->second;
1143                 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1144                         continue;
1145                 u16 id = i->first;
1146                 // Delete static object if block is loaded
1147                 if (obj->m_static_exists) {
1148                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1149                         if (block) {
1150                                 block->m_static_objects.remove(id);
1151                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1152                                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1153                                 obj->m_static_exists = false;
1154                         }
1155                 }
1156                 // If known by some client, don't delete immediately
1157                 if (obj->m_known_by_count > 0) {
1158                         obj->m_pending_deactivation = true;
1159                         obj->m_removed = true;
1160                         continue;
1161                 }
1162
1163                 // Tell the object about removal
1164                 obj->removingFromEnvironment();
1165                 // Deregister in scripting api
1166                 m_script->removeObjectReference(obj);
1167
1168                 // Delete active object
1169                 if (obj->environmentDeletes())
1170                         delete obj;
1171                 // Id to be removed from m_active_objects
1172                 objects_to_remove.push_back(id);
1173         }
1174
1175         // Remove references from m_active_objects
1176         for (std::vector<u16>::iterator i = objects_to_remove.begin();
1177                         i != objects_to_remove.end(); ++i) {
1178                 m_active_objects.erase(*i);
1179         }
1180
1181         // Get list of loaded blocks
1182         std::vector<v3s16> loaded_blocks;
1183         infostream << "ServerEnvironment::clearObjects(): "
1184                 << "Listing all loaded blocks" << std::endl;
1185         m_map->listAllLoadedBlocks(loaded_blocks);
1186         infostream << "ServerEnvironment::clearObjects(): "
1187                 << "Done listing all loaded blocks: "
1188                 << loaded_blocks.size()<<std::endl;
1189
1190         // Get list of loadable blocks
1191         std::vector<v3s16> loadable_blocks;
1192         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1193                 infostream << "ServerEnvironment::clearObjects(): "
1194                         << "Listing all loadable blocks" << std::endl;
1195                 m_map->listAllLoadableBlocks(loadable_blocks);
1196                 infostream << "ServerEnvironment::clearObjects(): "
1197                         << "Done listing all loadable blocks: "
1198                         << loadable_blocks.size() << std::endl;
1199         } else {
1200                 loadable_blocks = loaded_blocks;
1201         }
1202
1203         infostream << "ServerEnvironment::clearObjects(): "
1204                 << "Now clearing objects in " << loadable_blocks.size()
1205                 << " blocks" << std::endl;
1206
1207         // Grab a reference on each loaded block to avoid unloading it
1208         for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1209                         i != loaded_blocks.end(); ++i) {
1210                 v3s16 p = *i;
1211                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1212                 assert(block != NULL);
1213                 block->refGrab();
1214         }
1215
1216         // Remove objects in all loadable blocks
1217         u32 unload_interval = U32_MAX;
1218         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1219                 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1220                 unload_interval = MYMAX(unload_interval, 1);
1221         }
1222         u32 report_interval = loadable_blocks.size() / 10;
1223         u32 num_blocks_checked = 0;
1224         u32 num_blocks_cleared = 0;
1225         u32 num_objs_cleared = 0;
1226         for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1227                         i != loadable_blocks.end(); ++i) {
1228                 v3s16 p = *i;
1229                 MapBlock *block = m_map->emergeBlock(p, false);
1230                 if (!block) {
1231                         errorstream << "ServerEnvironment::clearObjects(): "
1232                                 << "Failed to emerge block " << PP(p) << std::endl;
1233                         continue;
1234                 }
1235                 u32 num_stored = block->m_static_objects.m_stored.size();
1236                 u32 num_active = block->m_static_objects.m_active.size();
1237                 if (num_stored != 0 || num_active != 0) {
1238                         block->m_static_objects.m_stored.clear();
1239                         block->m_static_objects.m_active.clear();
1240                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1241                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1242                         num_objs_cleared += num_stored + num_active;
1243                         num_blocks_cleared++;
1244                 }
1245                 num_blocks_checked++;
1246
1247                 if (report_interval != 0 &&
1248                                 num_blocks_checked % report_interval == 0) {
1249                         float percent = 100.0 * (float)num_blocks_checked /
1250                                 loadable_blocks.size();
1251                         infostream << "ServerEnvironment::clearObjects(): "
1252                                 << "Cleared " << num_objs_cleared << " objects"
1253                                 << " in " << num_blocks_cleared << " blocks ("
1254                                 << percent << "%)" << std::endl;
1255                 }
1256                 if (num_blocks_checked % unload_interval == 0) {
1257                         m_map->unloadUnreferencedBlocks();
1258                 }
1259         }
1260         m_map->unloadUnreferencedBlocks();
1261
1262         // Drop references that were added above
1263         for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1264                         i != loaded_blocks.end(); ++i) {
1265                 v3s16 p = *i;
1266                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1267                 assert(block);
1268                 block->refDrop();
1269         }
1270
1271         m_last_clear_objects_time = m_game_time;
1272
1273         infostream << "ServerEnvironment::clearObjects(): "
1274                 << "Finished: Cleared " << num_objs_cleared << " objects"
1275                 << " in " << num_blocks_cleared << " blocks" << std::endl;
1276 }
1277
1278 void ServerEnvironment::step(float dtime)
1279 {
1280         DSTACK(FUNCTION_NAME);
1281
1282         //TimeTaker timer("ServerEnv step");
1283
1284         /* Step time of day */
1285         stepTimeOfDay(dtime);
1286
1287         // Update this one
1288         // NOTE: This is kind of funny on a singleplayer game, but doesn't
1289         // really matter that much.
1290         static const float server_step = g_settings->getFloat("dedicated_server_step");
1291         m_recommended_send_interval = server_step;
1292
1293         /*
1294                 Increment game time
1295         */
1296         {
1297                 m_game_time_fraction_counter += dtime;
1298                 u32 inc_i = (u32)m_game_time_fraction_counter;
1299                 m_game_time += inc_i;
1300                 m_game_time_fraction_counter -= (float)inc_i;
1301         }
1302
1303         /*
1304                 Handle players
1305         */
1306         {
1307                 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1308                 for(std::vector<Player*>::iterator i = m_players.begin();
1309                                 i != m_players.end(); ++i)
1310                 {
1311                         Player *player = *i;
1312
1313                         // Ignore disconnected players
1314                         if(player->peer_id == 0)
1315                                 continue;
1316
1317                         // Move
1318                         player->move(dtime, this, 100*BS);
1319                 }
1320         }
1321
1322         /*
1323                 Manage active block list
1324         */
1325         if(m_active_blocks_management_interval.step(dtime, 2.0))
1326         {
1327                 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1328                 /*
1329                         Get player block positions
1330                 */
1331                 std::vector<v3s16> players_blockpos;
1332                 for(std::vector<Player*>::iterator
1333                                 i = m_players.begin();
1334                                 i != m_players.end(); ++i) {
1335                         Player *player = *i;
1336                         // Ignore disconnected players
1337                         if(player->peer_id == 0)
1338                                 continue;
1339
1340                         v3s16 blockpos = getNodeBlockPos(
1341                                         floatToInt(player->getPosition(), BS));
1342                         players_blockpos.push_back(blockpos);
1343                 }
1344
1345                 /*
1346                         Update list of active blocks, collecting changes
1347                 */
1348                 static const s16 active_block_range = g_settings->getS16("active_block_range");
1349                 std::set<v3s16> blocks_removed;
1350                 std::set<v3s16> blocks_added;
1351                 m_active_blocks.update(players_blockpos, active_block_range,
1352                                 blocks_removed, blocks_added);
1353
1354                 /*
1355                         Handle removed blocks
1356                 */
1357
1358                 // Convert active objects that are no more in active blocks to static
1359                 deactivateFarObjects(false);
1360
1361                 for(std::set<v3s16>::iterator
1362                                 i = blocks_removed.begin();
1363                                 i != blocks_removed.end(); ++i) {
1364                         v3s16 p = *i;
1365
1366                         /* infostream<<"Server: Block " << PP(p)
1367                                 << " became inactive"<<std::endl; */
1368
1369                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1370                         if(block==NULL)
1371                                 continue;
1372
1373                         // Set current time as timestamp (and let it set ChangedFlag)
1374                         block->setTimestamp(m_game_time);
1375                 }
1376
1377                 /*
1378                         Handle added blocks
1379                 */
1380
1381                 for(std::set<v3s16>::iterator
1382                                 i = blocks_added.begin();
1383                                 i != blocks_added.end(); ++i)
1384                 {
1385                         v3s16 p = *i;
1386
1387                         MapBlock *block = m_map->getBlockOrEmerge(p);
1388                         if(block==NULL){
1389                                 m_active_blocks.m_list.erase(p);
1390                                 continue;
1391                         }
1392
1393                         activateBlock(block);
1394                         /* infostream<<"Server: Block " << PP(p)
1395                                 << " became active"<<std::endl; */
1396                 }
1397         }
1398
1399         /*
1400                 Mess around in active blocks
1401         */
1402         if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1403         {
1404                 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1405
1406                 float dtime = 1.0;
1407
1408                 for(std::set<v3s16>::iterator
1409                                 i = m_active_blocks.m_list.begin();
1410                                 i != m_active_blocks.m_list.end(); ++i)
1411                 {
1412                         v3s16 p = *i;
1413
1414                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1415                                         <<") being handled"<<std::endl;*/
1416
1417                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1418                         if(block==NULL)
1419                                 continue;
1420
1421                         // Reset block usage timer
1422                         block->resetUsageTimer();
1423
1424                         // Set current time as timestamp
1425                         block->setTimestampNoChangedFlag(m_game_time);
1426                         // If time has changed much from the one on disk,
1427                         // set block to be saved when it is unloaded
1428                         if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1429                                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1430                                         MOD_REASON_BLOCK_EXPIRED);
1431
1432                         // Run node timers
1433                         std::map<v3s16, NodeTimer> elapsed_timers =
1434                                 block->m_node_timers.step((float)dtime);
1435                         if(!elapsed_timers.empty()){
1436                                 MapNode n;
1437                                 for(std::map<v3s16, NodeTimer>::iterator
1438                                                 i = elapsed_timers.begin();
1439                                                 i != elapsed_timers.end(); ++i){
1440                                         n = block->getNodeNoEx(i->first);
1441                                         p = i->first + block->getPosRelative();
1442                                         if(m_script->node_on_timer(p,n,i->second.elapsed))
1443                                                 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1444                                 }
1445                         }
1446                 }
1447         }
1448
1449         const float abm_interval = 1.0;
1450         if(m_active_block_modifier_interval.step(dtime, abm_interval))
1451         do{ // breakable
1452                 if(m_active_block_interval_overload_skip > 0){
1453                         ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1454                         m_active_block_interval_overload_skip--;
1455                         break;
1456                 }
1457                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1458                 TimeTaker timer("modify in active blocks");
1459
1460                 // Initialize handling of ActiveBlockModifiers
1461                 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1462
1463                 for(std::set<v3s16>::iterator
1464                                 i = m_active_blocks.m_list.begin();
1465                                 i != m_active_blocks.m_list.end(); ++i)
1466                 {
1467                         v3s16 p = *i;
1468
1469                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1470                                         <<") being handled"<<std::endl;*/
1471
1472                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1473                         if(block == NULL)
1474                                 continue;
1475
1476                         // Set current time as timestamp
1477                         block->setTimestampNoChangedFlag(m_game_time);
1478
1479                         /* Handle ActiveBlockModifiers */
1480                         abmhandler.apply(block);
1481                 }
1482
1483                 u32 time_ms = timer.stop(true);
1484                 u32 max_time_ms = 200;
1485                 if(time_ms > max_time_ms){
1486                         warningstream<<"active block modifiers took "
1487                                         <<time_ms<<"ms (longer than "
1488                                         <<max_time_ms<<"ms)"<<std::endl;
1489                         m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1490                 }
1491         }while(0);
1492
1493         /*
1494                 Step script environment (run global on_step())
1495         */
1496         m_script->environment_Step(dtime);
1497
1498         /*
1499                 Step active objects
1500         */
1501         {
1502                 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1503                 //TimeTaker timer("Step active objects");
1504
1505                 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1506
1507                 // This helps the objects to send data at the same time
1508                 bool send_recommended = false;
1509                 m_send_recommended_timer += dtime;
1510                 if(m_send_recommended_timer > getSendRecommendedInterval())
1511                 {
1512                         m_send_recommended_timer -= getSendRecommendedInterval();
1513                         send_recommended = true;
1514                 }
1515
1516                 for(std::map<u16, ServerActiveObject*>::iterator
1517                                 i = m_active_objects.begin();
1518                                 i != m_active_objects.end(); ++i)
1519                 {
1520                         ServerActiveObject* obj = i->second;
1521                         // Don't step if is to be removed or stored statically
1522                         if(obj->m_removed || obj->m_pending_deactivation)
1523                                 continue;
1524                         // Step object
1525                         obj->step(dtime, send_recommended);
1526                         // Read messages from object
1527                         while(!obj->m_messages_out.empty())
1528                         {
1529                                 m_active_object_messages.push(
1530                                                 obj->m_messages_out.front());
1531                                 obj->m_messages_out.pop();
1532                         }
1533                 }
1534         }
1535
1536         /*
1537                 Manage active objects
1538         */
1539         if(m_object_management_interval.step(dtime, 0.5))
1540         {
1541                 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1542                 /*
1543                         Remove objects that satisfy (m_removed && m_known_by_count==0)
1544                 */
1545                 removeRemovedObjects();
1546         }
1547 }
1548
1549 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1550 {
1551         std::map<u16, ServerActiveObject*>::iterator n;
1552         n = m_active_objects.find(id);
1553         if(n == m_active_objects.end())
1554                 return NULL;
1555         return n->second;
1556 }
1557
1558 bool isFreeServerActiveObjectId(u16 id,
1559                 std::map<u16, ServerActiveObject*> &objects)
1560 {
1561         if(id == 0)
1562                 return false;
1563
1564         return objects.find(id) == objects.end();
1565 }
1566
1567 u16 getFreeServerActiveObjectId(
1568                 std::map<u16, ServerActiveObject*> &objects)
1569 {
1570         //try to reuse id's as late as possible
1571         static u16 last_used_id = 0;
1572         u16 startid = last_used_id;
1573         for(;;)
1574         {
1575                 last_used_id ++;
1576                 if(isFreeServerActiveObjectId(last_used_id, objects))
1577                         return last_used_id;
1578
1579                 if(last_used_id == startid)
1580                         return 0;
1581         }
1582 }
1583
1584 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1585 {
1586         assert(object); // Pre-condition
1587         m_added_objects++;
1588         u16 id = addActiveObjectRaw(object, true, 0);
1589         return id;
1590 }
1591
1592 /*
1593         Finds out what new objects have been added to
1594         inside a radius around a position
1595 */
1596 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1597                 s16 player_radius,
1598                 std::set<u16> &current_objects,
1599                 std::queue<u16> &added_objects)
1600 {
1601         f32 radius_f = radius * BS;
1602         f32 player_radius_f = player_radius * BS;
1603
1604         if (player_radius_f < 0)
1605                 player_radius_f = 0;
1606
1607         /*
1608                 Go through the object list,
1609                 - discard m_removed objects,
1610                 - discard objects that are too far away,
1611                 - discard objects that are found in current_objects.
1612                 - add remaining objects to added_objects
1613         */
1614         for(std::map<u16, ServerActiveObject*>::iterator
1615                         i = m_active_objects.begin();
1616                         i != m_active_objects.end(); ++i) {
1617                 u16 id = i->first;
1618
1619                 // Get object
1620                 ServerActiveObject *object = i->second;
1621                 if(object == NULL)
1622                         continue;
1623
1624                 // Discard if removed or deactivating
1625                 if(object->m_removed || object->m_pending_deactivation)
1626                         continue;
1627
1628                 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1629                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1630                         // Discard if too far
1631                         if (distance_f > player_radius_f && player_radius_f != 0)
1632                                 continue;
1633                 } else if (distance_f > radius_f)
1634                         continue;
1635
1636                 // Discard if already on current_objects
1637                 std::set<u16>::iterator n;
1638                 n = current_objects.find(id);
1639                 if(n != current_objects.end())
1640                         continue;
1641                 // Add to added_objects
1642                 added_objects.push(id);
1643         }
1644 }
1645
1646 /*
1647         Finds out what objects have been removed from
1648         inside a radius around a position
1649 */
1650 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1651                 s16 player_radius,
1652                 std::set<u16> &current_objects,
1653                 std::queue<u16> &removed_objects)
1654 {
1655         f32 radius_f = radius * BS;
1656         f32 player_radius_f = player_radius * BS;
1657
1658         if (player_radius_f < 0)
1659                 player_radius_f = 0;
1660
1661         /*
1662                 Go through current_objects; object is removed if:
1663                 - object is not found in m_active_objects (this is actually an
1664                   error condition; objects should be set m_removed=true and removed
1665                   only after all clients have been informed about removal), or
1666                 - object has m_removed=true, or
1667                 - object is too far away
1668         */
1669         for(std::set<u16>::iterator
1670                         i = current_objects.begin();
1671                         i != current_objects.end(); ++i)
1672         {
1673                 u16 id = *i;
1674                 ServerActiveObject *object = getActiveObject(id);
1675
1676                 if (object == NULL) {
1677                         infostream << "ServerEnvironment::getRemovedActiveObjects():"
1678                                 << " object in current_objects is NULL" << std::endl;
1679                         removed_objects.push(id);
1680                         continue;
1681                 }
1682
1683                 if (object->m_removed || object->m_pending_deactivation) {
1684                         removed_objects.push(id);
1685                         continue;
1686                 }
1687
1688                 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1689                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1690                         if (distance_f <= player_radius_f || player_radius_f == 0)
1691                                 continue;
1692                 } else if (distance_f <= radius_f)
1693                         continue;
1694
1695                 // Object is no longer visible
1696                 removed_objects.push(id);
1697         }
1698 }
1699
1700 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1701         v3s16 blockpos, bool static_exists, v3s16 static_block)
1702 {
1703         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1704         if (!block)
1705                 return;
1706
1707         for (std::map<u16, StaticObject>::iterator
1708                         so_it = block->m_static_objects.m_active.begin();
1709                         so_it != block->m_static_objects.m_active.end(); ++so_it) {
1710                 // Get the ServerActiveObject counterpart to this StaticObject
1711                 std::map<u16, ServerActiveObject *>::iterator ao_it;
1712                 ao_it = m_active_objects.find(so_it->first);
1713                 if (ao_it == m_active_objects.end()) {
1714                         // If this ever happens, there must be some kind of nasty bug.
1715                         errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1716                                 "Object from MapBlock::m_static_objects::m_active not found "
1717                                 "in m_active_objects";
1718                         continue;
1719                 }
1720
1721                 ServerActiveObject *sao = ao_it->second;
1722                 sao->m_static_exists = static_exists;
1723                 sao->m_static_block  = static_block;
1724         }
1725 }
1726
1727 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1728 {
1729         if(m_active_object_messages.empty())
1730                 return ActiveObjectMessage(0);
1731
1732         ActiveObjectMessage message = m_active_object_messages.front();
1733         m_active_object_messages.pop();
1734         return message;
1735 }
1736
1737 /*
1738         ************ Private methods *************
1739 */
1740
1741 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1742                 bool set_changed, u32 dtime_s)
1743 {
1744         assert(object); // Pre-condition
1745         if(object->getId() == 0){
1746                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1747                 if(new_id == 0)
1748                 {
1749                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1750                                         <<"no free ids available"<<std::endl;
1751                         if(object->environmentDeletes())
1752                                 delete object;
1753                         return 0;
1754                 }
1755                 object->setId(new_id);
1756         }
1757         else{
1758                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1759                                 <<"supplied with id "<<object->getId()<<std::endl;
1760         }
1761         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1762         {
1763                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1764                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1765                 if(object->environmentDeletes())
1766                         delete object;
1767                 return 0;
1768         }
1769
1770         if (objectpos_over_limit(object->getBasePosition())) {
1771                 v3f p = object->getBasePosition();
1772                 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1773                         << "object position (" << p.X << "," << p.Y << "," << p.Z
1774                         << ") outside maximum range" << std::endl;
1775                 if (object->environmentDeletes())
1776                         delete object;
1777                 return 0;
1778         }
1779
1780         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1781                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1782
1783         m_active_objects[object->getId()] = object;
1784
1785         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1786                         <<"Added id="<<object->getId()<<"; there are now "
1787                         <<m_active_objects.size()<<" active objects."
1788                         <<std::endl;
1789
1790         // Register reference in scripting api (must be done before post-init)
1791         m_script->addObjectReference(object);
1792         // Post-initialize object
1793         object->addedToEnvironment(dtime_s);
1794
1795         // Add static data to block
1796         if(object->isStaticAllowed())
1797         {
1798                 // Add static object to active static list of the block
1799                 v3f objectpos = object->getBasePosition();
1800                 std::string staticdata = object->getStaticData();
1801                 StaticObject s_obj(object->getType(), objectpos, staticdata);
1802                 // Add to the block where the object is located in
1803                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1804                 MapBlock *block = m_map->emergeBlock(blockpos);
1805                 if(block){
1806                         block->m_static_objects.m_active[object->getId()] = s_obj;
1807                         object->m_static_exists = true;
1808                         object->m_static_block = blockpos;
1809
1810                         if(set_changed)
1811                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1812                                         MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1813                 } else {
1814                         v3s16 p = floatToInt(objectpos, BS);
1815                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1816                                         <<"could not emerge block for storing id="<<object->getId()
1817                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
1818                 }
1819         }
1820
1821         return object->getId();
1822 }
1823
1824 /*
1825         Remove objects that satisfy (m_removed && m_known_by_count==0)
1826 */
1827 void ServerEnvironment::removeRemovedObjects()
1828 {
1829         std::vector<u16> objects_to_remove;
1830         for(std::map<u16, ServerActiveObject*>::iterator
1831                         i = m_active_objects.begin();
1832                         i != m_active_objects.end(); ++i) {
1833                 u16 id = i->first;
1834                 ServerActiveObject* obj = i->second;
1835                 // This shouldn't happen but check it
1836                 if(obj == NULL)
1837                 {
1838                         infostream<<"NULL object found in ServerEnvironment"
1839                                         <<" while finding removed objects. id="<<id<<std::endl;
1840                         // Id to be removed from m_active_objects
1841                         objects_to_remove.push_back(id);
1842                         continue;
1843                 }
1844
1845                 /*
1846                         We will delete objects that are marked as removed or thatare
1847                         waiting for deletion after deactivation
1848                 */
1849                 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1850                         continue;
1851
1852                 /*
1853                         Delete static data from block if is marked as removed
1854                 */
1855                 if(obj->m_static_exists && obj->m_removed)
1856                 {
1857                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1858                         if (block) {
1859                                 block->m_static_objects.remove(id);
1860                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1861                                         MOD_REASON_REMOVE_OBJECTS_REMOVE);
1862                                 obj->m_static_exists = false;
1863                         } else {
1864                                 infostream<<"Failed to emerge block from which an object to "
1865                                                 <<"be removed was loaded from. id="<<id<<std::endl;
1866                         }
1867                 }
1868
1869                 // If m_known_by_count > 0, don't actually remove. On some future
1870                 // invocation this will be 0, which is when removal will continue.
1871                 if(obj->m_known_by_count > 0)
1872                         continue;
1873
1874                 /*
1875                         Move static data from active to stored if not marked as removed
1876                 */
1877                 if(obj->m_static_exists && !obj->m_removed){
1878                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1879                         if (block) {
1880                                 std::map<u16, StaticObject>::iterator i =
1881                                                 block->m_static_objects.m_active.find(id);
1882                                 if(i != block->m_static_objects.m_active.end()){
1883                                         block->m_static_objects.m_stored.push_back(i->second);
1884                                         block->m_static_objects.m_active.erase(id);
1885                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1886                                                 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1887                                 }
1888                         } else {
1889                                 infostream<<"Failed to emerge block from which an object to "
1890                                                 <<"be deactivated was loaded from. id="<<id<<std::endl;
1891                         }
1892                 }
1893
1894                 // Tell the object about removal
1895                 obj->removingFromEnvironment();
1896                 // Deregister in scripting api
1897                 m_script->removeObjectReference(obj);
1898
1899                 // Delete
1900                 if(obj->environmentDeletes())
1901                         delete obj;
1902
1903                 // Id to be removed from m_active_objects
1904                 objects_to_remove.push_back(id);
1905         }
1906         // Remove references from m_active_objects
1907         for(std::vector<u16>::iterator i = objects_to_remove.begin();
1908                         i != objects_to_remove.end(); ++i) {
1909                 m_active_objects.erase(*i);
1910         }
1911 }
1912
1913 static void print_hexdump(std::ostream &o, const std::string &data)
1914 {
1915         const int linelength = 16;
1916         for(int l=0; ; l++){
1917                 int i0 = linelength * l;
1918                 bool at_end = false;
1919                 int thislinelength = linelength;
1920                 if(i0 + thislinelength > (int)data.size()){
1921                         thislinelength = data.size() - i0;
1922                         at_end = true;
1923                 }
1924                 for(int di=0; di<linelength; di++){
1925                         int i = i0 + di;
1926                         char buf[4];
1927                         if(di<thislinelength)
1928                                 snprintf(buf, 4, "%.2x ", data[i]);
1929                         else
1930                                 snprintf(buf, 4, "   ");
1931                         o<<buf;
1932                 }
1933                 o<<" ";
1934                 for(int di=0; di<thislinelength; di++){
1935                         int i = i0 + di;
1936                         if(data[i] >= 32)
1937                                 o<<data[i];
1938                         else
1939                                 o<<".";
1940                 }
1941                 o<<std::endl;
1942                 if(at_end)
1943                         break;
1944         }
1945 }
1946
1947 /*
1948         Convert stored objects from blocks near the players to active.
1949 */
1950 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1951 {
1952         if(block == NULL)
1953                 return;
1954
1955         // Ignore if no stored objects (to not set changed flag)
1956         if(block->m_static_objects.m_stored.empty())
1957                 return;
1958
1959         verbosestream<<"ServerEnvironment::activateObjects(): "
1960                         <<"activating objects of block "<<PP(block->getPos())
1961                         <<" ("<<block->m_static_objects.m_stored.size()
1962                         <<" objects)"<<std::endl;
1963         bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1964         if (large_amount) {
1965                 errorstream<<"suspiciously large amount of objects detected: "
1966                                 <<block->m_static_objects.m_stored.size()<<" in "
1967                                 <<PP(block->getPos())
1968                                 <<"; removing all of them."<<std::endl;
1969                 // Clear stored list
1970                 block->m_static_objects.m_stored.clear();
1971                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1972                         MOD_REASON_TOO_MANY_OBJECTS);
1973                 return;
1974         }
1975
1976         // Activate stored objects
1977         std::vector<StaticObject> new_stored;
1978         for (std::vector<StaticObject>::iterator
1979                         i = block->m_static_objects.m_stored.begin();
1980                         i != block->m_static_objects.m_stored.end(); ++i) {
1981                 StaticObject &s_obj = *i;
1982
1983                 // Create an active object from the data
1984                 ServerActiveObject *obj = ServerActiveObject::create
1985                                 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1986                 // If couldn't create object, store static data back.
1987                 if(obj == NULL) {
1988                         errorstream<<"ServerEnvironment::activateObjects(): "
1989                                         <<"failed to create active object from static object "
1990                                         <<"in block "<<PP(s_obj.pos/BS)
1991                                         <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1992                         print_hexdump(verbosestream, s_obj.data);
1993
1994                         new_stored.push_back(s_obj);
1995                         continue;
1996                 }
1997                 verbosestream<<"ServerEnvironment::activateObjects(): "
1998                                 <<"activated static object pos="<<PP(s_obj.pos/BS)
1999                                 <<" type="<<(int)s_obj.type<<std::endl;
2000                 // This will also add the object to the active static list
2001                 addActiveObjectRaw(obj, false, dtime_s);
2002         }
2003         // Clear stored list
2004         block->m_static_objects.m_stored.clear();
2005         // Add leftover failed stuff to stored list
2006         for(std::vector<StaticObject>::iterator
2007                         i = new_stored.begin();
2008                         i != new_stored.end(); ++i) {
2009                 StaticObject &s_obj = *i;
2010                 block->m_static_objects.m_stored.push_back(s_obj);
2011         }
2012
2013         // Turn the active counterparts of activated objects not pending for
2014         // deactivation
2015         for(std::map<u16, StaticObject>::iterator
2016                         i = block->m_static_objects.m_active.begin();
2017                         i != block->m_static_objects.m_active.end(); ++i)
2018         {
2019                 u16 id = i->first;
2020                 ServerActiveObject *object = getActiveObject(id);
2021                 assert(object);
2022                 object->m_pending_deactivation = false;
2023         }
2024
2025         /*
2026                 Note: Block hasn't really been modified here.
2027                 The objects have just been activated and moved from the stored
2028                 static list to the active static list.
2029                 As such, the block is essentially the same.
2030                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2031                 Otherwise there would be a huge amount of unnecessary I/O.
2032         */
2033 }
2034
2035 /*
2036         Convert objects that are not standing inside active blocks to static.
2037
2038         If m_known_by_count != 0, active object is not deleted, but static
2039         data is still updated.
2040
2041         If force_delete is set, active object is deleted nevertheless. It
2042         shall only be set so in the destructor of the environment.
2043
2044         If block wasn't generated (not in memory or on disk),
2045 */
2046 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2047 {
2048         std::vector<u16> objects_to_remove;
2049         for(std::map<u16, ServerActiveObject*>::iterator
2050                         i = m_active_objects.begin();
2051                         i != m_active_objects.end(); ++i) {
2052                 ServerActiveObject* obj = i->second;
2053                 assert(obj);
2054
2055                 // Do not deactivate if static data creation not allowed
2056                 if(!force_delete && !obj->isStaticAllowed())
2057                         continue;
2058
2059                 // If pending deactivation, let removeRemovedObjects() do it
2060                 if(!force_delete && obj->m_pending_deactivation)
2061                         continue;
2062
2063                 u16 id = i->first;
2064                 v3f objectpos = obj->getBasePosition();
2065
2066                 // The block in which the object resides in
2067                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2068
2069                 // If object's static data is stored in a deactivated block and object
2070                 // is actually located in an active block, re-save to the block in
2071                 // which the object is actually located in.
2072                 if(!force_delete &&
2073                                 obj->m_static_exists &&
2074                                 !m_active_blocks.contains(obj->m_static_block) &&
2075                                  m_active_blocks.contains(blockpos_o))
2076                 {
2077                         v3s16 old_static_block = obj->m_static_block;
2078
2079                         // Save to block where object is located
2080                         MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2081                         if(!block){
2082                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2083                                                 <<"Could not save object id="<<id
2084                                                 <<" to it's current block "<<PP(blockpos_o)
2085                                                 <<std::endl;
2086                                 continue;
2087                         }
2088                         std::string staticdata_new = obj->getStaticData();
2089                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2090                         block->m_static_objects.insert(id, s_obj);
2091                         obj->m_static_block = blockpos_o;
2092                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2093                                 MOD_REASON_STATIC_DATA_ADDED);
2094
2095                         // Delete from block where object was located
2096                         block = m_map->emergeBlock(old_static_block, false);
2097                         if(!block){
2098                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2099                                                 <<"Could not delete object id="<<id
2100                                                 <<" from it's previous block "<<PP(old_static_block)
2101                                                 <<std::endl;
2102                                 continue;
2103                         }
2104                         block->m_static_objects.remove(id);
2105                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2106                                 MOD_REASON_STATIC_DATA_REMOVED);
2107                         continue;
2108                 }
2109
2110                 // If block is active, don't remove
2111                 if(!force_delete && m_active_blocks.contains(blockpos_o))
2112                         continue;
2113
2114                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2115                                 <<"deactivating object id="<<id<<" on inactive block "
2116                                 <<PP(blockpos_o)<<std::endl;
2117
2118                 // If known by some client, don't immediately delete.
2119                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2120
2121                 /*
2122                         Update the static data
2123                 */
2124
2125                 if(obj->isStaticAllowed())
2126                 {
2127                         // Create new static object
2128                         std::string staticdata_new = obj->getStaticData();
2129                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2130
2131                         bool stays_in_same_block = false;
2132                         bool data_changed = true;
2133
2134                         if (obj->m_static_exists) {
2135                                 if (obj->m_static_block == blockpos_o)
2136                                         stays_in_same_block = true;
2137
2138                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2139
2140                                 if (block) {
2141                                         std::map<u16, StaticObject>::iterator n =
2142                                                 block->m_static_objects.m_active.find(id);
2143                                         if (n != block->m_static_objects.m_active.end()) {
2144                                                 StaticObject static_old = n->second;
2145
2146                                                 float save_movem = obj->getMinimumSavedMovement();
2147
2148                                                 if (static_old.data == staticdata_new &&
2149                                                                 (static_old.pos - objectpos).getLength() < save_movem)
2150                                                         data_changed = false;
2151                                         } else {
2152                                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2153                                                         <<"id="<<id<<" m_static_exists=true but "
2154                                                         <<"static data doesn't actually exist in "
2155                                                         <<PP(obj->m_static_block)<<std::endl;
2156                                         }
2157                                 }
2158                         }
2159
2160                         bool shall_be_written = (!stays_in_same_block || data_changed);
2161
2162                         // Delete old static object
2163                         if(obj->m_static_exists)
2164                         {
2165                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2166                                 if(block)
2167                                 {
2168                                         block->m_static_objects.remove(id);
2169                                         obj->m_static_exists = false;
2170                                         // Only mark block as modified if data changed considerably
2171                                         if(shall_be_written)
2172                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2173                                                         MOD_REASON_STATIC_DATA_CHANGED);
2174                                 }
2175                         }
2176
2177                         // Add to the block where the object is located in
2178                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2179                         // Get or generate the block
2180                         MapBlock *block = NULL;
2181                         try{
2182                                 block = m_map->emergeBlock(blockpos);
2183                         } catch(InvalidPositionException &e){
2184                                 // Handled via NULL pointer
2185                                 // NOTE: emergeBlock's failure is usually determined by it
2186                                 //       actually returning NULL
2187                         }
2188
2189                         if(block)
2190                         {
2191                                 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2192                                         errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2193                                                         <<" statically but block "<<PP(blockpos)
2194                                                         <<" already contains "
2195                                                         <<block->m_static_objects.m_stored.size()
2196                                                         <<" objects."
2197                                                         <<" Forcing delete."<<std::endl;
2198                                         force_delete = true;
2199                                 } else {
2200                                         // If static counterpart already exists in target block,
2201                                         // remove it first.
2202                                         // This shouldn't happen because the object is removed from
2203                                         // the previous block before this according to
2204                                         // obj->m_static_block, but happens rarely for some unknown
2205                                         // reason. Unsuccessful attempts have been made to find
2206                                         // said reason.
2207                                         if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2208                                                 warningstream<<"ServerEnv: Performing hack #83274"
2209                                                                 <<std::endl;
2210                                                 block->m_static_objects.remove(id);
2211                                         }
2212                                         // Store static data
2213                                         u16 store_id = pending_delete ? id : 0;
2214                                         block->m_static_objects.insert(store_id, s_obj);
2215
2216                                         // Only mark block as modified if data changed considerably
2217                                         if(shall_be_written)
2218                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2219                                                         MOD_REASON_STATIC_DATA_CHANGED);
2220
2221                                         obj->m_static_exists = true;
2222                                         obj->m_static_block = block->getPos();
2223                                 }
2224                         }
2225                         else{
2226                                 if(!force_delete){
2227                                         v3s16 p = floatToInt(objectpos, BS);
2228                                         errorstream<<"ServerEnv: Could not find or generate "
2229                                                         <<"a block for storing id="<<obj->getId()
2230                                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
2231                                         continue;
2232                                 }
2233                         }
2234                 }
2235
2236                 /*
2237                         If known by some client, set pending deactivation.
2238                         Otherwise delete it immediately.
2239                 */
2240
2241                 if(pending_delete && !force_delete)
2242                 {
2243                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2244                                         <<"object id="<<id<<" is known by clients"
2245                                         <<"; not deleting yet"<<std::endl;
2246
2247                         obj->m_pending_deactivation = true;
2248                         continue;
2249                 }
2250
2251                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2252                                 <<"object id="<<id<<" is not known by clients"
2253                                 <<"; deleting"<<std::endl;
2254
2255                 // Tell the object about removal
2256                 obj->removingFromEnvironment();
2257                 // Deregister in scripting api
2258                 m_script->removeObjectReference(obj);
2259
2260                 // Delete active object
2261                 if(obj->environmentDeletes())
2262                         delete obj;
2263                 // Id to be removed from m_active_objects
2264                 objects_to_remove.push_back(id);
2265         }
2266
2267         // Remove references from m_active_objects
2268         for(std::vector<u16>::iterator i = objects_to_remove.begin();
2269                         i != objects_to_remove.end(); ++i) {
2270                 m_active_objects.erase(*i);
2271         }
2272 }
2273
2274 #ifndef SERVER
2275
2276 #include "clientsimpleobject.h"
2277
2278 /*
2279         ClientEnvironment
2280 */
2281
2282 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2283                 ITextureSource *texturesource, IGameDef *gamedef,
2284                 IrrlichtDevice *irr):
2285         m_map(map),
2286         m_smgr(smgr),
2287         m_texturesource(texturesource),
2288         m_gamedef(gamedef),
2289         m_irr(irr)
2290 {
2291         char zero = 0;
2292         memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2293 }
2294
2295 ClientEnvironment::~ClientEnvironment()
2296 {
2297         // delete active objects
2298         for(std::map<u16, ClientActiveObject*>::iterator
2299                         i = m_active_objects.begin();
2300                         i != m_active_objects.end(); ++i)
2301         {
2302                 delete i->second;
2303         }
2304
2305         for(std::vector<ClientSimpleObject*>::iterator
2306                         i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2307                 delete *i;
2308         }
2309
2310         // Drop/delete map
2311         m_map->drop();
2312 }
2313
2314 Map & ClientEnvironment::getMap()
2315 {
2316         return *m_map;
2317 }
2318
2319 ClientMap & ClientEnvironment::getClientMap()
2320 {
2321         return *m_map;
2322 }
2323
2324 void ClientEnvironment::addPlayer(Player *player)
2325 {
2326         DSTACK(FUNCTION_NAME);
2327         /*
2328                 It is a failure if player is local and there already is a local
2329                 player
2330         */
2331         FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2332                 "Player is local but there is already a local player");
2333
2334         Environment::addPlayer(player);
2335 }
2336
2337 LocalPlayer * ClientEnvironment::getLocalPlayer()
2338 {
2339         for(std::vector<Player*>::iterator i = m_players.begin();
2340                         i != m_players.end(); ++i) {
2341                 Player *player = *i;
2342                 if(player->isLocal())
2343                         return (LocalPlayer*)player;
2344         }
2345         return NULL;
2346 }
2347
2348 void ClientEnvironment::step(float dtime)
2349 {
2350         DSTACK(FUNCTION_NAME);
2351
2352         /* Step time of day */
2353         stepTimeOfDay(dtime);
2354
2355         // Get some settings
2356         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2357         bool free_move = fly_allowed && g_settings->getBool("free_move");
2358
2359         // Get local player
2360         LocalPlayer *lplayer = getLocalPlayer();
2361         assert(lplayer);
2362         // collision info queue
2363         std::vector<CollisionInfo> player_collisions;
2364
2365         /*
2366                 Get the speed the player is going
2367         */
2368         bool is_climbing = lplayer->is_climbing;
2369
2370         f32 player_speed = lplayer->getSpeed().getLength();
2371
2372         /*
2373                 Maximum position increment
2374         */
2375         //f32 position_max_increment = 0.05*BS;
2376         f32 position_max_increment = 0.1*BS;
2377
2378         // Maximum time increment (for collision detection etc)
2379         // time = distance / speed
2380         f32 dtime_max_increment = 1;
2381         if(player_speed > 0.001)
2382                 dtime_max_increment = position_max_increment / player_speed;
2383
2384         // Maximum time increment is 10ms or lower
2385         if(dtime_max_increment > 0.01)
2386                 dtime_max_increment = 0.01;
2387
2388         // Don't allow overly huge dtime
2389         if(dtime > 0.5)
2390                 dtime = 0.5;
2391
2392         f32 dtime_downcount = dtime;
2393
2394         /*
2395                 Stuff that has a maximum time increment
2396         */
2397
2398         u32 loopcount = 0;
2399         do
2400         {
2401                 loopcount++;
2402
2403                 f32 dtime_part;
2404                 if(dtime_downcount > dtime_max_increment)
2405                 {
2406                         dtime_part = dtime_max_increment;
2407                         dtime_downcount -= dtime_part;
2408                 }
2409                 else
2410                 {
2411                         dtime_part = dtime_downcount;
2412                         /*
2413                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
2414                                 when dtime_part is so small that dtime_downcount -= dtime_part
2415                                 does nothing
2416                         */
2417                         dtime_downcount = 0;
2418                 }
2419
2420                 /*
2421                         Handle local player
2422                 */
2423
2424                 {
2425                         // Apply physics
2426                         if(free_move == false && is_climbing == false)
2427                         {
2428                                 // Gravity
2429                                 v3f speed = lplayer->getSpeed();
2430                                 if(lplayer->in_liquid == false)
2431                                         speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2432
2433                                 // Liquid floating / sinking
2434                                 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2435                                         speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2436
2437                                 // Liquid resistance
2438                                 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2439                                 {
2440                                         // How much the node's viscosity blocks movement, ranges between 0 and 1
2441                                         // Should match the scale at which viscosity increase affects other liquid attributes
2442                                         const f32 viscosity_factor = 0.3;
2443
2444                                         v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2445                                         f32 dl = d_wanted.getLength();
2446                                         if(dl > lplayer->movement_liquid_fluidity_smooth)
2447                                                 dl = lplayer->movement_liquid_fluidity_smooth;
2448                                         dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2449
2450                                         v3f d = d_wanted.normalize() * dl;
2451                                         speed += d;
2452                                 }
2453
2454                                 lplayer->setSpeed(speed);
2455                         }
2456
2457                         /*
2458                                 Move the lplayer.
2459                                 This also does collision detection.
2460                         */
2461                         lplayer->move(dtime_part, this, position_max_increment,
2462                                         &player_collisions);
2463                 }
2464         }
2465         while(dtime_downcount > 0.001);
2466
2467         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2468
2469         for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2470                         i != player_collisions.end(); ++i) {
2471                 CollisionInfo &info = *i;
2472                 v3f speed_diff = info.new_speed - info.old_speed;;
2473                 // Handle only fall damage
2474                 // (because otherwise walking against something in fast_move kills you)
2475                 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2476                         continue;
2477                 // Get rid of other components
2478                 speed_diff.X = 0;
2479                 speed_diff.Z = 0;
2480                 f32 pre_factor = 1; // 1 hp per node/s
2481                 f32 tolerance = BS*14; // 5 without damage
2482                 f32 post_factor = 1; // 1 hp per node/s
2483                 if(info.type == COLLISION_NODE)
2484                 {
2485                         const ContentFeatures &f = m_gamedef->ndef()->
2486                                         get(m_map->getNodeNoEx(info.node_p));
2487                         // Determine fall damage multiplier
2488                         int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2489                         pre_factor = 1.0 + (float)addp/100.0;
2490                 }
2491                 float speed = pre_factor * speed_diff.getLength();
2492                 if(speed > tolerance)
2493                 {
2494                         f32 damage_f = (speed - tolerance)/BS * post_factor;
2495                         u16 damage = (u16)(damage_f+0.5);
2496                         if(damage != 0){
2497                                 damageLocalPlayer(damage, true);
2498                                 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2499                                 m_gamedef->event()->put(e);
2500                         }
2501                 }
2502         }
2503
2504         /*
2505                 A quick draft of lava damage
2506         */
2507         if(m_lava_hurt_interval.step(dtime, 1.0))
2508         {
2509                 v3f pf = lplayer->getPosition();
2510
2511                 // Feet, middle and head
2512                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2513                 MapNode n1 = m_map->getNodeNoEx(p1);
2514                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2515                 MapNode n2 = m_map->getNodeNoEx(p2);
2516                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2517                 MapNode n3 = m_map->getNodeNoEx(p3);
2518
2519                 u32 damage_per_second = 0;
2520                 damage_per_second = MYMAX(damage_per_second,
2521                                 m_gamedef->ndef()->get(n1).damage_per_second);
2522                 damage_per_second = MYMAX(damage_per_second,
2523                                 m_gamedef->ndef()->get(n2).damage_per_second);
2524                 damage_per_second = MYMAX(damage_per_second,
2525                                 m_gamedef->ndef()->get(n3).damage_per_second);
2526
2527                 if(damage_per_second != 0)
2528                 {
2529                         damageLocalPlayer(damage_per_second, true);
2530                 }
2531         }
2532
2533         /*
2534                 Drowning
2535         */
2536         if(m_drowning_interval.step(dtime, 2.0))
2537         {
2538                 v3f pf = lplayer->getPosition();
2539
2540                 // head
2541                 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2542                 MapNode n = m_map->getNodeNoEx(p);
2543                 ContentFeatures c = m_gamedef->ndef()->get(n);
2544                 u8 drowning_damage = c.drowning;
2545                 if(drowning_damage > 0 && lplayer->hp > 0){
2546                         u16 breath = lplayer->getBreath();
2547                         if(breath > 10){
2548                                 breath = 11;
2549                         }
2550                         if(breath > 0){
2551                                 breath -= 1;
2552                         }
2553                         lplayer->setBreath(breath);
2554                         updateLocalPlayerBreath(breath);
2555                 }
2556
2557                 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2558                         damageLocalPlayer(drowning_damage, true);
2559                 }
2560         }
2561         if(m_breathing_interval.step(dtime, 0.5))
2562         {
2563                 v3f pf = lplayer->getPosition();
2564
2565                 // head
2566                 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2567                 MapNode n = m_map->getNodeNoEx(p);
2568                 ContentFeatures c = m_gamedef->ndef()->get(n);
2569                 if (!lplayer->hp){
2570                         lplayer->setBreath(11);
2571                 }
2572                 else if(c.drowning == 0){
2573                         u16 breath = lplayer->getBreath();
2574                         if(breath <= 10){
2575                                 breath += 1;
2576                                 lplayer->setBreath(breath);
2577                                 updateLocalPlayerBreath(breath);
2578                         }
2579                 }
2580         }
2581
2582         /*
2583                 Stuff that can be done in an arbitarily large dtime
2584         */
2585         for(std::vector<Player*>::iterator i = m_players.begin();
2586                         i != m_players.end(); ++i) {
2587                 Player *player = *i;
2588
2589                 /*
2590                         Handle non-local players
2591                 */
2592                 if(player->isLocal() == false) {
2593                         // Move
2594                         player->move(dtime, this, 100*BS);
2595
2596                 }
2597         }
2598
2599         // Update lighting on local player (used for wield item)
2600         u32 day_night_ratio = getDayNightRatio();
2601         {
2602                 // Get node at head
2603
2604                 // On InvalidPositionException, use this as default
2605                 // (day: LIGHT_SUN, night: 0)
2606                 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2607
2608                 v3s16 p = lplayer->getLightPosition();
2609                 node_at_lplayer = m_map->getNodeNoEx(p);
2610
2611                 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2612                 u8 day = light & 0xff;
2613                 u8 night = (light >> 8) & 0xff;
2614                 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2615         }
2616
2617         /*
2618                 Step active objects and update lighting of them
2619         */
2620
2621         g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2622         bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2623         for(std::map<u16, ClientActiveObject*>::iterator
2624                         i = m_active_objects.begin();
2625                         i != m_active_objects.end(); ++i)
2626         {
2627                 ClientActiveObject* obj = i->second;
2628                 // Step object
2629                 obj->step(dtime, this);
2630
2631                 if(update_lighting)
2632                 {
2633                         // Update lighting
2634                         u8 light = 0;
2635                         bool pos_ok;
2636
2637                         // Get node at head
2638                         v3s16 p = obj->getLightPosition();
2639                         MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2640                         if (pos_ok)
2641                                 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2642                         else
2643                                 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2644
2645                         obj->updateLight(light);
2646                 }
2647         }
2648
2649         /*
2650                 Step and handle simple objects
2651         */
2652         g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2653         for(std::vector<ClientSimpleObject*>::iterator
2654                         i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2655                 std::vector<ClientSimpleObject*>::iterator cur = i;
2656                 ClientSimpleObject *simple = *cur;
2657
2658                 simple->step(dtime);
2659                 if(simple->m_to_be_removed) {
2660                         delete simple;
2661                         i = m_simple_objects.erase(cur);
2662                 }
2663                 else {
2664                         ++i;
2665                 }
2666         }
2667 }
2668
2669 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2670 {
2671         m_simple_objects.push_back(simple);
2672 }
2673
2674 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2675 {
2676         ClientActiveObject *obj = getActiveObject(id);
2677         if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2678                 return (GenericCAO*) obj;
2679         else
2680                 return NULL;
2681 }
2682
2683 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2684 {
2685         std::map<u16, ClientActiveObject*>::iterator n;
2686         n = m_active_objects.find(id);
2687         if(n == m_active_objects.end())
2688                 return NULL;
2689         return n->second;
2690 }
2691
2692 bool isFreeClientActiveObjectId(u16 id,
2693                 std::map<u16, ClientActiveObject*> &objects)
2694 {
2695         if(id == 0)
2696                 return false;
2697
2698         return objects.find(id) == objects.end();
2699 }
2700
2701 u16 getFreeClientActiveObjectId(
2702                 std::map<u16, ClientActiveObject*> &objects)
2703 {
2704         //try to reuse id's as late as possible
2705         static u16 last_used_id = 0;
2706         u16 startid = last_used_id;
2707         for(;;)
2708         {
2709                 last_used_id ++;
2710                 if(isFreeClientActiveObjectId(last_used_id, objects))
2711                         return last_used_id;
2712
2713                 if(last_used_id == startid)
2714                         return 0;
2715         }
2716 }
2717
2718 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2719 {
2720         assert(object); // Pre-condition
2721         if(object->getId() == 0)
2722         {
2723                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2724                 if(new_id == 0)
2725                 {
2726                         infostream<<"ClientEnvironment::addActiveObject(): "
2727                                         <<"no free ids available"<<std::endl;
2728                         delete object;
2729                         return 0;
2730                 }
2731                 object->setId(new_id);
2732         }
2733         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2734         {
2735                 infostream<<"ClientEnvironment::addActiveObject(): "
2736                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2737                 delete object;
2738                 return 0;
2739         }
2740         infostream<<"ClientEnvironment::addActiveObject(): "
2741                         <<"added (id="<<object->getId()<<")"<<std::endl;
2742         m_active_objects[object->getId()] = object;
2743         object->addToScene(m_smgr, m_texturesource, m_irr);
2744         { // Update lighting immediately
2745                 u8 light = 0;
2746                 bool pos_ok;
2747
2748                 // Get node at head
2749                 v3s16 p = object->getLightPosition();
2750                 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2751                 if (pos_ok)
2752                         light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2753                 else
2754                         light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2755
2756                 object->updateLight(light);
2757         }
2758         return object->getId();
2759 }
2760
2761 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2762                 const std::string &init_data)
2763 {
2764         ClientActiveObject* obj =
2765                         ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2766         if(obj == NULL)
2767         {
2768                 infostream<<"ClientEnvironment::addActiveObject(): "
2769                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2770                                 <<std::endl;
2771                 return;
2772         }
2773
2774         obj->setId(id);
2775
2776         try
2777         {
2778                 obj->initialize(init_data);
2779         }
2780         catch(SerializationError &e)
2781         {
2782                 errorstream<<"ClientEnvironment::addActiveObject():"
2783                                 <<" id="<<id<<" type="<<type
2784                                 <<": SerializationError in initialize(): "
2785                                 <<e.what()
2786                                 <<": init_data="<<serializeJsonString(init_data)
2787                                 <<std::endl;
2788         }
2789
2790         addActiveObject(obj);
2791 }
2792
2793 void ClientEnvironment::removeActiveObject(u16 id)
2794 {
2795         verbosestream<<"ClientEnvironment::removeActiveObject(): "
2796                         <<"id="<<id<<std::endl;
2797         ClientActiveObject* obj = getActiveObject(id);
2798         if(obj == NULL)
2799         {
2800                 infostream<<"ClientEnvironment::removeActiveObject(): "
2801                                 <<"id="<<id<<" not found"<<std::endl;
2802                 return;
2803         }
2804         obj->removeFromScene(true);
2805         delete obj;
2806         m_active_objects.erase(id);
2807 }
2808
2809 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2810 {
2811         ClientActiveObject *obj = getActiveObject(id);
2812         if (obj == NULL) {
2813                 infostream << "ClientEnvironment::processActiveObjectMessage():"
2814                         << " got message for id=" << id << ", which doesn't exist."
2815                         << std::endl;
2816                 return;
2817         }
2818
2819         try {
2820                 obj->processMessage(data);
2821         } catch (SerializationError &e) {
2822                 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2823                         << " id=" << id << " type=" << obj->getType()
2824                         << " SerializationError in processMessage(): " << e.what()
2825                         << std::endl;
2826         }
2827 }
2828
2829 /*
2830         Callbacks for activeobjects
2831 */
2832
2833 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2834 {
2835         LocalPlayer *lplayer = getLocalPlayer();
2836         assert(lplayer);
2837
2838         if (handle_hp) {
2839                 if (lplayer->hp > damage)
2840                         lplayer->hp -= damage;
2841                 else
2842                         lplayer->hp = 0;
2843         }
2844
2845         ClientEnvEvent event;
2846         event.type = CEE_PLAYER_DAMAGE;
2847         event.player_damage.amount = damage;
2848         event.player_damage.send_to_server = handle_hp;
2849         m_client_event_queue.push(event);
2850 }
2851
2852 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2853 {
2854         ClientEnvEvent event;
2855         event.type = CEE_PLAYER_BREATH;
2856         event.player_breath.amount = breath;
2857         m_client_event_queue.push(event);
2858 }
2859
2860 /*
2861         Client likes to call these
2862 */
2863
2864 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2865                 std::vector<DistanceSortedActiveObject> &dest)
2866 {
2867         for(std::map<u16, ClientActiveObject*>::iterator
2868                         i = m_active_objects.begin();
2869                         i != m_active_objects.end(); ++i)
2870         {
2871                 ClientActiveObject* obj = i->second;
2872
2873                 f32 d = (obj->getPosition() - origin).getLength();
2874
2875                 if(d > max_d)
2876                         continue;
2877
2878                 DistanceSortedActiveObject dso(obj, d);
2879
2880                 dest.push_back(dso);
2881         }
2882 }
2883
2884 ClientEnvEvent ClientEnvironment::getClientEvent()
2885 {
2886         ClientEnvEvent event;
2887         if(m_client_event_queue.empty())
2888                 event.type = CEE_NONE;
2889         else {
2890                 event = m_client_event_queue.front();
2891                 m_client_event_queue.pop();
2892         }
2893         return event;
2894 }
2895
2896 #endif // #ifndef SERVER