More code cleanup (UNORDERED + RemotePlayer/LocalPlayer)
[oweals/minetest.git] / src / environment.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <fstream>
21 #include "environment.h"
22 #include "filesys.h"
23 #include "porting.h"
24 #include "collision.h"
25 #include "content_mapnode.h"
26 #include "mapblock.h"
27 #include "serverobject.h"
28 #include "content_sao.h"
29 #include "settings.h"
30 #include "log.h"
31 #include "profiler.h"
32 #include "scripting_game.h"
33 #include "nodedef.h"
34 #include "nodemetadata.h"
35 #include "gamedef.h"
36 #ifndef SERVER
37 #include "clientmap.h"
38 #include "localplayer.h"
39 #include "mapblock_mesh.h"
40 #include "event.h"
41 #endif
42 #include "server.h"
43 #include "daynightratio.h"
44 #include "map.h"
45 #include "emerge.h"
46 #include "util/serialize.h"
47 #include "threading/mutex_auto_lock.h"
48
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50
51 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
52
53 // A number that is much smaller than the timeout for particle spawners should/could ever be
54 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
55
56 Environment::Environment():
57         m_time_of_day_speed(0),
58         m_time_of_day(9000),
59         m_time_of_day_f(9000./24000),
60         m_time_conversion_skew(0.0f),
61         m_enable_day_night_ratio_override(false),
62         m_day_night_ratio_override(0.0f)
63 {
64         m_cache_enable_shaders = g_settings->getBool("enable_shaders");
65         m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval");
66         m_cache_abm_interval = g_settings->getFloat("abm_interval");
67         m_cache_nodetimer_interval = g_settings->getFloat("nodetimer_interval");
68 }
69
70 Environment::~Environment()
71 {
72         // Deallocate players
73         for(std::vector<Player*>::iterator i = m_players.begin();
74                         i != m_players.end(); ++i) {
75                 delete (*i);
76         }
77 }
78
79 void Environment::addPlayer(Player *player)
80 {
81         DSTACK(FUNCTION_NAME);
82         /*
83                 Check that peer_ids are unique.
84                 Also check that names are unique.
85                 Exception: there can be multiple players with peer_id=0
86         */
87         // If peer id is non-zero, it has to be unique.
88         if(player->peer_id != 0)
89                 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
90         // Name has to be unique.
91         FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
92         // Add.
93         m_players.push_back(player);
94 }
95
96 void Environment::removePlayer(Player* player)
97 {
98         for (std::vector<Player*>::iterator it = m_players.begin();
99                         it != m_players.end(); ++it) {
100                 if ((*it) == player) {
101                         delete *it;
102                         m_players.erase(it);
103                         return;
104                 }
105         }
106 }
107
108 Player *Environment::getPlayer(u16 peer_id)
109 {
110         for (std::vector<Player*>::iterator i = m_players.begin();
111                         i != m_players.end(); ++i) {
112                 Player *player = *i;
113                 if (player->peer_id == peer_id)
114                         return player;
115         }
116         return NULL;
117 }
118
119 Player *Environment::getPlayer(const char *name)
120 {
121         for (std::vector<Player*>::iterator i = m_players.begin();
122                         i != m_players.end(); ++i) {
123                 Player *player = *i;
124                 if(strcmp(player->getName(), name) == 0)
125                         return player;
126         }
127         return NULL;
128 }
129
130 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                 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
607                         (*it)->protocol_version, reason, str_reason, reconnect);
608         }
609 }
610
611 void ServerEnvironment::saveLoadedPlayers()
612 {
613         std::string players_path = m_path_world + DIR_DELIM "players";
614         fs::CreateDir(players_path);
615
616         for (std::vector<Player*>::iterator it = m_players.begin();
617                         it != m_players.end();
618                         ++it) {
619                 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
620                 if (player->checkModified()) {
621                         player->save(players_path);
622                 }
623         }
624 }
625
626 void ServerEnvironment::savePlayer(RemotePlayer *player)
627 {
628         std::string players_path = m_path_world + DIR_DELIM "players";
629         fs::CreateDir(players_path);
630
631         player->save(players_path);
632 }
633
634 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername)
635 {
636         bool newplayer = false;
637         bool found = false;
638         std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
639         std::string path = players_path + playername;
640
641         RemotePlayer *player = getPlayer(playername.c_str());
642         if (!player) {
643                 player = new RemotePlayer(m_gamedef, "");
644                 newplayer = true;
645         }
646
647         for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
648                 //// Open file and deserialize
649                 std::ifstream is(path.c_str(), std::ios_base::binary);
650                 if (!is.good())
651                         continue;
652                 player->deSerialize(is, path);
653                 is.close();
654
655                 if (player->getName() == playername) {
656                         found = true;
657                         break;
658                 }
659
660                 path = players_path + playername + itos(i);
661         }
662
663         if (!found) {
664                 infostream << "Player file for player " << playername
665                                 << " not found" << std::endl;
666                 if (newplayer)
667                         delete player;
668                 return NULL;
669         }
670
671         if (newplayer)
672                 addPlayer(player);
673         player->setModified(false);
674         return player;
675 }
676
677 void ServerEnvironment::saveMeta()
678 {
679         std::string path = m_path_world + DIR_DELIM "env_meta.txt";
680
681         // Open file and serialize
682         std::ostringstream ss(std::ios_base::binary);
683
684         Settings args;
685         args.setU64("game_time", m_game_time);
686         args.setU64("time_of_day", getTimeOfDay());
687         args.setU64("last_clear_objects_time", m_last_clear_objects_time);
688         args.setU64("lbm_introduction_times_version", 1);
689         args.set("lbm_introduction_times",
690                 m_lbm_mgr.createIntroductionTimesString());
691         args.setU64("day_count", m_day_count);
692         args.writeLines(ss);
693         ss<<"EnvArgsEnd\n";
694
695         if(!fs::safeWriteToFile(path, ss.str()))
696         {
697                 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
698                                 <<path<<std::endl;
699                 throw SerializationError("Couldn't save env meta");
700         }
701 }
702
703 void ServerEnvironment::loadMeta()
704 {
705         std::string path = m_path_world + DIR_DELIM "env_meta.txt";
706
707         // Open file and deserialize
708         std::ifstream is(path.c_str(), std::ios_base::binary);
709         if (!is.good()) {
710                 infostream << "ServerEnvironment::loadMeta(): Failed to open "
711                                 << path << std::endl;
712                 throw SerializationError("Couldn't load env meta");
713         }
714
715         Settings args;
716
717         if (!args.parseConfigLines(is, "EnvArgsEnd")) {
718                 throw SerializationError("ServerEnvironment::loadMeta(): "
719                                 "EnvArgsEnd not found!");
720         }
721
722         try {
723                 m_game_time = args.getU64("game_time");
724         } catch (SettingNotFoundException &e) {
725                 // Getting this is crucial, otherwise timestamps are useless
726                 throw SerializationError("Couldn't load env meta game_time");
727         }
728
729         setTimeOfDay(args.exists("time_of_day") ?
730                 // set day to morning by default
731                 args.getU64("time_of_day") : 9000);
732
733         m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
734                 // If missing, do as if clearObjects was never called
735                 args.getU64("last_clear_objects_time") : 0;
736
737         std::string lbm_introduction_times = "";
738         try {
739                 u64 ver = args.getU64("lbm_introduction_times_version");
740                 if (ver == 1) {
741                         lbm_introduction_times = args.get("lbm_introduction_times");
742                 } else {
743                         infostream << "ServerEnvironment::loadMeta(): Non-supported"
744                                 << " introduction time version " << ver << std::endl;
745                 }
746         } catch (SettingNotFoundException &e) {
747                 // No problem, this is expected. Just continue with an empty string
748         }
749         m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
750
751         m_day_count = args.exists("day_count") ?
752                 args.getU64("day_count") : 0;
753 }
754
755 void ServerEnvironment::loadDefaultMeta()
756 {
757         m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
758 }
759
760 struct ActiveABM
761 {
762         ActiveBlockModifier *abm;
763         int chance;
764         std::set<content_t> required_neighbors;
765 };
766
767 class ABMHandler
768 {
769 private:
770         ServerEnvironment *m_env;
771         std::map<content_t, std::vector<ActiveABM> > m_aabms;
772 public:
773         ABMHandler(std::vector<ABMWithState> &abms,
774                         float dtime_s, ServerEnvironment *env,
775                         bool use_timers):
776                 m_env(env)
777         {
778                 if(dtime_s < 0.001)
779                         return;
780                 INodeDefManager *ndef = env->getGameDef()->ndef();
781                 for(std::vector<ABMWithState>::iterator
782                                 i = abms.begin(); i != abms.end(); ++i) {
783                         ActiveBlockModifier *abm = i->abm;
784                         float trigger_interval = abm->getTriggerInterval();
785                         if(trigger_interval < 0.001)
786                                 trigger_interval = 0.001;
787                         float actual_interval = dtime_s;
788                         if(use_timers){
789                                 i->timer += dtime_s;
790                                 if(i->timer < trigger_interval)
791                                         continue;
792                                 i->timer -= trigger_interval;
793                                 actual_interval = trigger_interval;
794                         }
795                         float chance = abm->getTriggerChance();
796                         if(chance == 0)
797                                 chance = 1;
798                         ActiveABM aabm;
799                         aabm.abm = abm;
800                         if(abm->getSimpleCatchUp()) {
801                                 float intervals = actual_interval / trigger_interval;
802                                 if(intervals == 0)
803                                         continue;
804                                 aabm.chance = chance / intervals;
805                                 if(aabm.chance == 0)
806                                         aabm.chance = 1;
807                         } else {
808                                 aabm.chance = chance;
809                         }
810                         // Trigger neighbors
811                         std::set<std::string> required_neighbors_s
812                                         = abm->getRequiredNeighbors();
813                         for(std::set<std::string>::iterator
814                                         i = required_neighbors_s.begin();
815                                         i != required_neighbors_s.end(); ++i)
816                         {
817                                 ndef->getIds(*i, aabm.required_neighbors);
818                         }
819                         // Trigger contents
820                         std::set<std::string> contents_s = abm->getTriggerContents();
821                         for(std::set<std::string>::iterator
822                                         i = contents_s.begin(); i != contents_s.end(); ++i)
823                         {
824                                 std::set<content_t> ids;
825                                 ndef->getIds(*i, ids);
826                                 for(std::set<content_t>::const_iterator k = ids.begin();
827                                                 k != ids.end(); ++k)
828                                 {
829                                         content_t c = *k;
830                                         std::map<content_t, std::vector<ActiveABM> >::iterator j;
831                                         j = m_aabms.find(c);
832                                         if(j == m_aabms.end()){
833                                                 std::vector<ActiveABM> aabmlist;
834                                                 m_aabms[c] = aabmlist;
835                                                 j = m_aabms.find(c);
836                                         }
837                                         j->second.push_back(aabm);
838                                 }
839                         }
840                 }
841         }
842         // Find out how many objects the given block and its neighbours contain.
843         // Returns the number of objects in the block, and also in 'wider' the
844         // number of objects in the block and all its neighbours. The latter
845         // may an estimate if any neighbours are unloaded.
846         u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
847         {
848                 wider = 0;
849                 u32 wider_unknown_count = 0;
850                 for(s16 x=-1; x<=1; x++)
851                 for(s16 y=-1; y<=1; y++)
852                 for(s16 z=-1; z<=1; z++)
853                 {
854                         MapBlock *block2 = map->getBlockNoCreateNoEx(
855                                         block->getPos() + v3s16(x,y,z));
856                         if(block2==NULL){
857                                 wider_unknown_count++;
858                                 continue;
859                         }
860                         wider += block2->m_static_objects.m_active.size()
861                                         + block2->m_static_objects.m_stored.size();
862                 }
863                 // Extrapolate
864                 u32 active_object_count = block->m_static_objects.m_active.size();
865                 u32 wider_known_count = 3*3*3 - wider_unknown_count;
866                 wider += wider_unknown_count * wider / wider_known_count;
867                 return active_object_count;
868
869         }
870         void apply(MapBlock *block)
871         {
872                 if(m_aabms.empty())
873                         return;
874
875                 ServerMap *map = &m_env->getServerMap();
876
877                 u32 active_object_count_wider;
878                 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
879                 m_env->m_added_objects = 0;
880
881                 v3s16 p0;
882                 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
883                 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
884                 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
885                 {
886                         MapNode n = block->getNodeNoEx(p0);
887                         content_t c = n.getContent();
888                         v3s16 p = p0 + block->getPosRelative();
889
890                         std::map<content_t, std::vector<ActiveABM> >::iterator j;
891                         j = m_aabms.find(c);
892                         if(j == m_aabms.end())
893                                 continue;
894
895                         for(std::vector<ActiveABM>::iterator
896                                         i = j->second.begin(); i != j->second.end(); ++i) {
897                                 if(myrand() % i->chance != 0)
898                                         continue;
899
900                                 // Check neighbors
901                                 if(!i->required_neighbors.empty())
902                                 {
903                                         v3s16 p1;
904                                         for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
905                                         for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
906                                         for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
907                                         {
908                                                 if(p1 == p)
909                                                         continue;
910                                                 MapNode n = map->getNodeNoEx(p1);
911                                                 content_t c = n.getContent();
912                                                 std::set<content_t>::const_iterator k;
913                                                 k = i->required_neighbors.find(c);
914                                                 if(k != i->required_neighbors.end()){
915                                                         goto neighbor_found;
916                                                 }
917                                         }
918                                         // No required neighbor found
919                                         continue;
920                                 }
921 neighbor_found:
922
923                                 // Call all the trigger variations
924                                 i->abm->trigger(m_env, p, n);
925                                 i->abm->trigger(m_env, p, n,
926                                                 active_object_count, active_object_count_wider);
927
928                                 // Count surrounding objects again if the abms added any
929                                 if(m_env->m_added_objects > 0) {
930                                         active_object_count = countObjects(block, map, active_object_count_wider);
931                                         m_env->m_added_objects = 0;
932                                 }
933                         }
934                 }
935         }
936 };
937
938 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
939 {
940         // Reset usage timer immediately, otherwise a block that becomes active
941         // again at around the same time as it would normally be unloaded will
942         // get unloaded incorrectly. (I think this still leaves a small possibility
943         // of a race condition between this and server::AsyncRunStep, which only
944         // some kind of synchronisation will fix, but it at least reduces the window
945         // of opportunity for it to break from seconds to nanoseconds)
946         block->resetUsageTimer();
947
948         // Get time difference
949         u32 dtime_s = 0;
950         u32 stamp = block->getTimestamp();
951         if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
952                 dtime_s = m_game_time - stamp;
953         dtime_s += additional_dtime;
954
955         /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
956                         <<stamp<<", game time: "<<m_game_time<<std::endl;*/
957
958         // Remove stored static objects if clearObjects was called since block's timestamp
959         if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
960                 block->m_static_objects.m_stored.clear();
961                 // do not set changed flag to avoid unnecessary mapblock writes
962         }
963
964         // Set current time as timestamp
965         block->setTimestampNoChangedFlag(m_game_time);
966
967         /*infostream<<"ServerEnvironment::activateBlock(): block is "
968                         <<dtime_s<<" seconds old."<<std::endl;*/
969
970         // Activate stored objects
971         activateObjects(block, dtime_s);
972
973         /* Handle LoadingBlockModifiers */
974         m_lbm_mgr.applyLBMs(this, block, stamp);
975
976         // Run node timers
977         std::vector<NodeTimer> elapsed_timers =
978                 block->m_node_timers.step((float)dtime_s);
979         if (!elapsed_timers.empty()) {
980                 MapNode n;
981                 for (std::vector<NodeTimer>::iterator
982                                 i = elapsed_timers.begin();
983                                 i != elapsed_timers.end(); ++i){
984                         n = block->getNodeNoEx(i->position);
985                         v3s16 p = i->position + block->getPosRelative();
986                         if (m_script->node_on_timer(p, n, i->elapsed))
987                                 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
988                 }
989         }
990
991         /* Handle ActiveBlockModifiers */
992         ABMHandler abmhandler(m_abms, dtime_s, this, false);
993         abmhandler.apply(block);
994 }
995
996 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
997 {
998         m_abms.push_back(ABMWithState(abm));
999 }
1000
1001 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1002 {
1003         m_lbm_mgr.addLBMDef(lbm);
1004 }
1005
1006 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1007 {
1008         INodeDefManager *ndef = m_gamedef->ndef();
1009         MapNode n_old = m_map->getNodeNoEx(p);
1010
1011         // Call destructor
1012         if (ndef->get(n_old).has_on_destruct)
1013                 m_script->node_on_destruct(p, n_old);
1014
1015         // Replace node
1016         if (!m_map->addNodeWithEvent(p, n))
1017                 return false;
1018
1019         // Update active VoxelManipulator if a mapgen thread
1020         m_map->updateVManip(p);
1021
1022         // Call post-destructor
1023         if (ndef->get(n_old).has_after_destruct)
1024                 m_script->node_after_destruct(p, n_old);
1025
1026         // Call constructor
1027         if (ndef->get(n).has_on_construct)
1028                 m_script->node_on_construct(p, n);
1029
1030         return true;
1031 }
1032
1033 bool ServerEnvironment::removeNode(v3s16 p)
1034 {
1035         INodeDefManager *ndef = m_gamedef->ndef();
1036         MapNode n_old = m_map->getNodeNoEx(p);
1037
1038         // Call destructor
1039         if (ndef->get(n_old).has_on_destruct)
1040                 m_script->node_on_destruct(p, n_old);
1041
1042         // Replace with air
1043         // This is slightly optimized compared to addNodeWithEvent(air)
1044         if (!m_map->removeNodeWithEvent(p))
1045                 return false;
1046
1047         // Update active VoxelManipulator if a mapgen thread
1048         m_map->updateVManip(p);
1049
1050         // Call post-destructor
1051         if (ndef->get(n_old).has_after_destruct)
1052                 m_script->node_after_destruct(p, n_old);
1053
1054         // Air doesn't require constructor
1055         return true;
1056 }
1057
1058 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1059 {
1060         if (!m_map->addNodeWithEvent(p, n, false))
1061                 return false;
1062
1063         // Update active VoxelManipulator if a mapgen thread
1064         m_map->updateVManip(p);
1065
1066         return true;
1067 }
1068
1069 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1070 {
1071         for (ActiveObjectMap::iterator i = m_active_objects.begin();
1072                         i != m_active_objects.end(); ++i) {
1073                 ServerActiveObject* obj = i->second;
1074                 u16 id = i->first;
1075                 v3f objectpos = obj->getBasePosition();
1076                 if (objectpos.getDistanceFrom(pos) > radius)
1077                         continue;
1078                 objects.push_back(id);
1079         }
1080 }
1081
1082 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1083 {
1084         infostream << "ServerEnvironment::clearObjects(): "
1085                 << "Removing all active objects" << std::endl;
1086         std::vector<u16> objects_to_remove;
1087         for (ActiveObjectMap::iterator i = m_active_objects.begin();
1088                         i != m_active_objects.end(); ++i) {
1089                 ServerActiveObject* obj = i->second;
1090                 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1091                         continue;
1092                 u16 id = i->first;
1093                 // Delete static object if block is loaded
1094                 if (obj->m_static_exists) {
1095                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1096                         if (block) {
1097                                 block->m_static_objects.remove(id);
1098                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1099                                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1100                                 obj->m_static_exists = false;
1101                         }
1102                 }
1103                 // If known by some client, don't delete immediately
1104                 if (obj->m_known_by_count > 0) {
1105                         obj->m_pending_deactivation = true;
1106                         obj->m_removed = true;
1107                         continue;
1108                 }
1109
1110                 // Tell the object about removal
1111                 obj->removingFromEnvironment();
1112                 // Deregister in scripting api
1113                 m_script->removeObjectReference(obj);
1114
1115                 // Delete active object
1116                 if (obj->environmentDeletes())
1117                         delete obj;
1118                 // Id to be removed from m_active_objects
1119                 objects_to_remove.push_back(id);
1120         }
1121
1122         // Remove references from m_active_objects
1123         for (std::vector<u16>::iterator i = objects_to_remove.begin();
1124                         i != objects_to_remove.end(); ++i) {
1125                 m_active_objects.erase(*i);
1126         }
1127
1128         // Get list of loaded blocks
1129         std::vector<v3s16> loaded_blocks;
1130         infostream << "ServerEnvironment::clearObjects(): "
1131                 << "Listing all loaded blocks" << std::endl;
1132         m_map->listAllLoadedBlocks(loaded_blocks);
1133         infostream << "ServerEnvironment::clearObjects(): "
1134                 << "Done listing all loaded blocks: "
1135                 << loaded_blocks.size()<<std::endl;
1136
1137         // Get list of loadable blocks
1138         std::vector<v3s16> loadable_blocks;
1139         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1140                 infostream << "ServerEnvironment::clearObjects(): "
1141                         << "Listing all loadable blocks" << std::endl;
1142                 m_map->listAllLoadableBlocks(loadable_blocks);
1143                 infostream << "ServerEnvironment::clearObjects(): "
1144                         << "Done listing all loadable blocks: "
1145                         << loadable_blocks.size() << std::endl;
1146         } else {
1147                 loadable_blocks = loaded_blocks;
1148         }
1149
1150         infostream << "ServerEnvironment::clearObjects(): "
1151                 << "Now clearing objects in " << loadable_blocks.size()
1152                 << " blocks" << std::endl;
1153
1154         // Grab a reference on each loaded block to avoid unloading it
1155         for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1156                         i != loaded_blocks.end(); ++i) {
1157                 v3s16 p = *i;
1158                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1159                 assert(block != NULL);
1160                 block->refGrab();
1161         }
1162
1163         // Remove objects in all loadable blocks
1164         u32 unload_interval = U32_MAX;
1165         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1166                 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1167                 unload_interval = MYMAX(unload_interval, 1);
1168         }
1169         u32 report_interval = loadable_blocks.size() / 10;
1170         u32 num_blocks_checked = 0;
1171         u32 num_blocks_cleared = 0;
1172         u32 num_objs_cleared = 0;
1173         for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1174                         i != loadable_blocks.end(); ++i) {
1175                 v3s16 p = *i;
1176                 MapBlock *block = m_map->emergeBlock(p, false);
1177                 if (!block) {
1178                         errorstream << "ServerEnvironment::clearObjects(): "
1179                                 << "Failed to emerge block " << PP(p) << std::endl;
1180                         continue;
1181                 }
1182                 u32 num_stored = block->m_static_objects.m_stored.size();
1183                 u32 num_active = block->m_static_objects.m_active.size();
1184                 if (num_stored != 0 || num_active != 0) {
1185                         block->m_static_objects.m_stored.clear();
1186                         block->m_static_objects.m_active.clear();
1187                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1188                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1189                         num_objs_cleared += num_stored + num_active;
1190                         num_blocks_cleared++;
1191                 }
1192                 num_blocks_checked++;
1193
1194                 if (report_interval != 0 &&
1195                                 num_blocks_checked % report_interval == 0) {
1196                         float percent = 100.0 * (float)num_blocks_checked /
1197                                 loadable_blocks.size();
1198                         infostream << "ServerEnvironment::clearObjects(): "
1199                                 << "Cleared " << num_objs_cleared << " objects"
1200                                 << " in " << num_blocks_cleared << " blocks ("
1201                                 << percent << "%)" << std::endl;
1202                 }
1203                 if (num_blocks_checked % unload_interval == 0) {
1204                         m_map->unloadUnreferencedBlocks();
1205                 }
1206         }
1207         m_map->unloadUnreferencedBlocks();
1208
1209         // Drop references that were added above
1210         for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1211                         i != loaded_blocks.end(); ++i) {
1212                 v3s16 p = *i;
1213                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1214                 assert(block);
1215                 block->refDrop();
1216         }
1217
1218         m_last_clear_objects_time = m_game_time;
1219
1220         infostream << "ServerEnvironment::clearObjects(): "
1221                 << "Finished: Cleared " << num_objs_cleared << " objects"
1222                 << " in " << num_blocks_cleared << " blocks" << std::endl;
1223 }
1224
1225 void ServerEnvironment::step(float dtime)
1226 {
1227         DSTACK(FUNCTION_NAME);
1228
1229         //TimeTaker timer("ServerEnv step");
1230
1231         /* Step time of day */
1232         stepTimeOfDay(dtime);
1233
1234         // Update this one
1235         // NOTE: This is kind of funny on a singleplayer game, but doesn't
1236         // really matter that much.
1237         static const float server_step = g_settings->getFloat("dedicated_server_step");
1238         m_recommended_send_interval = server_step;
1239
1240         /*
1241                 Increment game time
1242         */
1243         {
1244                 m_game_time_fraction_counter += dtime;
1245                 u32 inc_i = (u32)m_game_time_fraction_counter;
1246                 m_game_time += inc_i;
1247                 m_game_time_fraction_counter -= (float)inc_i;
1248         }
1249
1250         /*
1251                 Handle players
1252         */
1253         {
1254                 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1255                 for (std::vector<Player*>::iterator i = m_players.begin();
1256                                 i != m_players.end(); ++i) {
1257                         RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1258                         assert(player);
1259
1260                         // Ignore disconnected players
1261                         if(player->peer_id == 0)
1262                                 continue;
1263
1264                         // Move
1265                         player->move(dtime, this, 100*BS);
1266                 }
1267         }
1268
1269         /*
1270                 Manage active block list
1271         */
1272         if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1273                 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1274                 /*
1275                         Get player block positions
1276                 */
1277                 std::vector<v3s16> players_blockpos;
1278                 for (std::vector<Player*>::iterator i = m_players.begin();
1279                                 i != m_players.end(); ++i) {
1280                         RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1281                         assert(player);
1282                         // Ignore disconnected players
1283                         if (player->peer_id == 0)
1284                                 continue;
1285
1286                         v3s16 blockpos = getNodeBlockPos(
1287                                         floatToInt(player->getPosition(), BS));
1288                         players_blockpos.push_back(blockpos);
1289                 }
1290
1291                 /*
1292                         Update list of active blocks, collecting changes
1293                 */
1294                 static const s16 active_block_range = g_settings->getS16("active_block_range");
1295                 std::set<v3s16> blocks_removed;
1296                 std::set<v3s16> blocks_added;
1297                 m_active_blocks.update(players_blockpos, active_block_range,
1298                                 blocks_removed, blocks_added);
1299
1300                 /*
1301                         Handle removed blocks
1302                 */
1303
1304                 // Convert active objects that are no more in active blocks to static
1305                 deactivateFarObjects(false);
1306
1307                 for(std::set<v3s16>::iterator
1308                                 i = blocks_removed.begin();
1309                                 i != blocks_removed.end(); ++i) {
1310                         v3s16 p = *i;
1311
1312                         /* infostream<<"Server: Block " << PP(p)
1313                                 << " became inactive"<<std::endl; */
1314
1315                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1316                         if(block==NULL)
1317                                 continue;
1318
1319                         // Set current time as timestamp (and let it set ChangedFlag)
1320                         block->setTimestamp(m_game_time);
1321                 }
1322
1323                 /*
1324                         Handle added blocks
1325                 */
1326
1327                 for(std::set<v3s16>::iterator
1328                                 i = blocks_added.begin();
1329                                 i != blocks_added.end(); ++i)
1330                 {
1331                         v3s16 p = *i;
1332
1333                         MapBlock *block = m_map->getBlockOrEmerge(p);
1334                         if(block==NULL){
1335                                 m_active_blocks.m_list.erase(p);
1336                                 continue;
1337                         }
1338
1339                         activateBlock(block);
1340                         /* infostream<<"Server: Block " << PP(p)
1341                                 << " became active"<<std::endl; */
1342                 }
1343         }
1344
1345         /*
1346                 Mess around in active blocks
1347         */
1348         if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1349                 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1350
1351                 float dtime = m_cache_nodetimer_interval;
1352
1353                 for(std::set<v3s16>::iterator
1354                                 i = m_active_blocks.m_list.begin();
1355                                 i != m_active_blocks.m_list.end(); ++i)
1356                 {
1357                         v3s16 p = *i;
1358
1359                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1360                                         <<") being handled"<<std::endl;*/
1361
1362                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1363                         if(block==NULL)
1364                                 continue;
1365
1366                         // Reset block usage timer
1367                         block->resetUsageTimer();
1368
1369                         // Set current time as timestamp
1370                         block->setTimestampNoChangedFlag(m_game_time);
1371                         // If time has changed much from the one on disk,
1372                         // set block to be saved when it is unloaded
1373                         if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1374                                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1375                                         MOD_REASON_BLOCK_EXPIRED);
1376
1377                         // Run node timers
1378                         std::vector<NodeTimer> elapsed_timers =
1379                                 block->m_node_timers.step((float)dtime);
1380                         if (!elapsed_timers.empty()) {
1381                                 MapNode n;
1382                                 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1383                                                 i != elapsed_timers.end(); ++i) {
1384                                         n = block->getNodeNoEx(i->position);
1385                                         p = i->position + block->getPosRelative();
1386                                         if (m_script->node_on_timer(p, n, i->elapsed)) {
1387                                                 block->setNodeTimer(NodeTimer(
1388                                                         i->timeout, 0, i->position));
1389                                         }
1390                                 }
1391                         }
1392                 }
1393         }
1394
1395         if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1396         do{ // breakable
1397                 if(m_active_block_interval_overload_skip > 0){
1398                         ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1399                         m_active_block_interval_overload_skip--;
1400                         break;
1401                 }
1402                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1403                 TimeTaker timer("modify in active blocks per interval");
1404
1405                 // Initialize handling of ActiveBlockModifiers
1406                 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1407
1408                 for(std::set<v3s16>::iterator
1409                                 i = m_active_blocks.m_list.begin();
1410                                 i != m_active_blocks.m_list.end(); ++i)
1411                 {
1412                         v3s16 p = *i;
1413
1414                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1415                                         <<") being handled"<<std::endl;*/
1416
1417                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1418                         if(block == NULL)
1419                                 continue;
1420
1421                         // Set current time as timestamp
1422                         block->setTimestampNoChangedFlag(m_game_time);
1423
1424                         /* Handle ActiveBlockModifiers */
1425                         abmhandler.apply(block);
1426                 }
1427
1428                 u32 time_ms = timer.stop(true);
1429                 u32 max_time_ms = 200;
1430                 if(time_ms > max_time_ms){
1431                         warningstream<<"active block modifiers took "
1432                                         <<time_ms<<"ms (longer than "
1433                                         <<max_time_ms<<"ms)"<<std::endl;
1434                         m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1435                 }
1436         }while(0);
1437
1438         /*
1439                 Step script environment (run global on_step())
1440         */
1441         m_script->environment_Step(dtime);
1442
1443         /*
1444                 Step active objects
1445         */
1446         {
1447                 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1448                 //TimeTaker timer("Step active objects");
1449
1450                 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1451
1452                 // This helps the objects to send data at the same time
1453                 bool send_recommended = false;
1454                 m_send_recommended_timer += dtime;
1455                 if(m_send_recommended_timer > getSendRecommendedInterval())
1456                 {
1457                         m_send_recommended_timer -= getSendRecommendedInterval();
1458                         send_recommended = true;
1459                 }
1460
1461                 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1462                                 i != m_active_objects.end(); ++i) {
1463                         ServerActiveObject* obj = i->second;
1464                         // Don't step if is to be removed or stored statically
1465                         if(obj->m_removed || obj->m_pending_deactivation)
1466                                 continue;
1467                         // Step object
1468                         obj->step(dtime, send_recommended);
1469                         // Read messages from object
1470                         while(!obj->m_messages_out.empty())
1471                         {
1472                                 m_active_object_messages.push(
1473                                                 obj->m_messages_out.front());
1474                                 obj->m_messages_out.pop();
1475                         }
1476                 }
1477         }
1478
1479         /*
1480                 Manage active objects
1481         */
1482         if(m_object_management_interval.step(dtime, 0.5))
1483         {
1484                 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1485                 /*
1486                         Remove objects that satisfy (m_removed && m_known_by_count==0)
1487                 */
1488                 removeRemovedObjects();
1489         }
1490
1491         /*
1492                 Manage particle spawner expiration
1493         */
1494         if (m_particle_management_interval.step(dtime, 1.0)) {
1495                 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1496                                 i != m_particle_spawners.end(); ) {
1497                         //non expiring spawners
1498                         if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1499                                 ++i;
1500                                 continue;
1501                         }
1502
1503                         i->second -= 1.0f;
1504                         if (i->second <= 0.f)
1505                                 m_particle_spawners.erase(i++);
1506                         else
1507                                 ++i;
1508                 }
1509         }
1510 }
1511
1512 u32 ServerEnvironment::addParticleSpawner(float exptime)
1513 {
1514         // Timers with lifetime 0 do not expire
1515         float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1516
1517         u32 id = 0;
1518         for (;;) { // look for unused particlespawner id
1519                 id++;
1520                 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1521                 if (f == m_particle_spawners.end()) {
1522                         m_particle_spawners[id] = time;
1523                         break;
1524                 }
1525         }
1526         return id;
1527 }
1528
1529 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1530 {
1531         ActiveObjectMap::iterator n = m_active_objects.find(id);
1532         return (n != m_active_objects.end() ? n->second : NULL);
1533 }
1534
1535 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1536 {
1537         if (id == 0)
1538                 return false;
1539
1540         return objects.find(id) == objects.end();
1541 }
1542
1543 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1544 {
1545         //try to reuse id's as late as possible
1546         static u16 last_used_id = 0;
1547         u16 startid = last_used_id;
1548         for(;;)
1549         {
1550                 last_used_id ++;
1551                 if(isFreeServerActiveObjectId(last_used_id, objects))
1552                         return last_used_id;
1553
1554                 if(last_used_id == startid)
1555                         return 0;
1556         }
1557 }
1558
1559 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1560 {
1561         assert(object); // Pre-condition
1562         m_added_objects++;
1563         u16 id = addActiveObjectRaw(object, true, 0);
1564         return id;
1565 }
1566
1567 /*
1568         Finds out what new objects have been added to
1569         inside a radius around a position
1570 */
1571 void ServerEnvironment::getAddedActiveObjects(RemotePlayer *player, s16 radius,
1572                 s16 player_radius,
1573                 std::set<u16> &current_objects,
1574                 std::queue<u16> &added_objects)
1575 {
1576         f32 radius_f = radius * BS;
1577         f32 player_radius_f = player_radius * BS;
1578
1579         if (player_radius_f < 0)
1580                 player_radius_f = 0;
1581
1582         /*
1583                 Go through the object list,
1584                 - discard m_removed objects,
1585                 - discard objects that are too far away,
1586                 - discard objects that are found in current_objects.
1587                 - add remaining objects to added_objects
1588         */
1589         for(ActiveObjectMap::iterator i = m_active_objects.begin();
1590                         i != m_active_objects.end(); ++i) {
1591                 u16 id = i->first;
1592
1593                 // Get object
1594                 ServerActiveObject *object = i->second;
1595                 if(object == NULL)
1596                         continue;
1597
1598                 // Discard if removed or deactivating
1599                 if(object->m_removed || object->m_pending_deactivation)
1600                         continue;
1601
1602                 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1603                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1604                         // Discard if too far
1605                         if (distance_f > player_radius_f && player_radius_f != 0)
1606                                 continue;
1607                 } else if (distance_f > radius_f)
1608                         continue;
1609
1610                 // Discard if already on current_objects
1611                 std::set<u16>::iterator n;
1612                 n = current_objects.find(id);
1613                 if(n != current_objects.end())
1614                         continue;
1615                 // Add to added_objects
1616                 added_objects.push(id);
1617         }
1618 }
1619
1620 /*
1621         Finds out what objects have been removed from
1622         inside a radius around a position
1623 */
1624 void ServerEnvironment::getRemovedActiveObjects(RemotePlayer *player, s16 radius,
1625                 s16 player_radius,
1626                 std::set<u16> &current_objects,
1627                 std::queue<u16> &removed_objects)
1628 {
1629         f32 radius_f = radius * BS;
1630         f32 player_radius_f = player_radius * BS;
1631
1632         if (player_radius_f < 0)
1633                 player_radius_f = 0;
1634
1635         /*
1636                 Go through current_objects; object is removed if:
1637                 - object is not found in m_active_objects (this is actually an
1638                   error condition; objects should be set m_removed=true and removed
1639                   only after all clients have been informed about removal), or
1640                 - object has m_removed=true, or
1641                 - object is too far away
1642         */
1643         for(std::set<u16>::iterator
1644                         i = current_objects.begin();
1645                         i != current_objects.end(); ++i)
1646         {
1647                 u16 id = *i;
1648                 ServerActiveObject *object = getActiveObject(id);
1649
1650                 if (object == NULL) {
1651                         infostream << "ServerEnvironment::getRemovedActiveObjects():"
1652                                 << " object in current_objects is NULL" << std::endl;
1653                         removed_objects.push(id);
1654                         continue;
1655                 }
1656
1657                 if (object->m_removed || object->m_pending_deactivation) {
1658                         removed_objects.push(id);
1659                         continue;
1660                 }
1661
1662                 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1663                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1664                         if (distance_f <= player_radius_f || player_radius_f == 0)
1665                                 continue;
1666                 } else if (distance_f <= radius_f)
1667                         continue;
1668
1669                 // Object is no longer visible
1670                 removed_objects.push(id);
1671         }
1672 }
1673
1674 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1675         v3s16 blockpos, bool static_exists, v3s16 static_block)
1676 {
1677         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1678         if (!block)
1679                 return;
1680
1681         for (std::map<u16, StaticObject>::iterator
1682                         so_it = block->m_static_objects.m_active.begin();
1683                         so_it != block->m_static_objects.m_active.end(); ++so_it) {
1684                 // Get the ServerActiveObject counterpart to this StaticObject
1685                 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1686                 if (ao_it == m_active_objects.end()) {
1687                         // If this ever happens, there must be some kind of nasty bug.
1688                         errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1689                                 "Object from MapBlock::m_static_objects::m_active not found "
1690                                 "in m_active_objects";
1691                         continue;
1692                 }
1693
1694                 ServerActiveObject *sao = ao_it->second;
1695                 sao->m_static_exists = static_exists;
1696                 sao->m_static_block  = static_block;
1697         }
1698 }
1699
1700 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1701 {
1702         if(m_active_object_messages.empty())
1703                 return ActiveObjectMessage(0);
1704
1705         ActiveObjectMessage message = m_active_object_messages.front();
1706         m_active_object_messages.pop();
1707         return message;
1708 }
1709
1710 /*
1711         ************ Private methods *************
1712 */
1713
1714 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1715                 bool set_changed, u32 dtime_s)
1716 {
1717         assert(object); // Pre-condition
1718         if(object->getId() == 0){
1719                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1720                 if(new_id == 0)
1721                 {
1722                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1723                                         <<"no free ids available"<<std::endl;
1724                         if(object->environmentDeletes())
1725                                 delete object;
1726                         return 0;
1727                 }
1728                 object->setId(new_id);
1729         }
1730         else{
1731                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1732                                 <<"supplied with id "<<object->getId()<<std::endl;
1733         }
1734
1735         if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1736                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1737                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1738                 if(object->environmentDeletes())
1739                         delete object;
1740                 return 0;
1741         }
1742
1743         if (objectpos_over_limit(object->getBasePosition())) {
1744                 v3f p = object->getBasePosition();
1745                 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1746                         << "object position (" << p.X << "," << p.Y << "," << p.Z
1747                         << ") outside maximum range" << std::endl;
1748                 if (object->environmentDeletes())
1749                         delete object;
1750                 return 0;
1751         }
1752
1753         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1754                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1755
1756         m_active_objects[object->getId()] = object;
1757
1758         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1759                         <<"Added id="<<object->getId()<<"; there are now "
1760                         <<m_active_objects.size()<<" active objects."
1761                         <<std::endl;
1762
1763         // Register reference in scripting api (must be done before post-init)
1764         m_script->addObjectReference(object);
1765         // Post-initialize object
1766         object->addedToEnvironment(dtime_s);
1767
1768         // Add static data to block
1769         if(object->isStaticAllowed())
1770         {
1771                 // Add static object to active static list of the block
1772                 v3f objectpos = object->getBasePosition();
1773                 std::string staticdata = object->getStaticData();
1774                 StaticObject s_obj(object->getType(), objectpos, staticdata);
1775                 // Add to the block where the object is located in
1776                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1777                 MapBlock *block = m_map->emergeBlock(blockpos);
1778                 if(block){
1779                         block->m_static_objects.m_active[object->getId()] = s_obj;
1780                         object->m_static_exists = true;
1781                         object->m_static_block = blockpos;
1782
1783                         if(set_changed)
1784                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1785                                         MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1786                 } else {
1787                         v3s16 p = floatToInt(objectpos, BS);
1788                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1789                                         <<"could not emerge block for storing id="<<object->getId()
1790                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
1791                 }
1792         }
1793
1794         return object->getId();
1795 }
1796
1797 /*
1798         Remove objects that satisfy (m_removed && m_known_by_count==0)
1799 */
1800 void ServerEnvironment::removeRemovedObjects()
1801 {
1802         std::vector<u16> objects_to_remove;
1803         for(ActiveObjectMap::iterator i = m_active_objects.begin();
1804                         i != m_active_objects.end(); ++i) {
1805                 u16 id = i->first;
1806                 ServerActiveObject* obj = i->second;
1807                 // This shouldn't happen but check it
1808                 if(obj == NULL)
1809                 {
1810                         infostream<<"NULL object found in ServerEnvironment"
1811                                         <<" while finding removed objects. id="<<id<<std::endl;
1812                         // Id to be removed from m_active_objects
1813                         objects_to_remove.push_back(id);
1814                         continue;
1815                 }
1816
1817                 /*
1818                         We will delete objects that are marked as removed or thatare
1819                         waiting for deletion after deactivation
1820                 */
1821                 if (!obj->m_removed && !obj->m_pending_deactivation)
1822                         continue;
1823
1824                 /*
1825                         Delete static data from block if is marked as removed
1826                 */
1827                 if(obj->m_static_exists && obj->m_removed)
1828                 {
1829                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1830                         if (block) {
1831                                 block->m_static_objects.remove(id);
1832                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1833                                         MOD_REASON_REMOVE_OBJECTS_REMOVE);
1834                                 obj->m_static_exists = false;
1835                         } else {
1836                                 infostream<<"Failed to emerge block from which an object to "
1837                                                 <<"be removed was loaded from. id="<<id<<std::endl;
1838                         }
1839                 }
1840
1841                 // If m_known_by_count > 0, don't actually remove. On some future
1842                 // invocation this will be 0, which is when removal will continue.
1843                 if(obj->m_known_by_count > 0)
1844                         continue;
1845
1846                 /*
1847                         Move static data from active to stored if not marked as removed
1848                 */
1849                 if(obj->m_static_exists && !obj->m_removed){
1850                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1851                         if (block) {
1852                                 std::map<u16, StaticObject>::iterator i =
1853                                                 block->m_static_objects.m_active.find(id);
1854                                 if(i != block->m_static_objects.m_active.end()){
1855                                         block->m_static_objects.m_stored.push_back(i->second);
1856                                         block->m_static_objects.m_active.erase(id);
1857                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1858                                                 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1859                                 }
1860                         } else {
1861                                 infostream<<"Failed to emerge block from which an object to "
1862                                                 <<"be deactivated was loaded from. id="<<id<<std::endl;
1863                         }
1864                 }
1865
1866                 // Tell the object about removal
1867                 obj->removingFromEnvironment();
1868                 // Deregister in scripting api
1869                 m_script->removeObjectReference(obj);
1870
1871                 // Delete
1872                 if(obj->environmentDeletes())
1873                         delete obj;
1874
1875                 // Id to be removed from m_active_objects
1876                 objects_to_remove.push_back(id);
1877         }
1878         // Remove references from m_active_objects
1879         for(std::vector<u16>::iterator i = objects_to_remove.begin();
1880                         i != objects_to_remove.end(); ++i) {
1881                 m_active_objects.erase(*i);
1882         }
1883 }
1884
1885 static void print_hexdump(std::ostream &o, const std::string &data)
1886 {
1887         const int linelength = 16;
1888         for(int l=0; ; l++){
1889                 int i0 = linelength * l;
1890                 bool at_end = false;
1891                 int thislinelength = linelength;
1892                 if(i0 + thislinelength > (int)data.size()){
1893                         thislinelength = data.size() - i0;
1894                         at_end = true;
1895                 }
1896                 for(int di=0; di<linelength; di++){
1897                         int i = i0 + di;
1898                         char buf[4];
1899                         if(di<thislinelength)
1900                                 snprintf(buf, 4, "%.2x ", data[i]);
1901                         else
1902                                 snprintf(buf, 4, "   ");
1903                         o<<buf;
1904                 }
1905                 o<<" ";
1906                 for(int di=0; di<thislinelength; di++){
1907                         int i = i0 + di;
1908                         if(data[i] >= 32)
1909                                 o<<data[i];
1910                         else
1911                                 o<<".";
1912                 }
1913                 o<<std::endl;
1914                 if(at_end)
1915                         break;
1916         }
1917 }
1918
1919 /*
1920         Convert stored objects from blocks near the players to active.
1921 */
1922 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1923 {
1924         if(block == NULL)
1925                 return;
1926
1927         // Ignore if no stored objects (to not set changed flag)
1928         if(block->m_static_objects.m_stored.empty())
1929                 return;
1930
1931         verbosestream<<"ServerEnvironment::activateObjects(): "
1932                         <<"activating objects of block "<<PP(block->getPos())
1933                         <<" ("<<block->m_static_objects.m_stored.size()
1934                         <<" objects)"<<std::endl;
1935         bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1936         if (large_amount) {
1937                 errorstream<<"suspiciously large amount of objects detected: "
1938                                 <<block->m_static_objects.m_stored.size()<<" in "
1939                                 <<PP(block->getPos())
1940                                 <<"; removing all of them."<<std::endl;
1941                 // Clear stored list
1942                 block->m_static_objects.m_stored.clear();
1943                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1944                         MOD_REASON_TOO_MANY_OBJECTS);
1945                 return;
1946         }
1947
1948         // Activate stored objects
1949         std::vector<StaticObject> new_stored;
1950         for (std::vector<StaticObject>::iterator
1951                         i = block->m_static_objects.m_stored.begin();
1952                         i != block->m_static_objects.m_stored.end(); ++i) {
1953                 StaticObject &s_obj = *i;
1954
1955                 // Create an active object from the data
1956                 ServerActiveObject *obj = ServerActiveObject::create
1957                                 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1958                 // If couldn't create object, store static data back.
1959                 if(obj == NULL) {
1960                         errorstream<<"ServerEnvironment::activateObjects(): "
1961                                         <<"failed to create active object from static object "
1962                                         <<"in block "<<PP(s_obj.pos/BS)
1963                                         <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1964                         print_hexdump(verbosestream, s_obj.data);
1965
1966                         new_stored.push_back(s_obj);
1967                         continue;
1968                 }
1969                 verbosestream<<"ServerEnvironment::activateObjects(): "
1970                                 <<"activated static object pos="<<PP(s_obj.pos/BS)
1971                                 <<" type="<<(int)s_obj.type<<std::endl;
1972                 // This will also add the object to the active static list
1973                 addActiveObjectRaw(obj, false, dtime_s);
1974         }
1975         // Clear stored list
1976         block->m_static_objects.m_stored.clear();
1977         // Add leftover failed stuff to stored list
1978         for(std::vector<StaticObject>::iterator
1979                         i = new_stored.begin();
1980                         i != new_stored.end(); ++i) {
1981                 StaticObject &s_obj = *i;
1982                 block->m_static_objects.m_stored.push_back(s_obj);
1983         }
1984
1985         // Turn the active counterparts of activated objects not pending for
1986         // deactivation
1987         for(std::map<u16, StaticObject>::iterator
1988                         i = block->m_static_objects.m_active.begin();
1989                         i != block->m_static_objects.m_active.end(); ++i)
1990         {
1991                 u16 id = i->first;
1992                 ServerActiveObject *object = getActiveObject(id);
1993                 assert(object);
1994                 object->m_pending_deactivation = false;
1995         }
1996
1997         /*
1998                 Note: Block hasn't really been modified here.
1999                 The objects have just been activated and moved from the stored
2000                 static list to the active static list.
2001                 As such, the block is essentially the same.
2002                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2003                 Otherwise there would be a huge amount of unnecessary I/O.
2004         */
2005 }
2006
2007 /*
2008         Convert objects that are not standing inside active blocks to static.
2009
2010         If m_known_by_count != 0, active object is not deleted, but static
2011         data is still updated.
2012
2013         If force_delete is set, active object is deleted nevertheless. It
2014         shall only be set so in the destructor of the environment.
2015
2016         If block wasn't generated (not in memory or on disk),
2017 */
2018 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2019 {
2020         std::vector<u16> objects_to_remove;
2021         for(ActiveObjectMap::iterator i = m_active_objects.begin();
2022                         i != m_active_objects.end(); ++i) {
2023                 ServerActiveObject* obj = i->second;
2024                 assert(obj);
2025
2026                 // Do not deactivate if static data creation not allowed
2027                 if(!force_delete && !obj->isStaticAllowed())
2028                         continue;
2029
2030                 // If pending deactivation, let removeRemovedObjects() do it
2031                 if(!force_delete && obj->m_pending_deactivation)
2032                         continue;
2033
2034                 u16 id = i->first;
2035                 v3f objectpos = obj->getBasePosition();
2036
2037                 // The block in which the object resides in
2038                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2039
2040                 // If object's static data is stored in a deactivated block and object
2041                 // is actually located in an active block, re-save to the block in
2042                 // which the object is actually located in.
2043                 if(!force_delete &&
2044                                 obj->m_static_exists &&
2045                                 !m_active_blocks.contains(obj->m_static_block) &&
2046                                  m_active_blocks.contains(blockpos_o))
2047                 {
2048                         v3s16 old_static_block = obj->m_static_block;
2049
2050                         // Save to block where object is located
2051                         MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2052                         if(!block){
2053                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2054                                                 <<"Could not save object id="<<id
2055                                                 <<" to it's current block "<<PP(blockpos_o)
2056                                                 <<std::endl;
2057                                 continue;
2058                         }
2059                         std::string staticdata_new = obj->getStaticData();
2060                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2061                         block->m_static_objects.insert(id, s_obj);
2062                         obj->m_static_block = blockpos_o;
2063                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2064                                 MOD_REASON_STATIC_DATA_ADDED);
2065
2066                         // Delete from block where object was located
2067                         block = m_map->emergeBlock(old_static_block, false);
2068                         if(!block){
2069                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2070                                                 <<"Could not delete object id="<<id
2071                                                 <<" from it's previous block "<<PP(old_static_block)
2072                                                 <<std::endl;
2073                                 continue;
2074                         }
2075                         block->m_static_objects.remove(id);
2076                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2077                                 MOD_REASON_STATIC_DATA_REMOVED);
2078                         continue;
2079                 }
2080
2081                 // If block is active, don't remove
2082                 if(!force_delete && m_active_blocks.contains(blockpos_o))
2083                         continue;
2084
2085                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2086                                 <<"deactivating object id="<<id<<" on inactive block "
2087                                 <<PP(blockpos_o)<<std::endl;
2088
2089                 // If known by some client, don't immediately delete.
2090                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2091
2092                 /*
2093                         Update the static data
2094                 */
2095
2096                 if(obj->isStaticAllowed())
2097                 {
2098                         // Create new static object
2099                         std::string staticdata_new = obj->getStaticData();
2100                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2101
2102                         bool stays_in_same_block = false;
2103                         bool data_changed = true;
2104
2105                         if (obj->m_static_exists) {
2106                                 if (obj->m_static_block == blockpos_o)
2107                                         stays_in_same_block = true;
2108
2109                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2110
2111                                 if (block) {
2112                                         std::map<u16, StaticObject>::iterator n =
2113                                                 block->m_static_objects.m_active.find(id);
2114                                         if (n != block->m_static_objects.m_active.end()) {
2115                                                 StaticObject static_old = n->second;
2116
2117                                                 float save_movem = obj->getMinimumSavedMovement();
2118
2119                                                 if (static_old.data == staticdata_new &&
2120                                                                 (static_old.pos - objectpos).getLength() < save_movem)
2121                                                         data_changed = false;
2122                                         } else {
2123                                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2124                                                         <<"id="<<id<<" m_static_exists=true but "
2125                                                         <<"static data doesn't actually exist in "
2126                                                         <<PP(obj->m_static_block)<<std::endl;
2127                                         }
2128                                 }
2129                         }
2130
2131                         bool shall_be_written = (!stays_in_same_block || data_changed);
2132
2133                         // Delete old static object
2134                         if(obj->m_static_exists)
2135                         {
2136                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2137                                 if(block)
2138                                 {
2139                                         block->m_static_objects.remove(id);
2140                                         obj->m_static_exists = false;
2141                                         // Only mark block as modified if data changed considerably
2142                                         if(shall_be_written)
2143                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2144                                                         MOD_REASON_STATIC_DATA_CHANGED);
2145                                 }
2146                         }
2147
2148                         // Add to the block where the object is located in
2149                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2150                         // Get or generate the block
2151                         MapBlock *block = NULL;
2152                         try{
2153                                 block = m_map->emergeBlock(blockpos);
2154                         } catch(InvalidPositionException &e){
2155                                 // Handled via NULL pointer
2156                                 // NOTE: emergeBlock's failure is usually determined by it
2157                                 //       actually returning NULL
2158                         }
2159
2160                         if(block)
2161                         {
2162                                 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2163                                         errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2164                                                         <<" statically but block "<<PP(blockpos)
2165                                                         <<" already contains "
2166                                                         <<block->m_static_objects.m_stored.size()
2167                                                         <<" objects."
2168                                                         <<" Forcing delete."<<std::endl;
2169                                         force_delete = true;
2170                                 } else {
2171                                         // If static counterpart already exists in target block,
2172                                         // remove it first.
2173                                         // This shouldn't happen because the object is removed from
2174                                         // the previous block before this according to
2175                                         // obj->m_static_block, but happens rarely for some unknown
2176                                         // reason. Unsuccessful attempts have been made to find
2177                                         // said reason.
2178                                         if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2179                                                 warningstream<<"ServerEnv: Performing hack #83274"
2180                                                                 <<std::endl;
2181                                                 block->m_static_objects.remove(id);
2182                                         }
2183                                         // Store static data
2184                                         u16 store_id = pending_delete ? id : 0;
2185                                         block->m_static_objects.insert(store_id, s_obj);
2186
2187                                         // Only mark block as modified if data changed considerably
2188                                         if(shall_be_written)
2189                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2190                                                         MOD_REASON_STATIC_DATA_CHANGED);
2191
2192                                         obj->m_static_exists = true;
2193                                         obj->m_static_block = block->getPos();
2194                                 }
2195                         }
2196                         else{
2197                                 if(!force_delete){
2198                                         v3s16 p = floatToInt(objectpos, BS);
2199                                         errorstream<<"ServerEnv: Could not find or generate "
2200                                                         <<"a block for storing id="<<obj->getId()
2201                                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
2202                                         continue;
2203                                 }
2204                         }
2205                 }
2206
2207                 /*
2208                         If known by some client, set pending deactivation.
2209                         Otherwise delete it immediately.
2210                 */
2211
2212                 if(pending_delete && !force_delete)
2213                 {
2214                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2215                                         <<"object id="<<id<<" is known by clients"
2216                                         <<"; not deleting yet"<<std::endl;
2217
2218                         obj->m_pending_deactivation = true;
2219                         continue;
2220                 }
2221
2222                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2223                                 <<"object id="<<id<<" is not known by clients"
2224                                 <<"; deleting"<<std::endl;
2225
2226                 // Tell the object about removal
2227                 obj->removingFromEnvironment();
2228                 // Deregister in scripting api
2229                 m_script->removeObjectReference(obj);
2230
2231                 // Delete active object
2232                 if(obj->environmentDeletes())
2233                         delete obj;
2234                 // Id to be removed from m_active_objects
2235                 objects_to_remove.push_back(id);
2236         }
2237
2238         // Remove references from m_active_objects
2239         for(std::vector<u16>::iterator i = objects_to_remove.begin();
2240                         i != objects_to_remove.end(); ++i) {
2241                 m_active_objects.erase(*i);
2242         }
2243 }
2244
2245 #ifndef SERVER
2246
2247 #include "clientsimpleobject.h"
2248
2249 /*
2250         ClientEnvironment
2251 */
2252
2253 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2254                 ITextureSource *texturesource, IGameDef *gamedef,
2255                 IrrlichtDevice *irr):
2256         m_map(map),
2257         m_smgr(smgr),
2258         m_texturesource(texturesource),
2259         m_gamedef(gamedef),
2260         m_irr(irr)
2261 {
2262         char zero = 0;
2263         memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2264 }
2265
2266 ClientEnvironment::~ClientEnvironment()
2267 {
2268         // delete active objects
2269         for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2270                         i != m_active_objects.end(); ++i) {
2271                 delete i->second;
2272         }
2273
2274         for(std::vector<ClientSimpleObject*>::iterator
2275                         i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2276                 delete *i;
2277         }
2278
2279         // Drop/delete map
2280         m_map->drop();
2281 }
2282
2283 Map & ClientEnvironment::getMap()
2284 {
2285         return *m_map;
2286 }
2287
2288 ClientMap & ClientEnvironment::getClientMap()
2289 {
2290         return *m_map;
2291 }
2292
2293 LocalPlayer *ClientEnvironment::getPlayer(const u16 peer_id)
2294 {
2295         return dynamic_cast<LocalPlayer *>(Environment::getPlayer(peer_id));
2296 }
2297
2298 LocalPlayer *ClientEnvironment::getPlayer(const char* name)
2299 {
2300         return dynamic_cast<LocalPlayer *>(Environment::getPlayer(name));
2301 }
2302
2303 void ClientEnvironment::addPlayer(Player *player)
2304 {
2305         DSTACK(FUNCTION_NAME);
2306         /*
2307                 It is a failure if player is local and there already is a local
2308                 player
2309         */
2310         FATAL_ERROR_IF(player->isLocal() && 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                 LocalPlayer *player = dynamic_cast<LocalPlayer *>(*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