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