]> git.lizzy.rs Git - dragonfireclient.git/blob - src/environment.cpp
d3e8fa12cbbb2dab21ae226686fa3d22eca7694c
[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 > getSendRecommendedInterval())
1157                 {
1158                         m_send_recommended_timer -= getSendRecommendedInterval();
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                                 float save_movem = obj->getMinimumSavedMovement();
1753
1754                                 if(static_old.data == staticdata_new &&
1755                                                 (static_old.pos - objectpos).getLength() < save_movem)
1756                                         data_changed = false;
1757                         } else {
1758                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1759                                                 <<"id="<<id<<" m_static_exists=true but "
1760                                                 <<"static data doesn't actually exist in "
1761                                                 <<PP(obj->m_static_block)<<std::endl;
1762                         }
1763                 }
1764
1765                 bool shall_be_written = (!stays_in_same_block || data_changed);
1766                 
1767                 // Delete old static object
1768                 if(obj->m_static_exists)
1769                 {
1770                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1771                         if(block)
1772                         {
1773                                 block->m_static_objects.remove(id);
1774                                 obj->m_static_exists = false;
1775                                 // Only mark block as modified if data changed considerably
1776                                 if(shall_be_written)
1777                                         block->raiseModified(MOD_STATE_WRITE_NEEDED);
1778                         }
1779                 }
1780
1781                 // Add to the block where the object is located in
1782                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1783                 // Get or generate the block
1784                 MapBlock *block = m_map->emergeBlock(blockpos);
1785
1786                 if(block)
1787                 {
1788                         if(block->m_static_objects.m_stored.size() >= 49){
1789                                 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1790                                                 <<" statically but block "<<PP(blockpos)
1791                                                 <<" already contains "
1792                                                 <<block->m_static_objects.m_stored.size()
1793                                                 <<" (over 49) objects."
1794                                                 <<" Forcing delete."<<std::endl;
1795                                 force_delete = true;
1796                         } else {
1797                                 u16 new_id = pending_delete ? id : 0;
1798                                 block->m_static_objects.insert(new_id, s_obj);
1799                                 
1800                                 // Only mark block as modified if data changed considerably
1801                                 if(shall_be_written)
1802                                         block->raiseModified(MOD_STATE_WRITE_NEEDED);
1803                                 
1804                                 obj->m_static_exists = true;
1805                                 obj->m_static_block = block->getPos();
1806                         }
1807                 }
1808                 else{
1809                         errorstream<<"ServerEnv: Could not find or generate "
1810                                         <<"a block for storing id="<<obj->getId()
1811                                         <<" statically"<<std::endl;
1812                         continue;
1813                 }
1814
1815                 /*
1816                         If known by some client, set pending deactivation.
1817                         Otherwise delete it immediately.
1818                 */
1819
1820                 if(pending_delete)
1821                 {
1822                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1823                                         <<"object id="<<id<<" is known by clients"
1824                                         <<"; not deleting yet"<<std::endl;
1825
1826                         obj->m_pending_deactivation = true;
1827                         continue;
1828                 }
1829                 
1830                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1831                                 <<"object id="<<id<<" is not known by clients"
1832                                 <<"; deleting"<<std::endl;
1833
1834                 // Deregister in scripting api
1835                 scriptapi_rm_object_reference(m_lua, obj);
1836
1837                 // Delete active object
1838                 delete obj;
1839                 // Id to be removed from m_active_objects
1840                 objects_to_remove.push_back(id);
1841         }
1842
1843         // Remove references from m_active_objects
1844         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1845                         i != objects_to_remove.end(); i++)
1846         {
1847                 m_active_objects.remove(*i);
1848         }
1849 }
1850
1851
1852 #ifndef SERVER
1853
1854 /*
1855         ClientEnvironment
1856 */
1857
1858 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1859         m_map(map),
1860         m_smgr(smgr)
1861 {
1862         assert(m_map);
1863         assert(m_smgr);
1864 }
1865
1866 ClientEnvironment::~ClientEnvironment()
1867 {
1868         // delete active objects
1869         for(core::map<u16, ClientActiveObject*>::Iterator
1870                         i = m_active_objects.getIterator();
1871                         i.atEnd()==false; i++)
1872         {
1873                 delete i.getNode()->getValue();
1874         }
1875
1876         // Drop/delete map
1877         m_map->drop();
1878 }
1879
1880 void ClientEnvironment::addPlayer(Player *player)
1881 {
1882         DSTACK(__FUNCTION_NAME);
1883         /*
1884                 It is a failure if player is local and there already is a local
1885                 player
1886         */
1887         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1888
1889         Environment::addPlayer(player);
1890 }
1891
1892 LocalPlayer * ClientEnvironment::getLocalPlayer()
1893 {
1894         for(core::list<Player*>::Iterator i = m_players.begin();
1895                         i != m_players.end(); i++)
1896         {
1897                 Player *player = *i;
1898                 if(player->isLocal())
1899                         return (LocalPlayer*)player;
1900         }
1901         return NULL;
1902 }
1903
1904 void ClientEnvironment::step(float dtime)
1905 {
1906         DSTACK(__FUNCTION_NAME);
1907
1908         // Get some settings
1909         bool free_move = g_settings->getBool("free_move");
1910         bool footprints = g_settings->getBool("footprints");
1911
1912         // Get local player
1913         LocalPlayer *lplayer = getLocalPlayer();
1914         assert(lplayer);
1915         // collision info queue
1916         core::list<CollisionInfo> player_collisions;
1917         
1918         /*
1919                 Get the speed the player is going
1920         */
1921         bool is_climbing = lplayer->is_climbing;
1922         
1923         f32 player_speed = 0.001; // just some small value
1924         player_speed = lplayer->getSpeed().getLength();
1925         
1926         /*
1927                 Maximum position increment
1928         */
1929         //f32 position_max_increment = 0.05*BS;
1930         f32 position_max_increment = 0.1*BS;
1931
1932         // Maximum time increment (for collision detection etc)
1933         // time = distance / speed
1934         f32 dtime_max_increment = position_max_increment / player_speed;
1935         
1936         // Maximum time increment is 10ms or lower
1937         if(dtime_max_increment > 0.01)
1938                 dtime_max_increment = 0.01;
1939         
1940         // Don't allow overly huge dtime
1941         if(dtime > 0.5)
1942                 dtime = 0.5;
1943         
1944         f32 dtime_downcount = dtime;
1945
1946         /*
1947                 Stuff that has a maximum time increment
1948         */
1949
1950         u32 loopcount = 0;
1951         do
1952         {
1953                 loopcount++;
1954
1955                 f32 dtime_part;
1956                 if(dtime_downcount > dtime_max_increment)
1957                 {
1958                         dtime_part = dtime_max_increment;
1959                         dtime_downcount -= dtime_part;
1960                 }
1961                 else
1962                 {
1963                         dtime_part = dtime_downcount;
1964                         /*
1965                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
1966                                 when dtime_part is so small that dtime_downcount -= dtime_part
1967                                 does nothing
1968                         */
1969                         dtime_downcount = 0;
1970                 }
1971                 
1972                 /*
1973                         Handle local player
1974                 */
1975                 
1976                 {
1977                         v3f lplayerpos = lplayer->getPosition();
1978                         
1979                         // Apply physics
1980                         if(free_move == false && is_climbing == false)
1981                         {
1982                                 // Gravity
1983                                 v3f speed = lplayer->getSpeed();
1984                                 if(lplayer->swimming_up == false)
1985                                         speed.Y -= 9.81 * BS * dtime_part * 2;
1986
1987                                 // Water resistance
1988                                 if(lplayer->in_water_stable || lplayer->in_water)
1989                                 {
1990                                         f32 max_down = 2.0*BS;
1991                                         if(speed.Y < -max_down) speed.Y = -max_down;
1992
1993                                         f32 max = 2.5*BS;
1994                                         if(speed.getLength() > max)
1995                                         {
1996                                                 speed = speed / speed.getLength() * max;
1997                                         }
1998                                 }
1999
2000                                 lplayer->setSpeed(speed);
2001                         }
2002
2003                         /*
2004                                 Move the lplayer.
2005                                 This also does collision detection.
2006                         */
2007                         lplayer->move(dtime_part, *m_map, position_max_increment,
2008                                         &player_collisions);
2009                 }
2010         }
2011         while(dtime_downcount > 0.001);
2012                 
2013         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2014         
2015         for(core::list<CollisionInfo>::Iterator
2016                         i = player_collisions.begin();
2017                         i != player_collisions.end(); i++)
2018         {
2019                 CollisionInfo &info = *i;
2020                 if(info.t == COLLISION_FALL)
2021                 {
2022                         //f32 tolerance = BS*10; // 2 without damage
2023                         f32 tolerance = BS*12; // 3 without damage
2024                         f32 factor = 1;
2025                         if(info.speed > tolerance)
2026                         {
2027                                 f32 damage_f = (info.speed - tolerance)/BS*factor;
2028                                 u16 damage = (u16)(damage_f+0.5);
2029                                 if(lplayer->hp > damage)
2030                                         lplayer->hp -= damage;
2031                                 else
2032                                         lplayer->hp = 0;
2033
2034                                 ClientEnvEvent event;
2035                                 event.type = CEE_PLAYER_DAMAGE;
2036                                 event.player_damage.amount = damage;
2037                                 m_client_event_queue.push_back(event);
2038                         }
2039                 }
2040         }
2041         
2042         /*
2043                 A quick draft of lava damage
2044         */
2045         if(m_lava_hurt_interval.step(dtime, 1.0))
2046         {
2047                 v3f pf = lplayer->getPosition();
2048                 
2049                 // Feet, middle and head
2050                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2051                 MapNode n1 = m_map->getNodeNoEx(p1);
2052                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2053                 MapNode n2 = m_map->getNodeNoEx(p2);
2054                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2055                 MapNode n3 = m_map->getNodeNoEx(p2);
2056
2057                 u32 damage_per_second = 0;
2058                 damage_per_second = MYMAX(damage_per_second,
2059                                 content_features(n1).damage_per_second);
2060                 damage_per_second = MYMAX(damage_per_second,
2061                                 content_features(n2).damage_per_second);
2062                 damage_per_second = MYMAX(damage_per_second,
2063                                 content_features(n3).damage_per_second);
2064                 
2065                 if(damage_per_second != 0)
2066                 {
2067                         ClientEnvEvent event;
2068                         event.type = CEE_PLAYER_DAMAGE;
2069                         event.player_damage.amount = damage_per_second;
2070                         m_client_event_queue.push_back(event);
2071                 }
2072         }
2073         
2074         /*
2075                 Stuff that can be done in an arbitarily large dtime
2076         */
2077         for(core::list<Player*>::Iterator i = m_players.begin();
2078                         i != m_players.end(); i++)
2079         {
2080                 Player *player = *i;
2081                 v3f playerpos = player->getPosition();
2082                 
2083                 /*
2084                         Handle non-local players
2085                 */
2086                 if(player->isLocal() == false)
2087                 {
2088                         // Move
2089                         player->move(dtime, *m_map, 100*BS);
2090
2091                 }
2092                 
2093                 // Update lighting on all players on client
2094                 u8 light = LIGHT_MAX;
2095                 try{
2096                         // Get node at head
2097                         v3s16 p = player->getLightPosition();
2098                         MapNode n = m_map->getNode(p);
2099                         light = n.getLightBlend(getDayNightRatio());
2100                 }
2101                 catch(InvalidPositionException &e) {}
2102                 player->updateLight(light);
2103
2104                 /*
2105                         Add footsteps to grass
2106                 */
2107                 if(footprints)
2108                 {
2109                         // Get node that is at BS/4 under player
2110                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2111                         try{
2112                                 MapNode n = m_map->getNode(bottompos);
2113                                 if(n.getContent() == CONTENT_GRASS)
2114                                 {
2115                                         n.setContent(CONTENT_GRASS_FOOTSTEPS);
2116                                         m_map->setNode(bottompos, n);
2117                                         // Update mesh on client
2118                                         if(m_map->mapType() == MAPTYPE_CLIENT)
2119                                         {
2120                                                 v3s16 p_blocks = getNodeBlockPos(bottompos);
2121                                                 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2122                                                 //b->updateMesh(getDayNightRatio());
2123                                                 b->setMeshExpired(true);
2124                                         }
2125                                 }
2126                         }
2127                         catch(InvalidPositionException &e)
2128                         {
2129                         }
2130                 }
2131         }
2132         
2133         /*
2134                 Step active objects and update lighting of them
2135         */
2136         
2137         for(core::map<u16, ClientActiveObject*>::Iterator
2138                         i = m_active_objects.getIterator();
2139                         i.atEnd()==false; i++)
2140         {
2141                 ClientActiveObject* obj = i.getNode()->getValue();
2142                 // Step object
2143                 obj->step(dtime, this);
2144
2145                 if(m_active_object_light_update_interval.step(dtime, 0.21))
2146                 {
2147                         // Update lighting
2148                         //u8 light = LIGHT_MAX;
2149                         u8 light = 0;
2150                         try{
2151                                 // Get node at head
2152                                 v3s16 p = obj->getLightPosition();
2153                                 MapNode n = m_map->getNode(p);
2154                                 light = n.getLightBlend(getDayNightRatio());
2155                         }
2156                         catch(InvalidPositionException &e) {}
2157                         obj->updateLight(light);
2158                 }
2159         }
2160 }
2161
2162 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2163 {
2164         m_map->updateMeshes(blockpos, getDayNightRatio());
2165 }
2166
2167 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2168 {
2169         m_map->expireMeshes(only_daynight_diffed);
2170 }
2171
2172 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2173 {
2174         core::map<u16, ClientActiveObject*>::Node *n;
2175         n = m_active_objects.find(id);
2176         if(n == NULL)
2177                 return NULL;
2178         return n->getValue();
2179 }
2180
2181 bool isFreeClientActiveObjectId(u16 id,
2182                 core::map<u16, ClientActiveObject*> &objects)
2183 {
2184         if(id == 0)
2185                 return false;
2186         
2187         for(core::map<u16, ClientActiveObject*>::Iterator
2188                         i = objects.getIterator();
2189                         i.atEnd()==false; i++)
2190         {
2191                 if(i.getNode()->getKey() == id)
2192                         return false;
2193         }
2194         return true;
2195 }
2196
2197 u16 getFreeClientActiveObjectId(
2198                 core::map<u16, ClientActiveObject*> &objects)
2199 {
2200         u16 new_id = 1;
2201         for(;;)
2202         {
2203                 if(isFreeClientActiveObjectId(new_id, objects))
2204                         return new_id;
2205                 
2206                 if(new_id == 65535)
2207                         return 0;
2208
2209                 new_id++;
2210         }
2211 }
2212
2213 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2214 {
2215         assert(object);
2216         if(object->getId() == 0)
2217         {
2218                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2219                 if(new_id == 0)
2220                 {
2221                         infostream<<"ClientEnvironment::addActiveObject(): "
2222                                         <<"no free ids available"<<std::endl;
2223                         delete object;
2224                         return 0;
2225                 }
2226                 object->setId(new_id);
2227         }
2228         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2229         {
2230                 infostream<<"ClientEnvironment::addActiveObject(): "
2231                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2232                 delete object;
2233                 return 0;
2234         }
2235         infostream<<"ClientEnvironment::addActiveObject(): "
2236                         <<"added (id="<<object->getId()<<")"<<std::endl;
2237         m_active_objects.insert(object->getId(), object);
2238         object->addToScene(m_smgr);
2239         return object->getId();
2240 }
2241
2242 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2243                 const std::string &init_data)
2244 {
2245         ClientActiveObject* obj = ClientActiveObject::create(type);
2246         if(obj == NULL)
2247         {
2248                 infostream<<"ClientEnvironment::addActiveObject(): "
2249                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2250                                 <<std::endl;
2251                 return;
2252         }
2253         
2254         obj->setId(id);
2255
2256         obj->initialize(init_data);
2257         
2258         addActiveObject(obj);
2259 }
2260
2261 void ClientEnvironment::removeActiveObject(u16 id)
2262 {
2263         infostream<<"ClientEnvironment::removeActiveObject(): "
2264                         <<"id="<<id<<std::endl;
2265         ClientActiveObject* obj = getActiveObject(id);
2266         if(obj == NULL)
2267         {
2268                 infostream<<"ClientEnvironment::removeActiveObject(): "
2269                                 <<"id="<<id<<" not found"<<std::endl;
2270                 return;
2271         }
2272         obj->removeFromScene();
2273         delete obj;
2274         m_active_objects.remove(id);
2275 }
2276
2277 void ClientEnvironment::processActiveObjectMessage(u16 id,
2278                 const std::string &data)
2279 {
2280         ClientActiveObject* obj = getActiveObject(id);
2281         if(obj == NULL)
2282         {
2283                 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2284                                 <<" got message for id="<<id<<", which doesn't exist."
2285                                 <<std::endl;
2286                 return;
2287         }
2288         obj->processMessage(data);
2289 }
2290
2291 /*
2292         Callbacks for activeobjects
2293 */
2294
2295 void ClientEnvironment::damageLocalPlayer(u8 damage)
2296 {
2297         LocalPlayer *lplayer = getLocalPlayer();
2298         assert(lplayer);
2299
2300         if(lplayer->hp > damage)
2301                 lplayer->hp -= damage;
2302         else
2303                 lplayer->hp = 0;
2304
2305         ClientEnvEvent event;
2306         event.type = CEE_PLAYER_DAMAGE;
2307         event.player_damage.amount = damage;
2308         m_client_event_queue.push_back(event);
2309 }
2310
2311 /*
2312         Client likes to call these
2313 */
2314         
2315 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2316                 core::array<DistanceSortedActiveObject> &dest)
2317 {
2318         for(core::map<u16, ClientActiveObject*>::Iterator
2319                         i = m_active_objects.getIterator();
2320                         i.atEnd()==false; i++)
2321         {
2322                 ClientActiveObject* obj = i.getNode()->getValue();
2323
2324                 f32 d = (obj->getPosition() - origin).getLength();
2325
2326                 if(d > max_d)
2327                         continue;
2328
2329                 DistanceSortedActiveObject dso(obj, d);
2330
2331                 dest.push_back(dso);
2332         }
2333 }
2334
2335 ClientEnvEvent ClientEnvironment::getClientEvent()
2336 {
2337         if(m_client_event_queue.size() == 0)
2338         {
2339                 ClientEnvEvent event;
2340                 event.type = CEE_NONE;
2341                 return event;
2342         }
2343         return m_client_event_queue.pop_front();
2344 }
2345
2346 #endif // #ifndef SERVER
2347
2348