]> git.lizzy.rs Git - minetest.git/blob - src/environment.cpp
438c9ef4f684e89c30e25719727ff23d318aa12c
[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 "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, lua_State *L,
324                 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
325         m_map(map),
326         m_lua(L),
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(scriptapi_node_on_timer(m_lua,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                 scriptapi_node_on_destruct(m_lua, 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                 scriptapi_node_after_destruct(m_lua, p, n_old);
858         // Call constructor
859         if(ndef->get(n).has_on_construct)
860                 scriptapi_node_on_construct(m_lua, 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                 scriptapi_node_on_destruct(m_lua, 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                 scriptapi_node_after_destruct(m_lua, 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                 v3f objectpos = obj->getBasePosition();
914                 // Delete static object if block is loaded
915                 if(obj->m_static_exists){
916                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
917                         if(block){
918                                 block->m_static_objects.remove(id);
919                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
920                                                 "clearAllObjects");
921                                 obj->m_static_exists = false;
922                         }
923                 }
924                 // If known by some client, don't delete immediately
925                 if(obj->m_known_by_count > 0){
926                         obj->m_pending_deactivation = true;
927                         obj->m_removed = true;
928                         continue;
929                 }
930
931                 // Tell the object about removal
932                 obj->removingFromEnvironment();
933                 // Deregister in scripting api
934                 scriptapi_rm_object_reference(m_lua, obj);
935
936                 // Delete active object
937                 if(obj->environmentDeletes())
938                         delete obj;
939                 // Id to be removed from m_active_objects
940                 objects_to_remove.push_back(id);
941         }
942         // Remove references from m_active_objects
943         for(std::list<u16>::iterator i = objects_to_remove.begin();
944                         i != objects_to_remove.end(); ++i)
945         {
946                 m_active_objects.erase(*i);
947         }
948
949         std::list<v3s16> loadable_blocks;
950         infostream<<"ServerEnvironment::clearAllObjects(): "
951                         <<"Listing all loadable blocks"<<std::endl;
952         m_map->listAllLoadableBlocks(loadable_blocks);
953         infostream<<"ServerEnvironment::clearAllObjects(): "
954                         <<"Done listing all loadable blocks: "
955                         <<loadable_blocks.size()
956                         <<", now clearing"<<std::endl;
957         u32 report_interval = loadable_blocks.size() / 10;
958         u32 num_blocks_checked = 0;
959         u32 num_blocks_cleared = 0;
960         u32 num_objs_cleared = 0;
961         for(std::list<v3s16>::iterator i = loadable_blocks.begin();
962                         i != loadable_blocks.end(); ++i)
963         {
964                 v3s16 p = *i;
965                 MapBlock *block = m_map->emergeBlock(p, false);
966                 if(!block){
967                         errorstream<<"ServerEnvironment::clearAllObjects(): "
968                                         <<"Failed to emerge block "<<PP(p)<<std::endl;
969                         continue;
970                 }
971                 u32 num_stored = block->m_static_objects.m_stored.size();
972                 u32 num_active = block->m_static_objects.m_active.size();
973                 if(num_stored != 0 || num_active != 0){
974                         block->m_static_objects.m_stored.clear();
975                         block->m_static_objects.m_active.clear();
976                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
977                                         "clearAllObjects");
978                         num_objs_cleared += num_stored + num_active;
979                         num_blocks_cleared++;
980                 }
981                 num_blocks_checked++;
982
983                 if(num_blocks_checked % report_interval == 0){
984                         float percent = 100.0 * (float)num_blocks_checked /
985                                         loadable_blocks.size();
986                         infostream<<"ServerEnvironment::clearAllObjects(): "
987                                         <<"Cleared "<<num_objs_cleared<<" objects"
988                                         <<" in "<<num_blocks_cleared<<" blocks ("
989                                         <<percent<<"%)"<<std::endl;
990                 }
991         }
992         infostream<<"ServerEnvironment::clearAllObjects(): "
993                         <<"Finished: Cleared "<<num_objs_cleared<<" objects"
994                         <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
995 }
996
997 void ServerEnvironment::step(float dtime)
998 {
999         DSTACK(__FUNCTION_NAME);
1000         
1001         //TimeTaker timer("ServerEnv step");
1002
1003         /* Step time of day */
1004         stepTimeOfDay(dtime);
1005
1006         // Update this one
1007         // NOTE: This is kind of funny on a singleplayer game, but doesn't
1008         // really matter that much.
1009         m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1010
1011         /*
1012                 Increment game time
1013         */
1014         {
1015                 m_game_time_fraction_counter += dtime;
1016                 u32 inc_i = (u32)m_game_time_fraction_counter;
1017                 m_game_time += inc_i;
1018                 m_game_time_fraction_counter -= (float)inc_i;
1019         }
1020         
1021         /*
1022                 Handle players
1023         */
1024         {
1025                 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1026                 for(std::list<Player*>::iterator i = m_players.begin();
1027                                 i != m_players.end(); ++i)
1028                 {
1029                         Player *player = *i;
1030                         
1031                         // Ignore disconnected players
1032                         if(player->peer_id == 0)
1033                                 continue;
1034
1035                         v3f playerpos = player->getPosition();
1036                         
1037                         // Move
1038                         player->move(dtime, *m_map, 100*BS);
1039                 }
1040         }
1041
1042         /*
1043                 Manage active block list
1044         */
1045         if(m_active_blocks_management_interval.step(dtime, 2.0))
1046         {
1047                 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1048                 /*
1049                         Get player block positions
1050                 */
1051                 std::list<v3s16> players_blockpos;
1052                 for(std::list<Player*>::iterator
1053                                 i = m_players.begin();
1054                                 i != m_players.end(); ++i)
1055                 {
1056                         Player *player = *i;
1057                         // Ignore disconnected players
1058                         if(player->peer_id == 0)
1059                                 continue;
1060                         v3s16 blockpos = getNodeBlockPos(
1061                                         floatToInt(player->getPosition(), BS));
1062                         players_blockpos.push_back(blockpos);
1063                 }
1064                 
1065                 /*
1066                         Update list of active blocks, collecting changes
1067                 */
1068                 const s16 active_block_range = g_settings->getS16("active_block_range");
1069                 std::set<v3s16> blocks_removed;
1070                 std::set<v3s16> blocks_added;
1071                 m_active_blocks.update(players_blockpos, active_block_range,
1072                                 blocks_removed, blocks_added);
1073
1074                 /*
1075                         Handle removed blocks
1076                 */
1077
1078                 // Convert active objects that are no more in active blocks to static
1079                 deactivateFarObjects(false);
1080                 
1081                 for(std::set<v3s16>::iterator
1082                                 i = blocks_removed.begin();
1083                                 i != blocks_removed.end(); ++i)
1084                 {
1085                         v3s16 p = *i;
1086
1087                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1088                                         <<") became inactive"<<std::endl;*/
1089                         
1090                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1091                         if(block==NULL)
1092                                 continue;
1093                         
1094                         // Set current time as timestamp (and let it set ChangedFlag)
1095                         block->setTimestamp(m_game_time);
1096                 }
1097
1098                 /*
1099                         Handle added blocks
1100                 */
1101
1102                 for(std::set<v3s16>::iterator
1103                                 i = blocks_added.begin();
1104                                 i != blocks_added.end(); ++i)
1105                 {
1106                         v3s16 p = *i;
1107                         
1108                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1109                                         <<") became active"<<std::endl;*/
1110
1111                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1112                         if(block==NULL){
1113                                 // Block needs to be fetched first
1114                                 m_emerger->queueBlockEmerge(p, false);
1115                                 m_active_blocks.m_list.erase(p);
1116                                 continue;
1117                         }
1118
1119                         activateBlock(block);
1120                 }
1121         }
1122
1123         /*
1124                 Mess around in active blocks
1125         */
1126         if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1127         {
1128                 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1129                 
1130                 float dtime = 1.0;
1131
1132                 for(std::set<v3s16>::iterator
1133                                 i = m_active_blocks.m_list.begin();
1134                                 i != m_active_blocks.m_list.end(); ++i)
1135                 {
1136                         v3s16 p = *i;
1137                         
1138                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1139                                         <<") being handled"<<std::endl;*/
1140
1141                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1142                         if(block==NULL)
1143                                 continue;
1144
1145                         // Reset block usage timer
1146                         block->resetUsageTimer();
1147                         
1148                         // Set current time as timestamp
1149                         block->setTimestampNoChangedFlag(m_game_time);
1150                         // If time has changed much from the one on disk,
1151                         // set block to be saved when it is unloaded
1152                         if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1153                                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1154                                                 "Timestamp older than 60s (step)");
1155
1156                         // Run node timers
1157                         std::map<v3s16, NodeTimer> elapsed_timers =
1158                                 block->m_node_timers.step((float)dtime);
1159                         if(!elapsed_timers.empty()){
1160                                 MapNode n;
1161                                 for(std::map<v3s16, NodeTimer>::iterator
1162                                                 i = elapsed_timers.begin();
1163                                                 i != elapsed_timers.end(); i++){
1164                                         n = block->getNodeNoEx(i->first);
1165                                         p = i->first + block->getPosRelative();
1166                                         if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
1167                                                 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1168                                 }
1169                         }
1170                 }
1171         }
1172         
1173         const float abm_interval = 1.0;
1174         if(m_active_block_modifier_interval.step(dtime, abm_interval))
1175         do{ // breakable
1176                 if(m_active_block_interval_overload_skip > 0){
1177                         ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1178                         m_active_block_interval_overload_skip--;
1179                         break;
1180                 }
1181                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1182                 TimeTaker timer("modify in active blocks");
1183                 
1184                 // Initialize handling of ActiveBlockModifiers
1185                 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1186
1187                 for(std::set<v3s16>::iterator
1188                                 i = m_active_blocks.m_list.begin();
1189                                 i != m_active_blocks.m_list.end(); ++i)
1190                 {
1191                         v3s16 p = *i;
1192                         
1193                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1194                                         <<") being handled"<<std::endl;*/
1195
1196                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1197                         if(block==NULL)
1198                                 continue;
1199                         
1200                         // Set current time as timestamp
1201                         block->setTimestampNoChangedFlag(m_game_time);
1202
1203                         /* Handle ActiveBlockModifiers */
1204                         abmhandler.apply(block);
1205                 }
1206
1207                 u32 time_ms = timer.stop(true);
1208                 u32 max_time_ms = 200;
1209                 if(time_ms > max_time_ms){
1210                         infostream<<"WARNING: active block modifiers took "
1211                                         <<time_ms<<"ms (longer than "
1212                                         <<max_time_ms<<"ms)"<<std::endl;
1213                         m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1214                 }
1215         }while(0);
1216         
1217         /*
1218                 Step script environment (run global on_step())
1219         */
1220         scriptapi_environment_step(m_lua, dtime);
1221
1222         /*
1223                 Step active objects
1224         */
1225         {
1226                 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1227                 //TimeTaker timer("Step active objects");
1228
1229                 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1230                 
1231                 // This helps the objects to send data at the same time
1232                 bool send_recommended = false;
1233                 m_send_recommended_timer += dtime;
1234                 if(m_send_recommended_timer > getSendRecommendedInterval())
1235                 {
1236                         m_send_recommended_timer -= getSendRecommendedInterval();
1237                         send_recommended = true;
1238                 }
1239
1240                 for(std::map<u16, ServerActiveObject*>::iterator
1241                                 i = m_active_objects.begin();
1242                                 i != m_active_objects.end(); ++i)
1243                 {
1244                         ServerActiveObject* obj = i->second;
1245                         // Remove non-peaceful mobs on peaceful mode
1246                         if(g_settings->getBool("only_peaceful_mobs")){
1247                                 if(!obj->isPeaceful())
1248                                         obj->m_removed = true;
1249                         }
1250                         // Don't step if is to be removed or stored statically
1251                         if(obj->m_removed || obj->m_pending_deactivation)
1252                                 continue;
1253                         // Step object
1254                         obj->step(dtime, send_recommended);
1255                         // Read messages from object
1256                         while(!obj->m_messages_out.empty())
1257                         {
1258                                 m_active_object_messages.push_back(
1259                                                 obj->m_messages_out.pop_front());
1260                         }
1261                 }
1262         }
1263         
1264         /*
1265                 Manage active objects
1266         */
1267         if(m_object_management_interval.step(dtime, 0.5))
1268         {
1269                 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1270                 /*
1271                         Remove objects that satisfy (m_removed && m_known_by_count==0)
1272                 */
1273                 removeRemovedObjects();
1274         }
1275 }
1276
1277 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1278 {
1279         std::map<u16, ServerActiveObject*>::iterator n;
1280         n = m_active_objects.find(id);
1281         if(n == m_active_objects.end())
1282                 return NULL;
1283         return n->second;
1284 }
1285
1286 bool isFreeServerActiveObjectId(u16 id,
1287                 std::map<u16, ServerActiveObject*> &objects)
1288 {
1289         if(id == 0)
1290                 return false;
1291
1292         return objects.find(id) == objects.end();
1293 }
1294
1295 u16 getFreeServerActiveObjectId(
1296                 std::map<u16, ServerActiveObject*> &objects)
1297 {
1298         //try to reuse id's as late as possible
1299         static u16 last_used_id = 0;
1300         u16 startid = last_used_id;
1301         for(;;)
1302         {
1303                 last_used_id ++;
1304                 if(isFreeServerActiveObjectId(last_used_id, objects))
1305                         return last_used_id;
1306                 
1307                 if(last_used_id == startid)
1308                         return 0;
1309         }
1310 }
1311
1312 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1313 {
1314         assert(object);
1315         u16 id = addActiveObjectRaw(object, true, 0);
1316         return id;
1317 }
1318
1319 #if 0
1320 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1321 {
1322         assert(obj);
1323
1324         v3f objectpos = obj->getBasePosition(); 
1325
1326         // The block in which the object resides in
1327         v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1328
1329         /*
1330                 Update the static data
1331         */
1332
1333         // Create new static object
1334         std::string staticdata = obj->getStaticData();
1335         StaticObject s_obj(obj->getType(), objectpos, staticdata);
1336         // Add to the block where the object is located in
1337         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1338         // Get or generate the block
1339         MapBlock *block = m_map->emergeBlock(blockpos);
1340
1341         bool succeeded = false;
1342
1343         if(block)
1344         {
1345                 block->m_static_objects.insert(0, s_obj);
1346                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1347                                 "addActiveObjectAsStatic");
1348                 succeeded = true;
1349         }
1350         else{
1351                 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1352                                 <<"Could not find or generate "
1353                                 <<"a block for storing static object"<<std::endl;
1354                 succeeded = false;
1355         }
1356
1357         if(obj->environmentDeletes())
1358                 delete obj;
1359
1360         return succeeded;
1361 }
1362 #endif
1363
1364 /*
1365         Finds out what new objects have been added to
1366         inside a radius around a position
1367 */
1368 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1369                 std::set<u16> &current_objects,
1370                 std::set<u16> &added_objects)
1371 {
1372         v3f pos_f = intToFloat(pos, BS);
1373         f32 radius_f = radius * BS;
1374         /*
1375                 Go through the object list,
1376                 - discard m_removed objects,
1377                 - discard objects that are too far away,
1378                 - discard objects that are found in current_objects.
1379                 - add remaining objects to added_objects
1380         */
1381         for(std::map<u16, ServerActiveObject*>::iterator
1382                         i = m_active_objects.begin();
1383                         i != m_active_objects.end(); ++i)
1384         {
1385                 u16 id = i->first;
1386                 // Get object
1387                 ServerActiveObject *object = i->second;
1388                 if(object == NULL)
1389                         continue;
1390                 // Discard if removed
1391                 if(object->m_removed)
1392                         continue;
1393                 if(object->unlimitedTransferDistance() == false){
1394                         // Discard if too far
1395                         f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1396                         if(distance_f > radius_f)
1397                                 continue;
1398                 }
1399                 // Discard if already on current_objects
1400                 std::set<u16>::iterator n;
1401                 n = current_objects.find(id);
1402                 if(n != current_objects.end())
1403                         continue;
1404                 // Add to added_objects
1405                 added_objects.insert(id);
1406         }
1407 }
1408
1409 /*
1410         Finds out what objects have been removed from
1411         inside a radius around a position
1412 */
1413 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1414                 std::set<u16> &current_objects,
1415                 std::set<u16> &removed_objects)
1416 {
1417         v3f pos_f = intToFloat(pos, BS);
1418         f32 radius_f = radius * BS;
1419         /*
1420                 Go through current_objects; object is removed if:
1421                 - object is not found in m_active_objects (this is actually an
1422                   error condition; objects should be set m_removed=true and removed
1423                   only after all clients have been informed about removal), or
1424                 - object has m_removed=true, or
1425                 - object is too far away
1426         */
1427         for(std::set<u16>::iterator
1428                         i = current_objects.begin();
1429                         i != current_objects.end(); ++i)
1430         {
1431                 u16 id = *i;
1432                 ServerActiveObject *object = getActiveObject(id);
1433
1434                 if(object == NULL){
1435                         infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1436                                         <<" object in current_objects is NULL"<<std::endl;
1437                         removed_objects.insert(id);
1438                         continue;
1439                 }
1440
1441                 if(object->m_removed)
1442                 {
1443                         removed_objects.insert(id);
1444                         continue;
1445                 }
1446                 
1447                 // If transfer distance is unlimited, don't remove
1448                 if(object->unlimitedTransferDistance())
1449                         continue;
1450
1451                 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1452
1453                 if(distance_f >= radius_f)
1454                 {
1455                         removed_objects.insert(id);
1456                         continue;
1457                 }
1458                 
1459                 // Not removed
1460         }
1461 }
1462
1463 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1464 {
1465         if(m_active_object_messages.empty())
1466                 return ActiveObjectMessage(0);
1467         
1468         return m_active_object_messages.pop_front();
1469 }
1470
1471 /*
1472         ************ Private methods *************
1473 */
1474
1475 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1476                 bool set_changed, u32 dtime_s)
1477 {
1478         assert(object);
1479         if(object->getId() == 0){
1480                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1481                 if(new_id == 0)
1482                 {
1483                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1484                                         <<"no free ids available"<<std::endl;
1485                         if(object->environmentDeletes())
1486                                 delete object;
1487                         return 0;
1488                 }
1489                 object->setId(new_id);
1490         }
1491         else{
1492                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1493                                 <<"supplied with id "<<object->getId()<<std::endl;
1494         }
1495         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1496         {
1497                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1498                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1499                 if(object->environmentDeletes())
1500                         delete object;
1501                 return 0;
1502         }
1503         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1504                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1505                         
1506         m_active_objects[object->getId()] = object;
1507   
1508         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1509                         <<"Added id="<<object->getId()<<"; there are now "
1510                         <<m_active_objects.size()<<" active objects."
1511                         <<std::endl;
1512         
1513         // Register reference in scripting api (must be done before post-init)
1514         scriptapi_add_object_reference(m_lua, object);
1515         // Post-initialize object
1516         object->addedToEnvironment(dtime_s);
1517         
1518         // Add static data to block
1519         if(object->isStaticAllowed())
1520         {
1521                 // Add static object to active static list of the block
1522                 v3f objectpos = object->getBasePosition();
1523                 std::string staticdata = object->getStaticData();
1524                 StaticObject s_obj(object->getType(), objectpos, staticdata);
1525                 // Add to the block where the object is located in
1526                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1527                 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1528                 if(block)
1529                 {
1530                         block->m_static_objects.m_active[object->getId()] = s_obj;
1531                         object->m_static_exists = true;
1532                         object->m_static_block = blockpos;
1533
1534                         if(set_changed)
1535                                 block->raiseModified(MOD_STATE_WRITE_NEEDED, 
1536                                                 "addActiveObjectRaw");
1537                 }
1538                 else{
1539                         v3s16 p = floatToInt(objectpos, BS);
1540                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1541                                         <<"could not find block for storing id="<<object->getId()
1542                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
1543                 }
1544         }
1545         
1546         return object->getId();
1547 }
1548
1549 /*
1550         Remove objects that satisfy (m_removed && m_known_by_count==0)
1551 */
1552 void ServerEnvironment::removeRemovedObjects()
1553 {
1554         std::list<u16> objects_to_remove;
1555         for(std::map<u16, ServerActiveObject*>::iterator
1556                         i = m_active_objects.begin();
1557                         i != m_active_objects.end(); ++i)
1558         {
1559                 u16 id = i->first;
1560                 ServerActiveObject* obj = i->second;
1561                 // This shouldn't happen but check it
1562                 if(obj == NULL)
1563                 {
1564                         infostream<<"NULL object found in ServerEnvironment"
1565                                         <<" while finding removed objects. id="<<id<<std::endl;
1566                         // Id to be removed from m_active_objects
1567                         objects_to_remove.push_back(id);
1568                         continue;
1569                 }
1570
1571                 /*
1572                         We will delete objects that are marked as removed or thatare
1573                         waiting for deletion after deactivation
1574                 */
1575                 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1576                         continue;
1577
1578                 /*
1579                         Delete static data from block if is marked as removed
1580                 */
1581                 if(obj->m_static_exists && obj->m_removed)
1582                 {
1583                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1584                         if (block) {
1585                                 block->m_static_objects.remove(id);
1586                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1587                                                 "removeRemovedObjects");
1588                                 obj->m_static_exists = false;
1589                         } else {
1590                                 infostream << "failed to emerge block from which "
1591                                         "an object to be removed was loaded from. id="<<id<<std::endl;
1592                         }
1593                 }
1594
1595                 // If m_known_by_count > 0, don't actually remove.
1596                 if(obj->m_known_by_count > 0)
1597                         continue;
1598                 
1599                 // Tell the object about removal
1600                 obj->removingFromEnvironment();
1601                 // Deregister in scripting api
1602                 scriptapi_rm_object_reference(m_lua, obj);
1603
1604                 // Delete
1605                 if(obj->environmentDeletes())
1606                         delete obj;
1607                 // Id to be removed from m_active_objects
1608                 objects_to_remove.push_back(id);
1609         }
1610         // Remove references from m_active_objects
1611         for(std::list<u16>::iterator i = objects_to_remove.begin();
1612                         i != objects_to_remove.end(); ++i)
1613         {
1614                 m_active_objects.erase(*i);
1615         }
1616 }
1617
1618 static void print_hexdump(std::ostream &o, const std::string &data)
1619 {
1620         const int linelength = 16;
1621         for(int l=0; ; l++){
1622                 int i0 = linelength * l;
1623                 bool at_end = false;
1624                 int thislinelength = linelength;
1625                 if(i0 + thislinelength > (int)data.size()){
1626                         thislinelength = data.size() - i0;
1627                         at_end = true;
1628                 }
1629                 for(int di=0; di<linelength; di++){
1630                         int i = i0 + di;
1631                         char buf[4];
1632                         if(di<thislinelength)
1633                                 snprintf(buf, 4, "%.2x ", data[i]);
1634                         else
1635                                 snprintf(buf, 4, "   ");
1636                         o<<buf;
1637                 }
1638                 o<<" ";
1639                 for(int di=0; di<thislinelength; di++){
1640                         int i = i0 + di;
1641                         if(data[i] >= 32)
1642                                 o<<data[i];
1643                         else
1644                                 o<<".";
1645                 }
1646                 o<<std::endl;
1647                 if(at_end)
1648                         break;
1649         }
1650 }
1651
1652 /*
1653         Convert stored objects from blocks near the players to active.
1654 */
1655 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1656 {
1657         if(block==NULL)
1658                 return;
1659         // Ignore if no stored objects (to not set changed flag)
1660         if(block->m_static_objects.m_stored.size() == 0)
1661                 return;
1662         verbosestream<<"ServerEnvironment::activateObjects(): "
1663                         <<"activating objects of block "<<PP(block->getPos())
1664                         <<" ("<<block->m_static_objects.m_stored.size()
1665                         <<" objects)"<<std::endl;
1666         bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1667         if(large_amount){
1668                 errorstream<<"suspiciously large amount of objects detected: "
1669                                 <<block->m_static_objects.m_stored.size()<<" in "
1670                                 <<PP(block->getPos())
1671                                 <<"; removing all of them."<<std::endl;
1672                 // Clear stored list
1673                 block->m_static_objects.m_stored.clear();
1674                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1675                                 "stored list cleared in activateObjects due to "
1676                                 "large amount of objects");
1677                 return;
1678         }
1679         // A list for objects that couldn't be converted to active for some
1680         // reason. They will be stored back.
1681         std::list<StaticObject> new_stored;
1682         // Loop through stored static objects
1683         for(std::list<StaticObject>::iterator
1684                         i = block->m_static_objects.m_stored.begin();
1685                         i != block->m_static_objects.m_stored.end(); ++i)
1686         {
1687                 /*infostream<<"Server: Creating an active object from "
1688                                 <<"static data"<<std::endl;*/
1689                 StaticObject &s_obj = *i;
1690                 // Create an active object from the data
1691                 ServerActiveObject *obj = ServerActiveObject::create
1692                                 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1693                 // If couldn't create object, store static data back.
1694                 if(obj==NULL)
1695                 {
1696                         errorstream<<"ServerEnvironment::activateObjects(): "
1697                                         <<"failed to create active object from static object "
1698                                         <<"in block "<<PP(s_obj.pos/BS)
1699                                         <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1700                         print_hexdump(verbosestream, s_obj.data);
1701                         
1702                         new_stored.push_back(s_obj);
1703                         continue;
1704                 }
1705                 verbosestream<<"ServerEnvironment::activateObjects(): "
1706                                 <<"activated static object pos="<<PP(s_obj.pos/BS)
1707                                 <<" type="<<(int)s_obj.type<<std::endl;
1708                 // This will also add the object to the active static list
1709                 addActiveObjectRaw(obj, false, dtime_s);
1710         }
1711         // Clear stored list
1712         block->m_static_objects.m_stored.clear();
1713         // Add leftover failed stuff to stored list
1714         for(std::list<StaticObject>::iterator
1715                         i = new_stored.begin();
1716                         i != new_stored.end(); ++i)
1717         {
1718                 StaticObject &s_obj = *i;
1719                 block->m_static_objects.m_stored.push_back(s_obj);
1720         }
1721         /*
1722                 Note: Block hasn't really been modified here.
1723                 The objects have just been activated and moved from the stored
1724                 static list to the active static list.
1725                 As such, the block is essentially the same.
1726                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1727                 Otherwise there would be a huge amount of unnecessary I/O.
1728         */
1729 }
1730
1731 /*
1732         Convert objects that are not standing inside active blocks to static.
1733
1734         If m_known_by_count != 0, active object is not deleted, but static
1735         data is still updated.
1736
1737         If force_delete is set, active object is deleted nevertheless. It
1738         shall only be set so in the destructor of the environment.
1739
1740         If block wasn't generated (not in memory or on disk), 
1741 */
1742 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1743 {
1744         std::list<u16> objects_to_remove;
1745         for(std::map<u16, ServerActiveObject*>::iterator
1746                         i = m_active_objects.begin();
1747                         i != m_active_objects.end(); ++i)
1748         {
1749                 ServerActiveObject* obj = i->second;
1750                 assert(obj);
1751                 
1752                 // Do not deactivate if static data creation not allowed
1753                 if(!force_delete && !obj->isStaticAllowed())
1754                         continue;
1755
1756                 // If pending deactivation, let removeRemovedObjects() do it
1757                 if(!force_delete && obj->m_pending_deactivation)
1758                         continue;
1759
1760                 u16 id = i->first;
1761                 v3f objectpos = obj->getBasePosition(); 
1762
1763                 // The block in which the object resides in
1764                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1765
1766                 // If block is active, don't remove
1767                 if(!force_delete && m_active_blocks.contains(blockpos_o))
1768                         continue;
1769
1770                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1771                                 <<"deactivating object id="<<id<<" on inactive block "
1772                                 <<PP(blockpos_o)<<std::endl;
1773
1774                 // If known by some client, don't immediately delete.
1775                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1776
1777                 /*
1778                         Update the static data
1779                 */
1780
1781                 if(obj->isStaticAllowed())
1782                 {
1783                         // Create new static object
1784                         std::string staticdata_new = obj->getStaticData();
1785                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1786                         
1787                         bool stays_in_same_block = false;
1788                         bool data_changed = true;
1789
1790                         if(obj->m_static_exists){
1791                                 if(obj->m_static_block == blockpos_o)
1792                                         stays_in_same_block = true;
1793
1794                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1795                                 
1796                                 std::map<u16, StaticObject>::iterator n =
1797                                                 block->m_static_objects.m_active.find(id);
1798                                 if(n != block->m_static_objects.m_active.end()){
1799                                         StaticObject static_old = n->second;
1800
1801                                         float save_movem = obj->getMinimumSavedMovement();
1802
1803                                         if(static_old.data == staticdata_new &&
1804                                                         (static_old.pos - objectpos).getLength() < save_movem)
1805                                                 data_changed = false;
1806                                 } else {
1807                                         errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1808                                                         <<"id="<<id<<" m_static_exists=true but "
1809                                                         <<"static data doesn't actually exist in "
1810                                                         <<PP(obj->m_static_block)<<std::endl;
1811                                 }
1812                         }
1813
1814                         bool shall_be_written = (!stays_in_same_block || data_changed);
1815                         
1816                         // Delete old static object
1817                         if(obj->m_static_exists)
1818                         {
1819                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1820                                 if(block)
1821                                 {
1822                                         block->m_static_objects.remove(id);
1823                                         obj->m_static_exists = false;
1824                                         // Only mark block as modified if data changed considerably
1825                                         if(shall_be_written)
1826                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1827                                                                 "deactivateFarObjects: Static data "
1828                                                                 "changed considerably");
1829                                 }
1830                         }
1831
1832                         // Add to the block where the object is located in
1833                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1834                         // Get or generate the block
1835                         MapBlock *block = NULL;
1836                         try{
1837                                 block = m_map->emergeBlock(blockpos);
1838                         } catch(InvalidPositionException &e){
1839                                 // Handled via NULL pointer
1840                         }
1841
1842                         if(block)
1843                         {
1844                                 if(block->m_static_objects.m_stored.size() >= 49){
1845                                         errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1846                                                         <<" statically but block "<<PP(blockpos)
1847                                                         <<" already contains "
1848                                                         <<block->m_static_objects.m_stored.size()
1849                                                         <<" (over 49) objects."
1850                                                         <<" Forcing delete."<<std::endl;
1851                                         force_delete = true;
1852                                 } else {
1853                                         u16 new_id = pending_delete ? id : 0;
1854                                         // If static counterpart already exists, remove it first.
1855                                         // This shouldn't happen, but happens rarely for some
1856                                         // unknown reason. Unsuccessful attempts have been made to
1857                                         // find said reason.
1858                                         if(new_id && block->m_static_objects.m_active.find(new_id) != block->m_static_objects.m_active.end()){
1859                                                 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1860                                                                 <<std::endl;
1861                                                 block->m_static_objects.remove(new_id);
1862                                         }
1863                                         block->m_static_objects.insert(new_id, s_obj);
1864                                         
1865                                         // Only mark block as modified if data changed considerably
1866                                         if(shall_be_written)
1867                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1868                                                                 "deactivateFarObjects: Static data "
1869                                                                 "changed considerably");
1870                                         
1871                                         obj->m_static_exists = true;
1872                                         obj->m_static_block = block->getPos();
1873                                 }
1874                         }
1875                         else{
1876                                 if(!force_delete){
1877                                         v3s16 p = floatToInt(objectpos, BS);
1878                                         errorstream<<"ServerEnv: Could not find or generate "
1879                                                         <<"a block for storing id="<<obj->getId()
1880                                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
1881                                         continue;
1882                                 }
1883                         }
1884                 }
1885
1886                 /*
1887                         If known by some client, set pending deactivation.
1888                         Otherwise delete it immediately.
1889                 */
1890
1891                 if(pending_delete && !force_delete)
1892                 {
1893                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1894                                         <<"object id="<<id<<" is known by clients"
1895                                         <<"; not deleting yet"<<std::endl;
1896
1897                         obj->m_pending_deactivation = true;
1898                         continue;
1899                 }
1900                 
1901                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1902                                 <<"object id="<<id<<" is not known by clients"
1903                                 <<"; deleting"<<std::endl;
1904
1905                 // Tell the object about removal
1906                 obj->removingFromEnvironment();
1907                 // Deregister in scripting api
1908                 scriptapi_rm_object_reference(m_lua, obj);
1909
1910                 // Delete active object
1911                 if(obj->environmentDeletes())
1912                         delete obj;
1913                 // Id to be removed from m_active_objects
1914                 objects_to_remove.push_back(id);
1915         }
1916
1917         // Remove references from m_active_objects
1918         for(std::list<u16>::iterator i = objects_to_remove.begin();
1919                         i != objects_to_remove.end(); ++i)
1920         {
1921                 m_active_objects.erase(*i);
1922         }
1923 }
1924
1925
1926 #ifndef SERVER
1927
1928 #include "clientsimpleobject.h"
1929
1930 /*
1931         ClientEnvironment
1932 */
1933
1934 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1935                 ITextureSource *texturesource, IGameDef *gamedef,
1936                 IrrlichtDevice *irr):
1937         m_map(map),
1938         m_smgr(smgr),
1939         m_texturesource(texturesource),
1940         m_gamedef(gamedef),
1941         m_irr(irr)
1942 {
1943 }
1944
1945 ClientEnvironment::~ClientEnvironment()
1946 {
1947         // delete active objects
1948         for(std::map<u16, ClientActiveObject*>::iterator
1949                         i = m_active_objects.begin();
1950                         i != m_active_objects.end(); ++i)
1951         {
1952                 delete i->second;
1953         }
1954
1955         for(std::list<ClientSimpleObject*>::iterator
1956                         i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
1957         {
1958                 delete *i;
1959         }
1960
1961         // Drop/delete map
1962         m_map->drop();
1963 }
1964
1965 Map & ClientEnvironment::getMap()
1966 {
1967         return *m_map;
1968 }
1969
1970 ClientMap & ClientEnvironment::getClientMap()
1971 {
1972         return *m_map;
1973 }
1974
1975 void ClientEnvironment::addPlayer(Player *player)
1976 {
1977         DSTACK(__FUNCTION_NAME);
1978         /*
1979                 It is a failure if player is local and there already is a local
1980                 player
1981         */
1982         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1983
1984         Environment::addPlayer(player);
1985 }
1986
1987 LocalPlayer * ClientEnvironment::getLocalPlayer()
1988 {
1989         for(std::list<Player*>::iterator i = m_players.begin();
1990                         i != m_players.end(); ++i)
1991         {
1992                 Player *player = *i;
1993                 if(player->isLocal())
1994                         return (LocalPlayer*)player;
1995         }
1996         return NULL;
1997 }
1998
1999 void ClientEnvironment::step(float dtime)
2000 {
2001         DSTACK(__FUNCTION_NAME);
2002
2003         /* Step time of day */
2004         stepTimeOfDay(dtime);
2005
2006         // Get some settings
2007         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2008         bool free_move = fly_allowed && g_settings->getBool("free_move");
2009
2010         // Get local player
2011         LocalPlayer *lplayer = getLocalPlayer();
2012         assert(lplayer);
2013         // collision info queue
2014         std::list<CollisionInfo> player_collisions;
2015         
2016         /*
2017                 Get the speed the player is going
2018         */
2019         bool is_climbing = lplayer->is_climbing;
2020         
2021         f32 player_speed = lplayer->getSpeed().getLength();
2022         
2023         /*
2024                 Maximum position increment
2025         */
2026         //f32 position_max_increment = 0.05*BS;
2027         f32 position_max_increment = 0.1*BS;
2028
2029         // Maximum time increment (for collision detection etc)
2030         // time = distance / speed
2031         f32 dtime_max_increment = 1;
2032         if(player_speed > 0.001)
2033                 dtime_max_increment = position_max_increment / player_speed;
2034         
2035         // Maximum time increment is 10ms or lower
2036         if(dtime_max_increment > 0.01)
2037                 dtime_max_increment = 0.01;
2038         
2039         // Don't allow overly huge dtime
2040         if(dtime > 0.5)
2041                 dtime = 0.5;
2042         
2043         f32 dtime_downcount = dtime;
2044
2045         /*
2046                 Stuff that has a maximum time increment
2047         */
2048
2049         u32 loopcount = 0;
2050         do
2051         {
2052                 loopcount++;
2053
2054                 f32 dtime_part;
2055                 if(dtime_downcount > dtime_max_increment)
2056                 {
2057                         dtime_part = dtime_max_increment;
2058                         dtime_downcount -= dtime_part;
2059                 }
2060                 else
2061                 {
2062                         dtime_part = dtime_downcount;
2063                         /*
2064                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
2065                                 when dtime_part is so small that dtime_downcount -= dtime_part
2066                                 does nothing
2067                         */
2068                         dtime_downcount = 0;
2069                 }
2070                 
2071                 /*
2072                         Handle local player
2073                 */
2074                 
2075                 {
2076                         v3f lplayerpos = lplayer->getPosition();
2077                         
2078                         // Apply physics
2079                         if(free_move == false && is_climbing == false)
2080                         {
2081                                 // Gravity
2082                                 v3f speed = lplayer->getSpeed();
2083                                 if(lplayer->in_liquid == false)
2084                                         speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2085
2086                                 // Liquid floating / sinking
2087                                 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2088                                         speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2089
2090                                 // Liquid resistance
2091                                 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2092                                 {
2093                                         // How much the node's viscosity blocks movement, ranges between 0 and 1
2094                                         // Should match the scale at which viscosity increase affects other liquid attributes
2095                                         const f32 viscosity_factor = 0.3;
2096
2097                                         v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2098                                         f32 dl = d_wanted.getLength();
2099                                         if(dl > lplayer->movement_liquid_fluidity_smooth)
2100                                                 dl = lplayer->movement_liquid_fluidity_smooth;
2101                                         dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2102                                         
2103                                         v3f d = d_wanted.normalize() * dl;
2104                                         speed += d;
2105                                         
2106 #if 0 // old code
2107                                         if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth)      speed.X -= lplayer->movement_liquid_fluidity_smooth;
2108                                         if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth)     speed.X += lplayer->movement_liquid_fluidity_smooth;
2109                                         if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth)      speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2110                                         if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth)     speed.Y += lplayer->movement_liquid_fluidity_smooth;
2111                                         if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth)      speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2112                                         if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth)     speed.Z += lplayer->movement_liquid_fluidity_smooth;
2113 #endif
2114                                 }
2115
2116                                 lplayer->setSpeed(speed);
2117                         }
2118
2119                         /*
2120                                 Move the lplayer.
2121                                 This also does collision detection.
2122                         */
2123                         lplayer->move(dtime_part, this, position_max_increment,
2124                                         &player_collisions);
2125                 }
2126         }
2127         while(dtime_downcount > 0.001);
2128                 
2129         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2130         
2131         for(std::list<CollisionInfo>::iterator
2132                         i = player_collisions.begin();
2133                         i != player_collisions.end(); ++i)
2134         {
2135                 CollisionInfo &info = *i;
2136                 v3f speed_diff = info.new_speed - info.old_speed;;
2137                 // Handle only fall damage
2138                 // (because otherwise walking against something in fast_move kills you)
2139                 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2140                         continue;
2141                 // Get rid of other components
2142                 speed_diff.X = 0;
2143                 speed_diff.Z = 0;
2144                 f32 pre_factor = 1; // 1 hp per node/s
2145                 f32 tolerance = BS*14; // 5 without damage
2146                 f32 post_factor = 1; // 1 hp per node/s
2147                 if(info.type == COLLISION_NODE)
2148                 {
2149                         const ContentFeatures &f = m_gamedef->ndef()->
2150                                         get(m_map->getNodeNoEx(info.node_p));
2151                         // Determine fall damage multiplier
2152                         int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2153                         pre_factor = 1.0 + (float)addp/100.0;
2154                 }
2155                 float speed = pre_factor * speed_diff.getLength();
2156                 if(speed > tolerance)
2157                 {
2158                         f32 damage_f = (speed - tolerance)/BS * post_factor;
2159                         u16 damage = (u16)(damage_f+0.5);
2160                         if(damage != 0)
2161                                 damageLocalPlayer(damage, true);
2162                 }
2163         }
2164         
2165         /*
2166                 A quick draft of lava damage
2167         */
2168         if(m_lava_hurt_interval.step(dtime, 1.0))
2169         {
2170                 v3f pf = lplayer->getPosition();
2171                 
2172                 // Feet, middle and head
2173                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2174                 MapNode n1 = m_map->getNodeNoEx(p1);
2175                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2176                 MapNode n2 = m_map->getNodeNoEx(p2);
2177                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2178                 MapNode n3 = m_map->getNodeNoEx(p2);
2179
2180                 u32 damage_per_second = 0;
2181                 damage_per_second = MYMAX(damage_per_second,
2182                                 m_gamedef->ndef()->get(n1).damage_per_second);
2183                 damage_per_second = MYMAX(damage_per_second,
2184                                 m_gamedef->ndef()->get(n2).damage_per_second);
2185                 damage_per_second = MYMAX(damage_per_second,
2186                                 m_gamedef->ndef()->get(n3).damage_per_second);
2187                 
2188                 if(damage_per_second != 0)
2189                 {
2190                         damageLocalPlayer(damage_per_second, true);
2191                 }
2192         }
2193         
2194         /*
2195                 Stuff that can be done in an arbitarily large dtime
2196         */
2197         for(std::list<Player*>::iterator i = m_players.begin();
2198                         i != m_players.end(); ++i)
2199         {
2200                 Player *player = *i;
2201                 v3f playerpos = player->getPosition();
2202                 
2203                 /*
2204                         Handle non-local players
2205                 */
2206                 if(player->isLocal() == false)
2207                 {
2208                         // Move
2209                         player->move(dtime, *m_map, 100*BS);
2210
2211                 }
2212                 
2213                 // Update lighting on all players on client
2214                 float light = 1.0;
2215                 try{
2216                         // Get node at head
2217                         v3s16 p = player->getLightPosition();
2218                         MapNode n = m_map->getNode(p);
2219                         light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2220                 }
2221                 catch(InvalidPositionException &e){
2222                         light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2223                 }
2224                 player->light = light;
2225         }
2226         
2227         /*
2228                 Step active objects and update lighting of them
2229         */
2230         
2231         bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2232         for(std::map<u16, ClientActiveObject*>::iterator
2233                         i = m_active_objects.begin();
2234                         i != m_active_objects.end(); ++i)
2235         {
2236                 ClientActiveObject* obj = i->second;
2237                 // Step object
2238                 obj->step(dtime, this);
2239
2240                 if(update_lighting)
2241                 {
2242                         // Update lighting
2243                         u8 light = 0;
2244                         try{
2245                                 // Get node at head
2246                                 v3s16 p = obj->getLightPosition();
2247                                 MapNode n = m_map->getNode(p);
2248                                 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2249                         }
2250                         catch(InvalidPositionException &e){
2251                                 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2252                         }
2253                         obj->updateLight(light);
2254                 }
2255         }
2256
2257         /*
2258                 Step and handle simple objects
2259         */
2260         for(std::list<ClientSimpleObject*>::iterator
2261                         i = m_simple_objects.begin(); i != m_simple_objects.end();)
2262         {
2263                 ClientSimpleObject *simple = *i;
2264                 std::list<ClientSimpleObject*>::iterator cur = i;
2265                 ++i;
2266                 simple->step(dtime);
2267                 if(simple->m_to_be_removed){
2268                         delete simple;
2269                         m_simple_objects.erase(cur);
2270                 }
2271         }
2272 }
2273         
2274 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2275 {
2276         m_simple_objects.push_back(simple);
2277 }
2278
2279 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2280 {
2281         std::map<u16, ClientActiveObject*>::iterator n;
2282         n = m_active_objects.find(id);
2283         if(n == m_active_objects.end())
2284                 return NULL;
2285         return n->second;
2286 }
2287
2288 bool isFreeClientActiveObjectId(u16 id,
2289                 std::map<u16, ClientActiveObject*> &objects)
2290 {
2291         if(id == 0)
2292                 return false;
2293
2294         return objects.find(id) == objects.end();
2295 }
2296
2297 u16 getFreeClientActiveObjectId(
2298                 std::map<u16, ClientActiveObject*> &objects)
2299 {
2300         //try to reuse id's as late as possible
2301         static u16 last_used_id = 0;
2302         u16 startid = last_used_id;
2303         for(;;)
2304         {
2305                 last_used_id ++;
2306                 if(isFreeClientActiveObjectId(last_used_id, objects))
2307                         return last_used_id;
2308                 
2309                 if(last_used_id == startid)
2310                         return 0;
2311         }
2312 }
2313
2314 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2315 {
2316         assert(object);
2317         if(object->getId() == 0)
2318         {
2319                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2320                 if(new_id == 0)
2321                 {
2322                         infostream<<"ClientEnvironment::addActiveObject(): "
2323                                         <<"no free ids available"<<std::endl;
2324                         delete object;
2325                         return 0;
2326                 }
2327                 object->setId(new_id);
2328         }
2329         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2330         {
2331                 infostream<<"ClientEnvironment::addActiveObject(): "
2332                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2333                 delete object;
2334                 return 0;
2335         }
2336         infostream<<"ClientEnvironment::addActiveObject(): "
2337                         <<"added (id="<<object->getId()<<")"<<std::endl;
2338         m_active_objects[object->getId()] = object;
2339         object->addToScene(m_smgr, m_texturesource, m_irr);
2340         { // Update lighting immediately
2341                 u8 light = 0;
2342                 try{
2343                         // Get node at head
2344                         v3s16 p = object->getLightPosition();
2345                         MapNode n = m_map->getNode(p);
2346                         light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2347                 }
2348                 catch(InvalidPositionException &e){
2349                         light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2350                 }
2351                 object->updateLight(light);
2352         }
2353         return object->getId();
2354 }
2355
2356 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2357                 const std::string &init_data)
2358 {
2359         ClientActiveObject* obj =
2360                         ClientActiveObject::create(type, m_gamedef, this);
2361         if(obj == NULL)
2362         {
2363                 infostream<<"ClientEnvironment::addActiveObject(): "
2364                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2365                                 <<std::endl;
2366                 return;
2367         }
2368         
2369         obj->setId(id);
2370
2371         try
2372         {
2373                 obj->initialize(init_data);
2374         }
2375         catch(SerializationError &e)
2376         {
2377                 errorstream<<"ClientEnvironment::addActiveObject():"
2378                                 <<" id="<<id<<" type="<<type
2379                                 <<": SerializationError in initialize(): "
2380                                 <<e.what()
2381                                 <<": init_data="<<serializeJsonString(init_data)
2382                                 <<std::endl;
2383         }
2384
2385         addActiveObject(obj);
2386 }
2387
2388 void ClientEnvironment::removeActiveObject(u16 id)
2389 {
2390         verbosestream<<"ClientEnvironment::removeActiveObject(): "
2391                         <<"id="<<id<<std::endl;
2392         ClientActiveObject* obj = getActiveObject(id);
2393         if(obj == NULL)
2394         {
2395                 infostream<<"ClientEnvironment::removeActiveObject(): "
2396                                 <<"id="<<id<<" not found"<<std::endl;
2397                 return;
2398         }
2399         obj->removeFromScene(true);
2400         delete obj;
2401         m_active_objects.erase(id);
2402 }
2403
2404 void ClientEnvironment::processActiveObjectMessage(u16 id,
2405                 const std::string &data)
2406 {
2407         ClientActiveObject* obj = getActiveObject(id);
2408         if(obj == NULL)
2409         {
2410                 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2411                                 <<" got message for id="<<id<<", which doesn't exist."
2412                                 <<std::endl;
2413                 return;
2414         }
2415         try
2416         {
2417                 obj->processMessage(data);
2418         }
2419         catch(SerializationError &e)
2420         {
2421                 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2422                                 <<" id="<<id<<" type="<<obj->getType()
2423                                 <<" SerializationError in processMessage(),"
2424                                 <<" message="<<serializeJsonString(data)
2425                                 <<std::endl;
2426         }
2427 }
2428
2429 /*
2430         Callbacks for activeobjects
2431 */
2432
2433 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2434 {
2435         LocalPlayer *lplayer = getLocalPlayer();
2436         assert(lplayer);
2437         
2438         if(handle_hp){
2439                 if(lplayer->hp > damage)
2440                         lplayer->hp -= damage;
2441                 else
2442                         lplayer->hp = 0;
2443         }
2444
2445         ClientEnvEvent event;
2446         event.type = CEE_PLAYER_DAMAGE;
2447         event.player_damage.amount = damage;
2448         event.player_damage.send_to_server = handle_hp;
2449         m_client_event_queue.push_back(event);
2450 }
2451
2452 /*
2453         Client likes to call these
2454 */
2455         
2456 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2457                 std::vector<DistanceSortedActiveObject> &dest)
2458 {
2459         for(std::map<u16, ClientActiveObject*>::iterator
2460                         i = m_active_objects.begin();
2461                         i != m_active_objects.end(); ++i)
2462         {
2463                 ClientActiveObject* obj = i->second;
2464
2465                 f32 d = (obj->getPosition() - origin).getLength();
2466
2467                 if(d > max_d)
2468                         continue;
2469
2470                 DistanceSortedActiveObject dso(obj, d);
2471
2472                 dest.push_back(dso);
2473         }
2474 }
2475
2476 ClientEnvEvent ClientEnvironment::getClientEvent()
2477 {
2478         if(m_client_event_queue.empty())
2479         {
2480                 ClientEnvEvent event;
2481                 event.type = CEE_NONE;
2482                 return event;
2483         }
2484         return m_client_event_queue.pop_front();
2485 }
2486
2487 #endif // #ifndef SERVER
2488
2489