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