Make node timers more efficient
[oweals/minetest.git] / src / environment.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <fstream>
21 #include "environment.h"
22 #include "filesys.h"
23 #include "porting.h"
24 #include "collision.h"
25 #include "content_mapnode.h"
26 #include "mapblock.h"
27 #include "serverobject.h"
28 #include "content_sao.h"
29 #include "settings.h"
30 #include "log.h"
31 #include "profiler.h"
32 #include "scripting_game.h"
33 #include "nodedef.h"
34 #include "nodemetadata.h"
35 #include "gamedef.h"
36 #ifndef SERVER
37 #include "clientmap.h"
38 #include "localplayer.h"
39 #include "mapblock_mesh.h"
40 #include "event.h"
41 #endif
42 #include "server.h"
43 #include "daynightratio.h"
44 #include "map.h"
45 #include "emerge.h"
46 #include "util/serialize.h"
47 #include "threading/mutex_auto_lock.h"
48
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50
51 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
52
53 // 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(std::map<u16, ServerActiveObject*>::iterator
1128                         i = m_active_objects.begin();
1129                         i != m_active_objects.end(); ++i)
1130         {
1131                 ServerActiveObject* obj = i->second;
1132                 u16 id = i->first;
1133                 v3f objectpos = obj->getBasePosition();
1134                 if(objectpos.getDistanceFrom(pos) > radius)
1135                         continue;
1136                 objects.push_back(id);
1137         }
1138 }
1139
1140 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1141 {
1142         infostream << "ServerEnvironment::clearObjects(): "
1143                 << "Removing all active objects" << std::endl;
1144         std::vector<u16> objects_to_remove;
1145         for (std::map<u16, ServerActiveObject*>::iterator
1146                         i = m_active_objects.begin();
1147                         i != m_active_objects.end(); ++i) {
1148                 ServerActiveObject* obj = i->second;
1149                 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1150                         continue;
1151                 u16 id = i->first;
1152                 // Delete static object if block is loaded
1153                 if (obj->m_static_exists) {
1154                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1155                         if (block) {
1156                                 block->m_static_objects.remove(id);
1157                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1158                                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1159                                 obj->m_static_exists = false;
1160                         }
1161                 }
1162                 // If known by some client, don't delete immediately
1163                 if (obj->m_known_by_count > 0) {
1164                         obj->m_pending_deactivation = true;
1165                         obj->m_removed = true;
1166                         continue;
1167                 }
1168
1169                 // Tell the object about removal
1170                 obj->removingFromEnvironment();
1171                 // Deregister in scripting api
1172                 m_script->removeObjectReference(obj);
1173
1174                 // Delete active object
1175                 if (obj->environmentDeletes())
1176                         delete obj;
1177                 // Id to be removed from m_active_objects
1178                 objects_to_remove.push_back(id);
1179         }
1180
1181         // Remove references from m_active_objects
1182         for (std::vector<u16>::iterator i = objects_to_remove.begin();
1183                         i != objects_to_remove.end(); ++i) {
1184                 m_active_objects.erase(*i);
1185         }
1186
1187         // Get list of loaded blocks
1188         std::vector<v3s16> loaded_blocks;
1189         infostream << "ServerEnvironment::clearObjects(): "
1190                 << "Listing all loaded blocks" << std::endl;
1191         m_map->listAllLoadedBlocks(loaded_blocks);
1192         infostream << "ServerEnvironment::clearObjects(): "
1193                 << "Done listing all loaded blocks: "
1194                 << loaded_blocks.size()<<std::endl;
1195
1196         // Get list of loadable blocks
1197         std::vector<v3s16> loadable_blocks;
1198         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1199                 infostream << "ServerEnvironment::clearObjects(): "
1200                         << "Listing all loadable blocks" << std::endl;
1201                 m_map->listAllLoadableBlocks(loadable_blocks);
1202                 infostream << "ServerEnvironment::clearObjects(): "
1203                         << "Done listing all loadable blocks: "
1204                         << loadable_blocks.size() << std::endl;
1205         } else {
1206                 loadable_blocks = loaded_blocks;
1207         }
1208
1209         infostream << "ServerEnvironment::clearObjects(): "
1210                 << "Now clearing objects in " << loadable_blocks.size()
1211                 << " blocks" << std::endl;
1212
1213         // Grab a reference on each loaded block to avoid unloading it
1214         for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1215                         i != loaded_blocks.end(); ++i) {
1216                 v3s16 p = *i;
1217                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1218                 assert(block != NULL);
1219                 block->refGrab();
1220         }
1221
1222         // Remove objects in all loadable blocks
1223         u32 unload_interval = U32_MAX;
1224         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1225                 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1226                 unload_interval = MYMAX(unload_interval, 1);
1227         }
1228         u32 report_interval = loadable_blocks.size() / 10;
1229         u32 num_blocks_checked = 0;
1230         u32 num_blocks_cleared = 0;
1231         u32 num_objs_cleared = 0;
1232         for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1233                         i != loadable_blocks.end(); ++i) {
1234                 v3s16 p = *i;
1235                 MapBlock *block = m_map->emergeBlock(p, false);
1236                 if (!block) {
1237                         errorstream << "ServerEnvironment::clearObjects(): "
1238                                 << "Failed to emerge block " << PP(p) << std::endl;
1239                         continue;
1240                 }
1241                 u32 num_stored = block->m_static_objects.m_stored.size();
1242                 u32 num_active = block->m_static_objects.m_active.size();
1243                 if (num_stored != 0 || num_active != 0) {
1244                         block->m_static_objects.m_stored.clear();
1245                         block->m_static_objects.m_active.clear();
1246                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1247                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1248                         num_objs_cleared += num_stored + num_active;
1249                         num_blocks_cleared++;
1250                 }
1251                 num_blocks_checked++;
1252
1253                 if (report_interval != 0 &&
1254                                 num_blocks_checked % report_interval == 0) {
1255                         float percent = 100.0 * (float)num_blocks_checked /
1256                                 loadable_blocks.size();
1257                         infostream << "ServerEnvironment::clearObjects(): "
1258                                 << "Cleared " << num_objs_cleared << " objects"
1259                                 << " in " << num_blocks_cleared << " blocks ("
1260                                 << percent << "%)" << std::endl;
1261                 }
1262                 if (num_blocks_checked % unload_interval == 0) {
1263                         m_map->unloadUnreferencedBlocks();
1264                 }
1265         }
1266         m_map->unloadUnreferencedBlocks();
1267
1268         // Drop references that were added above
1269         for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1270                         i != loaded_blocks.end(); ++i) {
1271                 v3s16 p = *i;
1272                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1273                 assert(block);
1274                 block->refDrop();
1275         }
1276
1277         m_last_clear_objects_time = m_game_time;
1278
1279         infostream << "ServerEnvironment::clearObjects(): "
1280                 << "Finished: Cleared " << num_objs_cleared << " objects"
1281                 << " in " << num_blocks_cleared << " blocks" << std::endl;
1282 }
1283
1284 void ServerEnvironment::step(float dtime)
1285 {
1286         DSTACK(FUNCTION_NAME);
1287
1288         //TimeTaker timer("ServerEnv step");
1289
1290         /* Step time of day */
1291         stepTimeOfDay(dtime);
1292
1293         // Update this one
1294         // NOTE: This is kind of funny on a singleplayer game, but doesn't
1295         // really matter that much.
1296         static const float server_step = g_settings->getFloat("dedicated_server_step");
1297         m_recommended_send_interval = server_step;
1298
1299         /*
1300                 Increment game time
1301         */
1302         {
1303                 m_game_time_fraction_counter += dtime;
1304                 u32 inc_i = (u32)m_game_time_fraction_counter;
1305                 m_game_time += inc_i;
1306                 m_game_time_fraction_counter -= (float)inc_i;
1307         }
1308
1309         /*
1310                 Handle players
1311         */
1312         {
1313                 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1314                 for(std::vector<Player*>::iterator i = m_players.begin();
1315                                 i != m_players.end(); ++i)
1316                 {
1317                         Player *player = *i;
1318
1319                         // Ignore disconnected players
1320                         if(player->peer_id == 0)
1321                                 continue;
1322
1323                         // Move
1324                         player->move(dtime, this, 100*BS);
1325                 }
1326         }
1327
1328         /*
1329                 Manage active block list
1330         */
1331         if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1332                 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1333                 /*
1334                         Get player block positions
1335                 */
1336                 std::vector<v3s16> players_blockpos;
1337                 for(std::vector<Player*>::iterator
1338                                 i = m_players.begin();
1339                                 i != m_players.end(); ++i) {
1340                         Player *player = *i;
1341                         // Ignore disconnected players
1342                         if(player->peer_id == 0)
1343                                 continue;
1344
1345                         v3s16 blockpos = getNodeBlockPos(
1346                                         floatToInt(player->getPosition(), BS));
1347                         players_blockpos.push_back(blockpos);
1348                 }
1349
1350                 /*
1351                         Update list of active blocks, collecting changes
1352                 */
1353                 static const s16 active_block_range = g_settings->getS16("active_block_range");
1354                 std::set<v3s16> blocks_removed;
1355                 std::set<v3s16> blocks_added;
1356                 m_active_blocks.update(players_blockpos, active_block_range,
1357                                 blocks_removed, blocks_added);
1358
1359                 /*
1360                         Handle removed blocks
1361                 */
1362
1363                 // Convert active objects that are no more in active blocks to static
1364                 deactivateFarObjects(false);
1365
1366                 for(std::set<v3s16>::iterator
1367                                 i = blocks_removed.begin();
1368                                 i != blocks_removed.end(); ++i) {
1369                         v3s16 p = *i;
1370
1371                         /* infostream<<"Server: Block " << PP(p)
1372                                 << " became inactive"<<std::endl; */
1373
1374                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1375                         if(block==NULL)
1376                                 continue;
1377
1378                         // Set current time as timestamp (and let it set ChangedFlag)
1379                         block->setTimestamp(m_game_time);
1380                 }
1381
1382                 /*
1383                         Handle added blocks
1384                 */
1385
1386                 for(std::set<v3s16>::iterator
1387                                 i = blocks_added.begin();
1388                                 i != blocks_added.end(); ++i)
1389                 {
1390                         v3s16 p = *i;
1391
1392                         MapBlock *block = m_map->getBlockOrEmerge(p);
1393                         if(block==NULL){
1394                                 m_active_blocks.m_list.erase(p);
1395                                 continue;
1396                         }
1397
1398                         activateBlock(block);
1399                         /* infostream<<"Server: Block " << PP(p)
1400                                 << " became active"<<std::endl; */
1401                 }
1402         }
1403
1404         /*
1405                 Mess around in active blocks
1406         */
1407         if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1408                 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1409
1410                 float dtime = m_cache_nodetimer_interval;
1411
1412                 for(std::set<v3s16>::iterator
1413                                 i = m_active_blocks.m_list.begin();
1414                                 i != m_active_blocks.m_list.end(); ++i)
1415                 {
1416                         v3s16 p = *i;
1417
1418                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1419                                         <<") being handled"<<std::endl;*/
1420
1421                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1422                         if(block==NULL)
1423                                 continue;
1424
1425                         // Reset block usage timer
1426                         block->resetUsageTimer();
1427
1428                         // Set current time as timestamp
1429                         block->setTimestampNoChangedFlag(m_game_time);
1430                         // If time has changed much from the one on disk,
1431                         // set block to be saved when it is unloaded
1432                         if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1433                                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1434                                         MOD_REASON_BLOCK_EXPIRED);
1435
1436                         // Run node timers
1437                         std::vector<NodeTimer> elapsed_timers =
1438                                 block->m_node_timers.step((float)dtime);
1439                         if (!elapsed_timers.empty()) {
1440                                 MapNode n;
1441                                 for (std::vector<NodeTimer>::iterator
1442                                                 i = elapsed_timers.begin();
1443                                                 i != elapsed_timers.end(); ++i) {
1444                                         n = block->getNodeNoEx(i->position);
1445                                         p = i->position + block->getPosRelative();
1446                                         if (m_script->node_on_timer(p, n, i->elapsed)) {
1447                                                 block->setNodeTimer(NodeTimer(
1448                                                         i->timeout, 0, i->position));
1449                                         }
1450                                 }
1451                         }
1452                 }
1453         }
1454
1455         if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1456         do{ // breakable
1457                 if(m_active_block_interval_overload_skip > 0){
1458                         ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1459                         m_active_block_interval_overload_skip--;
1460                         break;
1461                 }
1462                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1463                 TimeTaker timer("modify in active blocks per interval");
1464
1465                 // Initialize handling of ActiveBlockModifiers
1466                 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1467
1468                 for(std::set<v3s16>::iterator
1469                                 i = m_active_blocks.m_list.begin();
1470                                 i != m_active_blocks.m_list.end(); ++i)
1471                 {
1472                         v3s16 p = *i;
1473
1474                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1475                                         <<") being handled"<<std::endl;*/
1476
1477                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1478                         if(block == NULL)
1479                                 continue;
1480
1481                         // Set current time as timestamp
1482                         block->setTimestampNoChangedFlag(m_game_time);
1483
1484                         /* Handle ActiveBlockModifiers */
1485                         abmhandler.apply(block);
1486                 }
1487
1488                 u32 time_ms = timer.stop(true);
1489                 u32 max_time_ms = 200;
1490                 if(time_ms > max_time_ms){
1491                         warningstream<<"active block modifiers took "
1492                                         <<time_ms<<"ms (longer than "
1493                                         <<max_time_ms<<"ms)"<<std::endl;
1494                         m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1495                 }
1496         }while(0);
1497
1498         /*
1499                 Step script environment (run global on_step())
1500         */
1501         m_script->environment_Step(dtime);
1502
1503         /*
1504                 Step active objects
1505         */
1506         {
1507                 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1508                 //TimeTaker timer("Step active objects");
1509
1510                 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1511
1512                 // This helps the objects to send data at the same time
1513                 bool send_recommended = false;
1514                 m_send_recommended_timer += dtime;
1515                 if(m_send_recommended_timer > getSendRecommendedInterval())
1516                 {
1517                         m_send_recommended_timer -= getSendRecommendedInterval();
1518                         send_recommended = true;
1519                 }
1520
1521                 for(std::map<u16, ServerActiveObject*>::iterator
1522                                 i = m_active_objects.begin();
1523                                 i != m_active_objects.end(); ++i)
1524                 {
1525                         ServerActiveObject* obj = i->second;
1526                         // Don't step if is to be removed or stored statically
1527                         if(obj->m_removed || obj->m_pending_deactivation)
1528                                 continue;
1529                         // Step object
1530                         obj->step(dtime, send_recommended);
1531                         // Read messages from object
1532                         while(!obj->m_messages_out.empty())
1533                         {
1534                                 m_active_object_messages.push(
1535                                                 obj->m_messages_out.front());
1536                                 obj->m_messages_out.pop();
1537                         }
1538                 }
1539         }
1540
1541         /*
1542                 Manage active objects
1543         */
1544         if(m_object_management_interval.step(dtime, 0.5))
1545         {
1546                 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1547                 /*
1548                         Remove objects that satisfy (m_removed && m_known_by_count==0)
1549                 */
1550                 removeRemovedObjects();
1551         }
1552
1553         /*
1554                 Manage particle spawner expiration
1555         */
1556         if (m_particle_management_interval.step(dtime, 1.0)) {
1557                 for (std::map<u32, float>::iterator i = m_particle_spawners.begin();
1558                                 i != m_particle_spawners.end(); ) {
1559                         //non expiring spawners
1560                         if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1561                                 ++i;
1562                                 continue;
1563                         }
1564
1565                         i->second -= 1.0f;
1566                         if (i->second <= 0.f)
1567                                 m_particle_spawners.erase(i++);
1568                         else
1569                                 ++i;
1570                 }
1571         }
1572 }
1573
1574 u32 ServerEnvironment::addParticleSpawner(float exptime)
1575 {
1576         // Timers with lifetime 0 do not expire
1577         float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1578
1579         u32 id = 0;
1580         for (;;) { // look for unused particlespawner id
1581                 id++;
1582                 std::map<u32, float>::iterator f;
1583                 f = m_particle_spawners.find(id);
1584                 if (f == m_particle_spawners.end()) {
1585                         m_particle_spawners[id] = time;
1586                         break;
1587                 }
1588         }
1589         return id;
1590 }
1591
1592 void ServerEnvironment::deleteParticleSpawner(u32 id)
1593 {
1594         m_particle_spawners.erase(id);
1595 }
1596
1597 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1598 {
1599         std::map<u16, ServerActiveObject*>::iterator n;
1600         n = m_active_objects.find(id);
1601         if(n == m_active_objects.end())
1602                 return NULL;
1603         return n->second;
1604 }
1605
1606 bool isFreeServerActiveObjectId(u16 id,
1607                 std::map<u16, ServerActiveObject*> &objects)
1608 {
1609         if(id == 0)
1610                 return false;
1611
1612         return objects.find(id) == objects.end();
1613 }
1614
1615 u16 getFreeServerActiveObjectId(
1616                 std::map<u16, ServerActiveObject*> &objects)
1617 {
1618         //try to reuse id's as late as possible
1619         static u16 last_used_id = 0;
1620         u16 startid = last_used_id;
1621         for(;;)
1622         {
1623                 last_used_id ++;
1624                 if(isFreeServerActiveObjectId(last_used_id, objects))
1625                         return last_used_id;
1626
1627                 if(last_used_id == startid)
1628                         return 0;
1629         }
1630 }
1631
1632 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1633 {
1634         assert(object); // Pre-condition
1635         m_added_objects++;
1636         u16 id = addActiveObjectRaw(object, true, 0);
1637         return id;
1638 }
1639
1640 /*
1641         Finds out what new objects have been added to
1642         inside a radius around a position
1643 */
1644 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1645                 s16 player_radius,
1646                 std::set<u16> &current_objects,
1647                 std::queue<u16> &added_objects)
1648 {
1649         f32 radius_f = radius * BS;
1650         f32 player_radius_f = player_radius * BS;
1651
1652         if (player_radius_f < 0)
1653                 player_radius_f = 0;
1654
1655         /*
1656                 Go through the object list,
1657                 - discard m_removed objects,
1658                 - discard objects that are too far away,
1659                 - discard objects that are found in current_objects.
1660                 - add remaining objects to added_objects
1661         */
1662         for(std::map<u16, ServerActiveObject*>::iterator
1663                         i = m_active_objects.begin();
1664                         i != m_active_objects.end(); ++i) {
1665                 u16 id = i->first;
1666
1667                 // Get object
1668                 ServerActiveObject *object = i->second;
1669                 if(object == NULL)
1670                         continue;
1671
1672                 // Discard if removed or deactivating
1673                 if(object->m_removed || object->m_pending_deactivation)
1674                         continue;
1675
1676                 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1677                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1678                         // Discard if too far
1679                         if (distance_f > player_radius_f && player_radius_f != 0)
1680                                 continue;
1681                 } else if (distance_f > radius_f)
1682                         continue;
1683
1684                 // Discard if already on current_objects
1685                 std::set<u16>::iterator n;
1686                 n = current_objects.find(id);
1687                 if(n != current_objects.end())
1688                         continue;
1689                 // Add to added_objects
1690                 added_objects.push(id);
1691         }
1692 }
1693
1694 /*
1695         Finds out what objects have been removed from
1696         inside a radius around a position
1697 */
1698 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1699                 s16 player_radius,
1700                 std::set<u16> &current_objects,
1701                 std::queue<u16> &removed_objects)
1702 {
1703         f32 radius_f = radius * BS;
1704         f32 player_radius_f = player_radius * BS;
1705
1706         if (player_radius_f < 0)
1707                 player_radius_f = 0;
1708
1709         /*
1710                 Go through current_objects; object is removed if:
1711                 - object is not found in m_active_objects (this is actually an
1712                   error condition; objects should be set m_removed=true and removed
1713                   only after all clients have been informed about removal), or
1714                 - object has m_removed=true, or
1715                 - object is too far away
1716         */
1717         for(std::set<u16>::iterator
1718                         i = current_objects.begin();
1719                         i != current_objects.end(); ++i)
1720         {
1721                 u16 id = *i;
1722                 ServerActiveObject *object = getActiveObject(id);
1723
1724                 if (object == NULL) {
1725                         infostream << "ServerEnvironment::getRemovedActiveObjects():"
1726                                 << " object in current_objects is NULL" << std::endl;
1727                         removed_objects.push(id);
1728                         continue;
1729                 }
1730
1731                 if (object->m_removed || object->m_pending_deactivation) {
1732                         removed_objects.push(id);
1733                         continue;
1734                 }
1735
1736                 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1737                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1738                         if (distance_f <= player_radius_f || player_radius_f == 0)
1739                                 continue;
1740                 } else if (distance_f <= radius_f)
1741                         continue;
1742
1743                 // Object is no longer visible
1744                 removed_objects.push(id);
1745         }
1746 }
1747
1748 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1749         v3s16 blockpos, bool static_exists, v3s16 static_block)
1750 {
1751         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1752         if (!block)
1753                 return;
1754
1755         for (std::map<u16, StaticObject>::iterator
1756                         so_it = block->m_static_objects.m_active.begin();
1757                         so_it != block->m_static_objects.m_active.end(); ++so_it) {
1758                 // Get the ServerActiveObject counterpart to this StaticObject
1759                 std::map<u16, ServerActiveObject *>::iterator ao_it;
1760                 ao_it = m_active_objects.find(so_it->first);
1761                 if (ao_it == m_active_objects.end()) {
1762                         // If this ever happens, there must be some kind of nasty bug.
1763                         errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1764                                 "Object from MapBlock::m_static_objects::m_active not found "
1765                                 "in m_active_objects";
1766                         continue;
1767                 }
1768
1769                 ServerActiveObject *sao = ao_it->second;
1770                 sao->m_static_exists = static_exists;
1771                 sao->m_static_block  = static_block;
1772         }
1773 }
1774
1775 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1776 {
1777         if(m_active_object_messages.empty())
1778                 return ActiveObjectMessage(0);
1779
1780         ActiveObjectMessage message = m_active_object_messages.front();
1781         m_active_object_messages.pop();
1782         return message;
1783 }
1784
1785 /*
1786         ************ Private methods *************
1787 */
1788
1789 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1790                 bool set_changed, u32 dtime_s)
1791 {
1792         assert(object); // Pre-condition
1793         if(object->getId() == 0){
1794                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1795                 if(new_id == 0)
1796                 {
1797                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1798                                         <<"no free ids available"<<std::endl;
1799                         if(object->environmentDeletes())
1800                                 delete object;
1801                         return 0;
1802                 }
1803                 object->setId(new_id);
1804         }
1805         else{
1806                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1807                                 <<"supplied with id "<<object->getId()<<std::endl;
1808         }
1809         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1810         {
1811                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1812                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1813                 if(object->environmentDeletes())
1814                         delete object;
1815                 return 0;
1816         }
1817
1818         if (objectpos_over_limit(object->getBasePosition())) {
1819                 v3f p = object->getBasePosition();
1820                 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1821                         << "object position (" << p.X << "," << p.Y << "," << p.Z
1822                         << ") outside maximum range" << std::endl;
1823                 if (object->environmentDeletes())
1824                         delete object;
1825                 return 0;
1826         }
1827
1828         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1829                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1830
1831         m_active_objects[object->getId()] = object;
1832
1833         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1834                         <<"Added id="<<object->getId()<<"; there are now "
1835                         <<m_active_objects.size()<<" active objects."
1836                         <<std::endl;
1837
1838         // Register reference in scripting api (must be done before post-init)
1839         m_script->addObjectReference(object);
1840         // Post-initialize object
1841         object->addedToEnvironment(dtime_s);
1842
1843         // Add static data to block
1844         if(object->isStaticAllowed())
1845         {
1846                 // Add static object to active static list of the block
1847                 v3f objectpos = object->getBasePosition();
1848                 std::string staticdata = object->getStaticData();
1849                 StaticObject s_obj(object->getType(), objectpos, staticdata);
1850                 // Add to the block where the object is located in
1851                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1852                 MapBlock *block = m_map->emergeBlock(blockpos);
1853                 if(block){
1854                         block->m_static_objects.m_active[object->getId()] = s_obj;
1855                         object->m_static_exists = true;
1856                         object->m_static_block = blockpos;
1857
1858                         if(set_changed)
1859                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1860                                         MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1861                 } else {
1862                         v3s16 p = floatToInt(objectpos, BS);
1863                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1864                                         <<"could not emerge block for storing id="<<object->getId()
1865                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
1866                 }
1867         }
1868
1869         return object->getId();
1870 }
1871
1872 /*
1873         Remove objects that satisfy (m_removed && m_known_by_count==0)
1874 */
1875 void ServerEnvironment::removeRemovedObjects()
1876 {
1877         std::vector<u16> objects_to_remove;
1878         for(std::map<u16, ServerActiveObject*>::iterator
1879                         i = m_active_objects.begin();
1880                         i != m_active_objects.end(); ++i) {
1881                 u16 id = i->first;
1882                 ServerActiveObject* obj = i->second;
1883                 // This shouldn't happen but check it
1884                 if(obj == NULL)
1885                 {
1886                         infostream<<"NULL object found in ServerEnvironment"
1887                                         <<" while finding removed objects. id="<<id<<std::endl;
1888                         // Id to be removed from m_active_objects
1889                         objects_to_remove.push_back(id);
1890                         continue;
1891                 }
1892
1893                 /*
1894                         We will delete objects that are marked as removed or thatare
1895                         waiting for deletion after deactivation
1896                 */
1897                 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1898                         continue;
1899
1900                 /*
1901                         Delete static data from block if is marked as removed
1902                 */
1903                 if(obj->m_static_exists && obj->m_removed)
1904                 {
1905                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1906                         if (block) {
1907                                 block->m_static_objects.remove(id);
1908                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1909                                         MOD_REASON_REMOVE_OBJECTS_REMOVE);
1910                                 obj->m_static_exists = false;
1911                         } else {
1912                                 infostream<<"Failed to emerge block from which an object to "
1913                                                 <<"be removed was loaded from. id="<<id<<std::endl;
1914                         }
1915                 }
1916
1917                 // If m_known_by_count > 0, don't actually remove. On some future
1918                 // invocation this will be 0, which is when removal will continue.
1919                 if(obj->m_known_by_count > 0)
1920                         continue;
1921
1922                 /*
1923                         Move static data from active to stored if not marked as removed
1924                 */
1925                 if(obj->m_static_exists && !obj->m_removed){
1926                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1927                         if (block) {
1928                                 std::map<u16, StaticObject>::iterator i =
1929                                                 block->m_static_objects.m_active.find(id);
1930                                 if(i != block->m_static_objects.m_active.end()){
1931                                         block->m_static_objects.m_stored.push_back(i->second);
1932                                         block->m_static_objects.m_active.erase(id);
1933                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1934                                                 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1935                                 }
1936                         } else {
1937                                 infostream<<"Failed to emerge block from which an object to "
1938                                                 <<"be deactivated was loaded from. id="<<id<<std::endl;
1939                         }
1940                 }
1941
1942                 // Tell the object about removal
1943                 obj->removingFromEnvironment();
1944                 // Deregister in scripting api
1945                 m_script->removeObjectReference(obj);
1946
1947                 // Delete
1948                 if(obj->environmentDeletes())
1949                         delete obj;
1950
1951                 // Id to be removed from m_active_objects
1952                 objects_to_remove.push_back(id);
1953         }
1954         // Remove references from m_active_objects
1955         for(std::vector<u16>::iterator i = objects_to_remove.begin();
1956                         i != objects_to_remove.end(); ++i) {
1957                 m_active_objects.erase(*i);
1958         }
1959 }
1960
1961 static void print_hexdump(std::ostream &o, const std::string &data)
1962 {
1963         const int linelength = 16;
1964         for(int l=0; ; l++){
1965                 int i0 = linelength * l;
1966                 bool at_end = false;
1967                 int thislinelength = linelength;
1968                 if(i0 + thislinelength > (int)data.size()){
1969                         thislinelength = data.size() - i0;
1970                         at_end = true;
1971                 }
1972                 for(int di=0; di<linelength; di++){
1973                         int i = i0 + di;
1974                         char buf[4];
1975                         if(di<thislinelength)
1976                                 snprintf(buf, 4, "%.2x ", data[i]);
1977                         else
1978                                 snprintf(buf, 4, "   ");
1979                         o<<buf;
1980                 }
1981                 o<<" ";
1982                 for(int di=0; di<thislinelength; di++){
1983                         int i = i0 + di;
1984                         if(data[i] >= 32)
1985                                 o<<data[i];
1986                         else
1987                                 o<<".";
1988                 }
1989                 o<<std::endl;
1990                 if(at_end)
1991                         break;
1992         }
1993 }
1994
1995 /*
1996         Convert stored objects from blocks near the players to active.
1997 */
1998 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1999 {
2000         if(block == NULL)
2001                 return;
2002
2003         // Ignore if no stored objects (to not set changed flag)
2004         if(block->m_static_objects.m_stored.empty())
2005                 return;
2006
2007         verbosestream<<"ServerEnvironment::activateObjects(): "
2008                         <<"activating objects of block "<<PP(block->getPos())
2009                         <<" ("<<block->m_static_objects.m_stored.size()
2010                         <<" objects)"<<std::endl;
2011         bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
2012         if (large_amount) {
2013                 errorstream<<"suspiciously large amount of objects detected: "
2014                                 <<block->m_static_objects.m_stored.size()<<" in "
2015                                 <<PP(block->getPos())
2016                                 <<"; removing all of them."<<std::endl;
2017                 // Clear stored list
2018                 block->m_static_objects.m_stored.clear();
2019                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2020                         MOD_REASON_TOO_MANY_OBJECTS);
2021                 return;
2022         }
2023
2024         // Activate stored objects
2025         std::vector<StaticObject> new_stored;
2026         for (std::vector<StaticObject>::iterator
2027                         i = block->m_static_objects.m_stored.begin();
2028                         i != block->m_static_objects.m_stored.end(); ++i) {
2029                 StaticObject &s_obj = *i;
2030
2031                 // Create an active object from the data
2032                 ServerActiveObject *obj = ServerActiveObject::create
2033                                 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
2034                 // If couldn't create object, store static data back.
2035                 if(obj == NULL) {
2036                         errorstream<<"ServerEnvironment::activateObjects(): "
2037                                         <<"failed to create active object from static object "
2038                                         <<"in block "<<PP(s_obj.pos/BS)
2039                                         <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
2040                         print_hexdump(verbosestream, s_obj.data);
2041
2042                         new_stored.push_back(s_obj);
2043                         continue;
2044                 }
2045                 verbosestream<<"ServerEnvironment::activateObjects(): "
2046                                 <<"activated static object pos="<<PP(s_obj.pos/BS)
2047                                 <<" type="<<(int)s_obj.type<<std::endl;
2048                 // This will also add the object to the active static list
2049                 addActiveObjectRaw(obj, false, dtime_s);
2050         }
2051         // Clear stored list
2052         block->m_static_objects.m_stored.clear();
2053         // Add leftover failed stuff to stored list
2054         for(std::vector<StaticObject>::iterator
2055                         i = new_stored.begin();
2056                         i != new_stored.end(); ++i) {
2057                 StaticObject &s_obj = *i;
2058                 block->m_static_objects.m_stored.push_back(s_obj);
2059         }
2060
2061         // Turn the active counterparts of activated objects not pending for
2062         // deactivation
2063         for(std::map<u16, StaticObject>::iterator
2064                         i = block->m_static_objects.m_active.begin();
2065                         i != block->m_static_objects.m_active.end(); ++i)
2066         {
2067                 u16 id = i->first;
2068                 ServerActiveObject *object = getActiveObject(id);
2069                 assert(object);
2070                 object->m_pending_deactivation = false;
2071         }
2072
2073         /*
2074                 Note: Block hasn't really been modified here.
2075                 The objects have just been activated and moved from the stored
2076                 static list to the active static list.
2077                 As such, the block is essentially the same.
2078                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2079                 Otherwise there would be a huge amount of unnecessary I/O.
2080         */
2081 }
2082
2083 /*
2084         Convert objects that are not standing inside active blocks to static.
2085
2086         If m_known_by_count != 0, active object is not deleted, but static
2087         data is still updated.
2088
2089         If force_delete is set, active object is deleted nevertheless. It
2090         shall only be set so in the destructor of the environment.
2091
2092         If block wasn't generated (not in memory or on disk),
2093 */
2094 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2095 {
2096         std::vector<u16> objects_to_remove;
2097         for(std::map<u16, ServerActiveObject*>::iterator
2098                         i = m_active_objects.begin();
2099                         i != m_active_objects.end(); ++i) {
2100                 ServerActiveObject* obj = i->second;
2101                 assert(obj);
2102
2103                 // Do not deactivate if static data creation not allowed
2104                 if(!force_delete && !obj->isStaticAllowed())
2105                         continue;
2106
2107                 // If pending deactivation, let removeRemovedObjects() do it
2108                 if(!force_delete && obj->m_pending_deactivation)
2109                         continue;
2110
2111                 u16 id = i->first;
2112                 v3f objectpos = obj->getBasePosition();
2113
2114                 // The block in which the object resides in
2115                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2116
2117                 // If object's static data is stored in a deactivated block and object
2118                 // is actually located in an active block, re-save to the block in
2119                 // which the object is actually located in.
2120                 if(!force_delete &&
2121                                 obj->m_static_exists &&
2122                                 !m_active_blocks.contains(obj->m_static_block) &&
2123                                  m_active_blocks.contains(blockpos_o))
2124                 {
2125                         v3s16 old_static_block = obj->m_static_block;
2126
2127                         // Save to block where object is located
2128                         MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2129                         if(!block){
2130                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2131                                                 <<"Could not save object id="<<id
2132                                                 <<" to it's current block "<<PP(blockpos_o)
2133                                                 <<std::endl;
2134                                 continue;
2135                         }
2136                         std::string staticdata_new = obj->getStaticData();
2137                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2138                         block->m_static_objects.insert(id, s_obj);
2139                         obj->m_static_block = blockpos_o;
2140                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2141                                 MOD_REASON_STATIC_DATA_ADDED);
2142
2143                         // Delete from block where object was located
2144                         block = m_map->emergeBlock(old_static_block, false);
2145                         if(!block){
2146                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2147                                                 <<"Could not delete object id="<<id
2148                                                 <<" from it's previous block "<<PP(old_static_block)
2149                                                 <<std::endl;
2150                                 continue;
2151                         }
2152                         block->m_static_objects.remove(id);
2153                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2154                                 MOD_REASON_STATIC_DATA_REMOVED);
2155                         continue;
2156                 }
2157
2158                 // If block is active, don't remove
2159                 if(!force_delete && m_active_blocks.contains(blockpos_o))
2160                         continue;
2161
2162                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2163                                 <<"deactivating object id="<<id<<" on inactive block "
2164                                 <<PP(blockpos_o)<<std::endl;
2165
2166                 // If known by some client, don't immediately delete.
2167                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2168
2169                 /*
2170                         Update the static data
2171                 */
2172
2173                 if(obj->isStaticAllowed())
2174                 {
2175                         // Create new static object
2176                         std::string staticdata_new = obj->getStaticData();
2177                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2178
2179                         bool stays_in_same_block = false;
2180                         bool data_changed = true;
2181
2182                         if (obj->m_static_exists) {
2183                                 if (obj->m_static_block == blockpos_o)
2184                                         stays_in_same_block = true;
2185
2186                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2187
2188                                 if (block) {
2189                                         std::map<u16, StaticObject>::iterator n =
2190                                                 block->m_static_objects.m_active.find(id);
2191                                         if (n != block->m_static_objects.m_active.end()) {
2192                                                 StaticObject static_old = n->second;
2193
2194                                                 float save_movem = obj->getMinimumSavedMovement();
2195
2196                                                 if (static_old.data == staticdata_new &&
2197                                                                 (static_old.pos - objectpos).getLength() < save_movem)
2198                                                         data_changed = false;
2199                                         } else {
2200                                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2201                                                         <<"id="<<id<<" m_static_exists=true but "
2202                                                         <<"static data doesn't actually exist in "
2203                                                         <<PP(obj->m_static_block)<<std::endl;
2204                                         }
2205                                 }
2206                         }
2207
2208                         bool shall_be_written = (!stays_in_same_block || data_changed);
2209
2210                         // Delete old static object
2211                         if(obj->m_static_exists)
2212                         {
2213                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2214                                 if(block)
2215                                 {
2216                                         block->m_static_objects.remove(id);
2217                                         obj->m_static_exists = false;
2218                                         // Only mark block as modified if data changed considerably
2219                                         if(shall_be_written)
2220                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2221                                                         MOD_REASON_STATIC_DATA_CHANGED);
2222                                 }
2223                         }
2224
2225                         // Add to the block where the object is located in
2226                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2227                         // Get or generate the block
2228                         MapBlock *block = NULL;
2229                         try{
2230                                 block = m_map->emergeBlock(blockpos);
2231                         } catch(InvalidPositionException &e){
2232                                 // Handled via NULL pointer
2233                                 // NOTE: emergeBlock's failure is usually determined by it
2234                                 //       actually returning NULL
2235                         }
2236
2237                         if(block)
2238                         {
2239                                 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2240                                         errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2241                                                         <<" statically but block "<<PP(blockpos)
2242                                                         <<" already contains "
2243                                                         <<block->m_static_objects.m_stored.size()
2244                                                         <<" objects."
2245                                                         <<" Forcing delete."<<std::endl;
2246                                         force_delete = true;
2247                                 } else {
2248                                         // If static counterpart already exists in target block,
2249                                         // remove it first.
2250                                         // This shouldn't happen because the object is removed from
2251                                         // the previous block before this according to
2252                                         // obj->m_static_block, but happens rarely for some unknown
2253                                         // reason. Unsuccessful attempts have been made to find
2254                                         // said reason.
2255                                         if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2256                                                 warningstream<<"ServerEnv: Performing hack #83274"
2257                                                                 <<std::endl;
2258                                                 block->m_static_objects.remove(id);
2259                                         }
2260                                         // Store static data
2261                                         u16 store_id = pending_delete ? id : 0;
2262                                         block->m_static_objects.insert(store_id, s_obj);
2263
2264                                         // Only mark block as modified if data changed considerably
2265                                         if(shall_be_written)
2266                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2267                                                         MOD_REASON_STATIC_DATA_CHANGED);
2268
2269                                         obj->m_static_exists = true;
2270                                         obj->m_static_block = block->getPos();
2271                                 }
2272                         }
2273                         else{
2274                                 if(!force_delete){
2275                                         v3s16 p = floatToInt(objectpos, BS);
2276                                         errorstream<<"ServerEnv: Could not find or generate "
2277                                                         <<"a block for storing id="<<obj->getId()
2278                                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
2279                                         continue;
2280                                 }
2281                         }
2282                 }
2283
2284                 /*
2285                         If known by some client, set pending deactivation.
2286                         Otherwise delete it immediately.
2287                 */
2288
2289                 if(pending_delete && !force_delete)
2290                 {
2291                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2292                                         <<"object id="<<id<<" is known by clients"
2293                                         <<"; not deleting yet"<<std::endl;
2294
2295                         obj->m_pending_deactivation = true;
2296                         continue;
2297                 }
2298
2299                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2300                                 <<"object id="<<id<<" is not known by clients"
2301                                 <<"; deleting"<<std::endl;
2302
2303                 // Tell the object about removal
2304                 obj->removingFromEnvironment();
2305                 // Deregister in scripting api
2306                 m_script->removeObjectReference(obj);
2307
2308                 // Delete active object
2309                 if(obj->environmentDeletes())
2310                         delete obj;
2311                 // Id to be removed from m_active_objects
2312                 objects_to_remove.push_back(id);
2313         }
2314
2315         // Remove references from m_active_objects
2316         for(std::vector<u16>::iterator i = objects_to_remove.begin();
2317                         i != objects_to_remove.end(); ++i) {
2318                 m_active_objects.erase(*i);
2319         }
2320 }
2321
2322 #ifndef SERVER
2323
2324 #include "clientsimpleobject.h"
2325
2326 /*
2327         ClientEnvironment
2328 */
2329
2330 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2331                 ITextureSource *texturesource, IGameDef *gamedef,
2332                 IrrlichtDevice *irr):
2333         m_map(map),
2334         m_smgr(smgr),
2335         m_texturesource(texturesource),
2336         m_gamedef(gamedef),
2337         m_irr(irr)
2338 {
2339         char zero = 0;
2340         memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2341 }
2342
2343 ClientEnvironment::~ClientEnvironment()
2344 {
2345         // delete active objects
2346         for(std::map<u16, ClientActiveObject*>::iterator
2347                         i = m_active_objects.begin();
2348                         i != m_active_objects.end(); ++i)
2349         {
2350                 delete i->second;
2351         }
2352
2353         for(std::vector<ClientSimpleObject*>::iterator
2354                         i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2355                 delete *i;
2356         }
2357
2358         // Drop/delete map
2359         m_map->drop();
2360 }
2361
2362 Map & ClientEnvironment::getMap()
2363 {
2364         return *m_map;
2365 }
2366
2367 ClientMap & ClientEnvironment::getClientMap()
2368 {
2369         return *m_map;
2370 }
2371
2372 void ClientEnvironment::addPlayer(Player *player)
2373 {
2374         DSTACK(FUNCTION_NAME);
2375         /*
2376                 It is a failure if player is local and there already is a local
2377                 player
2378         */
2379         FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2380                 "Player is local but there is already a local player");
2381
2382         Environment::addPlayer(player);
2383 }
2384
2385 LocalPlayer * ClientEnvironment::getLocalPlayer()
2386 {
2387         for(std::vector<Player*>::iterator i = m_players.begin();
2388                         i != m_players.end(); ++i) {
2389                 Player *player = *i;
2390                 if(player->isLocal())
2391                         return (LocalPlayer*)player;
2392         }
2393         return NULL;
2394 }
2395
2396 void ClientEnvironment::step(float dtime)
2397 {
2398         DSTACK(FUNCTION_NAME);
2399
2400         /* Step time of day */
2401         stepTimeOfDay(dtime);
2402
2403         // Get some settings
2404         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2405         bool free_move = fly_allowed && g_settings->getBool("free_move");
2406
2407         // Get local player
2408         LocalPlayer *lplayer = getLocalPlayer();
2409         assert(lplayer);
2410         // collision info queue
2411         std::vector<CollisionInfo> player_collisions;
2412
2413         /*
2414                 Get the speed the player is going
2415         */
2416         bool is_climbing = lplayer->is_climbing;
2417
2418         f32 player_speed = lplayer->getSpeed().getLength();
2419
2420         /*
2421                 Maximum position increment
2422         */
2423         //f32 position_max_increment = 0.05*BS;
2424         f32 position_max_increment = 0.1*BS;
2425
2426         // Maximum time increment (for collision detection etc)
2427         // time = distance / speed
2428         f32 dtime_max_increment = 1;
2429         if(player_speed > 0.001)
2430                 dtime_max_increment = position_max_increment / player_speed;
2431
2432         // Maximum time increment is 10ms or lower
2433         if(dtime_max_increment > 0.01)
2434                 dtime_max_increment = 0.01;
2435
2436         // Don't allow overly huge dtime
2437         if(dtime > 0.5)
2438                 dtime = 0.5;
2439
2440         f32 dtime_downcount = dtime;
2441
2442         /*
2443                 Stuff that has a maximum time increment
2444         */
2445
2446         u32 loopcount = 0;
2447         do
2448         {
2449                 loopcount++;
2450
2451                 f32 dtime_part;
2452                 if(dtime_downcount > dtime_max_increment)
2453                 {
2454                         dtime_part = dtime_max_increment;
2455                         dtime_downcount -= dtime_part;
2456                 }
2457                 else
2458                 {
2459                         dtime_part = dtime_downcount;
2460                         /*
2461                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
2462                                 when dtime_part is so small that dtime_downcount -= dtime_part
2463                                 does nothing
2464                         */
2465                         dtime_downcount = 0;
2466                 }
2467
2468                 /*
2469                         Handle local player
2470                 */
2471
2472                 {
2473                         // Apply physics
2474                         if(free_move == false && is_climbing == false)
2475                         {
2476                                 // Gravity
2477                                 v3f speed = lplayer->getSpeed();
2478                                 if(lplayer->in_liquid == false)
2479                                         speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2480
2481                                 // Liquid floating / sinking
2482                                 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2483                                         speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2484
2485                                 // Liquid resistance
2486                                 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2487                                 {
2488                                         // How much the node's viscosity blocks movement, ranges between 0 and 1
2489                                         // Should match the scale at which viscosity increase affects other liquid attributes
2490                                         const f32 viscosity_factor = 0.3;
2491
2492                                         v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2493                                         f32 dl = d_wanted.getLength();
2494                                         if(dl > lplayer->movement_liquid_fluidity_smooth)
2495                                                 dl = lplayer->movement_liquid_fluidity_smooth;
2496                                         dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2497
2498                                         v3f d = d_wanted.normalize() * dl;
2499                                         speed += d;
2500                                 }
2501
2502                                 lplayer->setSpeed(speed);
2503                         }
2504
2505                         /*
2506                                 Move the lplayer.
2507                                 This also does collision detection.
2508                         */
2509                         lplayer->move(dtime_part, this, position_max_increment,
2510                                         &player_collisions);
2511                 }
2512         }
2513         while(dtime_downcount > 0.001);
2514
2515         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2516
2517         for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2518                         i != player_collisions.end(); ++i) {
2519                 CollisionInfo &info = *i;
2520                 v3f speed_diff = info.new_speed - info.old_speed;;
2521                 // Handle only fall damage
2522                 // (because otherwise walking against something in fast_move kills you)
2523                 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2524                         continue;
2525                 // Get rid of other components
2526                 speed_diff.X = 0;
2527                 speed_diff.Z = 0;
2528                 f32 pre_factor = 1; // 1 hp per node/s
2529                 f32 tolerance = BS*14; // 5 without damage
2530                 f32 post_factor = 1; // 1 hp per node/s
2531                 if(info.type == COLLISION_NODE)
2532                 {
2533                         const ContentFeatures &f = m_gamedef->ndef()->
2534                                         get(m_map->getNodeNoEx(info.node_p));
2535                         // Determine fall damage multiplier
2536                         int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2537                         pre_factor = 1.0 + (float)addp/100.0;
2538                 }
2539                 float speed = pre_factor * speed_diff.getLength();
2540                 if(speed > tolerance)
2541                 {
2542                         f32 damage_f = (speed - tolerance)/BS * post_factor;
2543                         u16 damage = (u16)(damage_f+0.5);
2544                         if(damage != 0){
2545                                 damageLocalPlayer(damage, true);
2546                                 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2547                                 m_gamedef->event()->put(e);
2548                         }
2549                 }
2550         }
2551
2552         /*
2553                 A quick draft of lava damage
2554         */
2555         if(m_lava_hurt_interval.step(dtime, 1.0))
2556         {
2557                 v3f pf = lplayer->getPosition();
2558
2559                 // Feet, middle and head
2560                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2561                 MapNode n1 = m_map->getNodeNoEx(p1);
2562                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2563                 MapNode n2 = m_map->getNodeNoEx(p2);
2564                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2565                 MapNode n3 = m_map->getNodeNoEx(p3);
2566
2567                 u32 damage_per_second = 0;
2568                 damage_per_second = MYMAX(damage_per_second,
2569                                 m_gamedef->ndef()->get(n1).damage_per_second);
2570                 damage_per_second = MYMAX(damage_per_second,
2571                                 m_gamedef->ndef()->get(n2).damage_per_second);
2572                 damage_per_second = MYMAX(damage_per_second,
2573                                 m_gamedef->ndef()->get(n3).damage_per_second);
2574
2575                 if(damage_per_second != 0)
2576                 {
2577                         damageLocalPlayer(damage_per_second, true);
2578                 }
2579         }
2580
2581         /*
2582                 Drowning
2583         */
2584         if(m_drowning_interval.step(dtime, 2.0))
2585         {
2586                 v3f pf = lplayer->getPosition();
2587
2588                 // head
2589                 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2590                 MapNode n = m_map->getNodeNoEx(p);
2591                 ContentFeatures c = m_gamedef->ndef()->get(n);
2592                 u8 drowning_damage = c.drowning;
2593                 if(drowning_damage > 0 && lplayer->hp > 0){
2594                         u16 breath = lplayer->getBreath();
2595                         if(breath > 10){
2596                                 breath = 11;
2597                         }
2598                         if(breath > 0){
2599                                 breath -= 1;
2600                         }
2601                         lplayer->setBreath(breath);
2602                         updateLocalPlayerBreath(breath);
2603                 }
2604
2605                 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2606                         damageLocalPlayer(drowning_damage, true);
2607                 }
2608         }
2609         if(m_breathing_interval.step(dtime, 0.5))
2610         {
2611                 v3f pf = lplayer->getPosition();
2612
2613                 // head
2614                 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2615                 MapNode n = m_map->getNodeNoEx(p);
2616                 ContentFeatures c = m_gamedef->ndef()->get(n);
2617                 if (!lplayer->hp){
2618                         lplayer->setBreath(11);
2619                 }
2620                 else if(c.drowning == 0){
2621                         u16 breath = lplayer->getBreath();
2622                         if(breath <= 10){
2623                                 breath += 1;
2624                                 lplayer->setBreath(breath);
2625                                 updateLocalPlayerBreath(breath);
2626                         }
2627                 }
2628         }
2629
2630         /*
2631                 Stuff that can be done in an arbitarily large dtime
2632         */
2633         for(std::vector<Player*>::iterator i = m_players.begin();
2634                         i != m_players.end(); ++i) {
2635                 Player *player = *i;
2636
2637                 /*
2638                         Handle non-local players
2639                 */
2640                 if(player->isLocal() == false) {
2641                         // Move
2642                         player->move(dtime, this, 100*BS);
2643
2644                 }
2645         }
2646
2647         // Update lighting on local player (used for wield item)
2648         u32 day_night_ratio = getDayNightRatio();
2649         {
2650                 // Get node at head
2651
2652                 // On InvalidPositionException, use this as default
2653                 // (day: LIGHT_SUN, night: 0)
2654                 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2655
2656                 v3s16 p = lplayer->getLightPosition();
2657                 node_at_lplayer = m_map->getNodeNoEx(p);
2658
2659                 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2660                 u8 day = light & 0xff;
2661                 u8 night = (light >> 8) & 0xff;
2662                 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2663         }
2664
2665         /*
2666                 Step active objects and update lighting of them
2667         */
2668
2669         g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2670         bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2671         for(std::map<u16, ClientActiveObject*>::iterator
2672                         i = m_active_objects.begin();
2673                         i != m_active_objects.end(); ++i)
2674         {
2675                 ClientActiveObject* obj = i->second;
2676                 // Step object
2677                 obj->step(dtime, this);
2678
2679                 if(update_lighting)
2680                 {
2681                         // Update lighting
2682                         u8 light = 0;
2683                         bool pos_ok;
2684
2685                         // Get node at head
2686                         v3s16 p = obj->getLightPosition();
2687                         MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2688                         if (pos_ok)
2689                                 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2690                         else
2691                                 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2692
2693                         obj->updateLight(light);
2694                 }
2695         }
2696
2697         /*
2698                 Step and handle simple objects
2699         */
2700         g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2701         for(std::vector<ClientSimpleObject*>::iterator
2702                         i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2703                 std::vector<ClientSimpleObject*>::iterator cur = i;
2704                 ClientSimpleObject *simple = *cur;
2705
2706                 simple->step(dtime);
2707                 if(simple->m_to_be_removed) {
2708                         delete simple;
2709                         i = m_simple_objects.erase(cur);
2710                 }
2711                 else {
2712                         ++i;
2713                 }
2714         }
2715 }
2716
2717 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2718 {
2719         m_simple_objects.push_back(simple);
2720 }
2721
2722 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2723 {
2724         ClientActiveObject *obj = getActiveObject(id);
2725         if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2726                 return (GenericCAO*) obj;
2727         else
2728                 return NULL;
2729 }
2730
2731 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2732 {
2733         std::map<u16, ClientActiveObject*>::iterator n;
2734         n = m_active_objects.find(id);
2735         if(n == m_active_objects.end())
2736                 return NULL;
2737         return n->second;
2738 }
2739
2740 bool isFreeClientActiveObjectId(u16 id,
2741                 std::map<u16, ClientActiveObject*> &objects)
2742 {
2743         if(id == 0)
2744                 return false;
2745
2746         return objects.find(id) == objects.end();
2747 }
2748
2749 u16 getFreeClientActiveObjectId(
2750                 std::map<u16, ClientActiveObject*> &objects)
2751 {
2752         //try to reuse id's as late as possible
2753         static u16 last_used_id = 0;
2754         u16 startid = last_used_id;
2755         for(;;)
2756         {
2757                 last_used_id ++;
2758                 if(isFreeClientActiveObjectId(last_used_id, objects))
2759                         return last_used_id;
2760
2761                 if(last_used_id == startid)
2762                         return 0;
2763         }
2764 }
2765
2766 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2767 {
2768         assert(object); // Pre-condition
2769         if(object->getId() == 0)
2770         {
2771                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2772                 if(new_id == 0)
2773                 {
2774                         infostream<<"ClientEnvironment::addActiveObject(): "
2775                                         <<"no free ids available"<<std::endl;
2776                         delete object;
2777                         return 0;
2778                 }
2779                 object->setId(new_id);
2780         }
2781         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2782         {
2783                 infostream<<"ClientEnvironment::addActiveObject(): "
2784                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2785                 delete object;
2786                 return 0;
2787         }
2788         infostream<<"ClientEnvironment::addActiveObject(): "
2789                         <<"added (id="<<object->getId()<<")"<<std::endl;
2790         m_active_objects[object->getId()] = object;
2791         object->addToScene(m_smgr, m_texturesource, m_irr);
2792         { // Update lighting immediately
2793                 u8 light = 0;
2794                 bool pos_ok;
2795
2796                 // Get node at head
2797                 v3s16 p = object->getLightPosition();
2798                 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2799                 if (pos_ok)
2800                         light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2801                 else
2802                         light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2803
2804                 object->updateLight(light);
2805         }
2806         return object->getId();
2807 }
2808
2809 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2810                 const std::string &init_data)
2811 {
2812         ClientActiveObject* obj =
2813                         ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2814         if(obj == NULL)
2815         {
2816                 infostream<<"ClientEnvironment::addActiveObject(): "
2817                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2818                                 <<std::endl;
2819                 return;
2820         }
2821
2822         obj->setId(id);
2823
2824         try
2825         {
2826                 obj->initialize(init_data);
2827         }
2828         catch(SerializationError &e)
2829         {
2830                 errorstream<<"ClientEnvironment::addActiveObject():"
2831                                 <<" id="<<id<<" type="<<type
2832                                 <<": SerializationError in initialize(): "
2833                                 <<e.what()
2834                                 <<": init_data="<<serializeJsonString(init_data)
2835                                 <<std::endl;
2836         }
2837
2838         addActiveObject(obj);
2839 }
2840
2841 void ClientEnvironment::removeActiveObject(u16 id)
2842 {
2843         verbosestream<<"ClientEnvironment::removeActiveObject(): "
2844                         <<"id="<<id<<std::endl;
2845         ClientActiveObject* obj = getActiveObject(id);
2846         if(obj == NULL)
2847         {
2848                 infostream<<"ClientEnvironment::removeActiveObject(): "
2849                                 <<"id="<<id<<" not found"<<std::endl;
2850                 return;
2851         }
2852         obj->removeFromScene(true);
2853         delete obj;
2854         m_active_objects.erase(id);
2855 }
2856
2857 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2858 {
2859         ClientActiveObject *obj = getActiveObject(id);
2860         if (obj == NULL) {
2861                 infostream << "ClientEnvironment::processActiveObjectMessage():"
2862                         << " got message for id=" << id << ", which doesn't exist."
2863                         << std::endl;
2864                 return;
2865         }
2866
2867         try {
2868                 obj->processMessage(data);
2869         } catch (SerializationError &e) {
2870                 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2871                         << " id=" << id << " type=" << obj->getType()
2872                         << " SerializationError in processMessage(): " << e.what()
2873                         << std::endl;
2874         }
2875 }
2876
2877 /*
2878         Callbacks for activeobjects
2879 */
2880
2881 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2882 {
2883         LocalPlayer *lplayer = getLocalPlayer();
2884         assert(lplayer);
2885
2886         if (handle_hp) {
2887                 if (lplayer->hp > damage)
2888                         lplayer->hp -= damage;
2889                 else
2890                         lplayer->hp = 0;
2891         }
2892
2893         ClientEnvEvent event;
2894         event.type = CEE_PLAYER_DAMAGE;
2895         event.player_damage.amount = damage;
2896         event.player_damage.send_to_server = handle_hp;
2897         m_client_event_queue.push(event);
2898 }
2899
2900 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2901 {
2902         ClientEnvEvent event;
2903         event.type = CEE_PLAYER_BREATH;
2904         event.player_breath.amount = breath;
2905         m_client_event_queue.push(event);
2906 }
2907
2908 /*
2909         Client likes to call these
2910 */
2911
2912 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2913                 std::vector<DistanceSortedActiveObject> &dest)
2914 {
2915         for(std::map<u16, ClientActiveObject*>::iterator
2916                         i = m_active_objects.begin();
2917                         i != m_active_objects.end(); ++i)
2918         {
2919                 ClientActiveObject* obj = i->second;
2920
2921                 f32 d = (obj->getPosition() - origin).getLength();
2922
2923                 if(d > max_d)
2924                         continue;
2925
2926                 DistanceSortedActiveObject dso(obj, d);
2927
2928                 dest.push_back(dso);
2929         }
2930 }
2931
2932 ClientEnvEvent ClientEnvironment::getClientEvent()
2933 {
2934         ClientEnvEvent event;
2935         if(m_client_event_queue.empty())
2936                 event.type = CEE_NONE;
2937         else {
2938                 event = m_client_event_queue.front();
2939                 m_client_event_queue.pop();
2940         }
2941         return event;
2942 }
2943
2944 #endif // #ifndef SERVER