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