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