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