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