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