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