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