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