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