Catch SendFailedException when replying back in Connection::Receive()
[oweals/minetest.git] / src / environment.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "environment.h"
21 #include "filesys.h"
22 #include "porting.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
25 #include "mapblock.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
28 #include "mapgen.h"
29 #include "settings.h"
30 #include "log.h"
31 #include "profiler.h"
32
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                         }
892
893                         v3s16 p0;
894                         for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
895                         for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
896                         for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
897                         {
898                                 v3s16 p = p0 + block->getPosRelative();
899                                 MapNode n = block->getNodeNoEx(p0);
900
901                                 /*
902                                         Test something:
903                                         Convert mud under proper lighting to grass
904                                 */
905                                 if(n.getContent() == CONTENT_MUD)
906                                 {
907                                         if(myrand()%20 == 0)
908                                         {
909                                                 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
910                                                 if(content_features(n_top).air_equivalent &&
911                                                                 n_top.getLightBlend(getDayNightRatio()) >= 13)
912                                                 {
913                                                         n.setContent(CONTENT_GRASS);
914                                                         m_map->addNodeWithEvent(p, n);
915                                                 }
916                                         }
917                                 }
918                                 /*
919                                         Convert grass into mud if under something else than air
920                                 */
921                                 if(n.getContent() == CONTENT_GRASS)
922                                 {
923                                         //if(myrand()%20 == 0)
924                                         {
925                                                 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
926                                                 if(content_features(n_top).air_equivalent == false)
927                                                 {
928                                                         n.setContent(CONTENT_MUD);
929                                                         m_map->addNodeWithEvent(p, n);
930                                                 }
931                                         }
932                                 }
933                                 /*
934                                         Rats spawn around regular trees
935                                 */
936                                 if(n.getContent() == CONTENT_TREE ||
937                                                 n.getContent() == CONTENT_JUNGLETREE)
938                                 {
939                                         if(myrand()%200 == 0 && active_object_count_wider == 0)
940                                         {
941                                                 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
942                                                                 0, myrand_range(-2, 2));
943                                                 MapNode n1 = m_map->getNodeNoEx(p1);
944                                                 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
945                                                 if(n1b.getContent() == CONTENT_GRASS &&
946                                                                 n1.getContent() == CONTENT_AIR)
947                                                 {
948                                                         v3f pos = intToFloat(p1, BS);
949                                                         ServerActiveObject *obj = new RatSAO(this, 0, pos);
950                                                         addActiveObject(obj);
951                                                 }
952                                         }
953                                 }
954                                 /*
955                                         Fun things spawn in caves and dungeons
956                                 */
957                                 if(n.getContent() == CONTENT_STONE ||
958                                                 n.getContent() == CONTENT_MOSSYCOBBLE)
959                                 {
960                                         if(myrand()%200 == 0 && active_object_count_wider == 0)
961                                         {
962                                                 v3s16 p1 = p + v3s16(0,1,0);
963                                                 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
964                                                 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
965                                                         MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
966                                                         if(n1a.getContent() == CONTENT_AIR &&
967                                                                         n1b.getContent() == CONTENT_AIR)
968                                                         {
969                                                                 v3f pos = intToFloat(p1, BS);
970                                                                 int i = myrand()%5;
971                                                                 if(i == 0 || i == 1){
972                                                                         actionstream<<"A dungeon master spawns at "
973                                                                                         <<PP(p1)<<std::endl;
974                                                                         Settings properties;
975                                                                         getMob_dungeon_master(properties);
976                                                                         ServerActiveObject *obj = new MobV2SAO(
977                                                                                         this, 0, pos, &properties);
978                                                                         addActiveObject(obj);
979                                                                 } else if(i == 2 || i == 3){
980                                                                         actionstream<<"Rats spawn at "
981                                                                                         <<PP(p1)<<std::endl;
982                                                                         for(int j=0; j<3; j++){
983                                                                                 ServerActiveObject *obj = new RatSAO(
984                                                                                                 this, 0, pos);
985                                                                                 addActiveObject(obj);
986                                                                         }
987                                                                 } else {
988                                                                         actionstream<<"An oerkki spawns at "
989                                                                                         <<PP(p1)<<std::endl;
990                                                                         ServerActiveObject *obj = new Oerkki1SAO(
991                                                                                         this, 0, pos);
992                                                                         addActiveObject(obj);
993                                                                 }
994                                                         }
995                                                 }
996                                         }
997                                 }
998                                 /*
999                                         Make trees from saplings!
1000                                 */
1001                                 if(n.getContent() == CONTENT_SAPLING)
1002                                 {
1003                                         if(myrand()%50 == 0)
1004                                         {
1005                                                 actionstream<<"A sapling grows into a tree at "
1006                                                                 <<PP(p)<<std::endl;
1007
1008                                                 core::map<v3s16, MapBlock*> modified_blocks;
1009                                                 v3s16 tree_p = p;
1010                                                 ManualMapVoxelManipulator vmanip(m_map);
1011                                                 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1012                                                 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1013                                                 bool is_apple_tree = myrand()%4 == 0;
1014                                                 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1015                                                 vmanip.blitBackAll(&modified_blocks);
1016
1017                                                 // update lighting
1018                                                 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1019                                                 for(core::map<v3s16, MapBlock*>::Iterator
1020                                                         i = modified_blocks.getIterator();
1021                                                         i.atEnd() == false; i++)
1022                                                 {
1023                                                         lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1024                                                 }
1025                                                 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1026
1027                                                 // Send a MEET_OTHER event
1028                                                 MapEditEvent event;
1029                                                 event.type = MEET_OTHER;
1030                                                 for(core::map<v3s16, MapBlock*>::Iterator
1031                                                         i = modified_blocks.getIterator();
1032                                                         i.atEnd() == false; i++)
1033                                                 {
1034                                                         v3s16 p = i.getNode()->getKey();
1035                                                         event.modified_blocks.insert(p, true);
1036                                                 }
1037                                                 m_map->dispatchEvent(&event);
1038                                         }
1039                                 }
1040                         }
1041                 }
1042         }
1043         
1044         /*
1045                 Step active objects
1046         */
1047         {
1048                 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1049                 //TimeTaker timer("Step active objects");
1050
1051                 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1052                 
1053                 // This helps the objects to send data at the same time
1054                 bool send_recommended = false;
1055                 m_send_recommended_timer += dtime;
1056                 if(m_send_recommended_timer > 0.10)
1057                 {
1058                         m_send_recommended_timer = 0;
1059                         send_recommended = true;
1060                 }
1061
1062                 for(core::map<u16, ServerActiveObject*>::Iterator
1063                                 i = m_active_objects.getIterator();
1064                                 i.atEnd()==false; i++)
1065                 {
1066                         ServerActiveObject* obj = i.getNode()->getValue();
1067                         // Remove non-peaceful mobs on peaceful mode
1068                         if(g_settings->getBool("only_peaceful_mobs")){
1069                                 if(!obj->isPeaceful())
1070                                         obj->m_removed = true;
1071                         }
1072                         // Don't step if is to be removed or stored statically
1073                         if(obj->m_removed || obj->m_pending_deactivation)
1074                                 continue;
1075                         // Step object
1076                         obj->step(dtime, send_recommended);
1077                         // Read messages from object
1078                         while(obj->m_messages_out.size() > 0)
1079                         {
1080                                 m_active_object_messages.push_back(
1081                                                 obj->m_messages_out.pop_front());
1082                         }
1083                 }
1084         }
1085         
1086         /*
1087                 Manage active objects
1088         */
1089         if(m_object_management_interval.step(dtime, 0.5))
1090         {
1091                 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1092                 /*
1093                         Remove objects that satisfy (m_removed && m_known_by_count==0)
1094                 */
1095                 removeRemovedObjects();
1096         }
1097
1098         if(g_settings->getBool("enable_experimental"))
1099         {
1100
1101         /*
1102                 TEST CODE
1103         */
1104 #if 0
1105         m_random_spawn_timer -= dtime;
1106         if(m_random_spawn_timer < 0)
1107         {
1108                 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1109                 //m_random_spawn_timer += 2.0;
1110                 m_random_spawn_timer += 200.0;
1111
1112                 /*
1113                         Find some position
1114                 */
1115
1116                 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1117                 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1118                 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1119                 
1120                 Player *player = getRandomConnectedPlayer();
1121                 v3f pos(0,0,0);
1122                 if(player)
1123                         pos = player->getPosition();
1124                 pos += v3f(
1125                         myrand_range(-3,3)*BS,
1126                         5,
1127                         myrand_range(-3,3)*BS
1128                 );
1129
1130                 /*
1131                         Create a ServerActiveObject
1132                 */
1133
1134                 //TestSAO *obj = new TestSAO(this, 0, pos);
1135                 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1136                 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1137                 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1138                 //ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1139
1140                 infostream<<"Server: Spawning MobV2SAO at "
1141                                 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1142                 
1143                 Settings properties;
1144                 getMob_dungeon_master(properties);
1145                 ServerActiveObject *obj = new MobV2SAO(this, 0, pos, &properties);
1146                 addActiveObject(obj);
1147         }
1148 #endif
1149
1150         } // enable_experimental
1151 }
1152
1153 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1154 {
1155         core::map<u16, ServerActiveObject*>::Node *n;
1156         n = m_active_objects.find(id);
1157         if(n == NULL)
1158                 return NULL;
1159         return n->getValue();
1160 }
1161
1162 bool isFreeServerActiveObjectId(u16 id,
1163                 core::map<u16, ServerActiveObject*> &objects)
1164 {
1165         if(id == 0)
1166                 return false;
1167         
1168         for(core::map<u16, ServerActiveObject*>::Iterator
1169                         i = objects.getIterator();
1170                         i.atEnd()==false; i++)
1171         {
1172                 if(i.getNode()->getKey() == id)
1173                         return false;
1174         }
1175         return true;
1176 }
1177
1178 u16 getFreeServerActiveObjectId(
1179                 core::map<u16, ServerActiveObject*> &objects)
1180 {
1181         u16 new_id = 1;
1182         for(;;)
1183         {
1184                 if(isFreeServerActiveObjectId(new_id, objects))
1185                         return new_id;
1186                 
1187                 if(new_id == 65535)
1188                         return 0;
1189
1190                 new_id++;
1191         }
1192 }
1193
1194 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1195 {
1196         assert(object);
1197         u16 id = addActiveObjectRaw(object, true);
1198         return id;
1199 }
1200
1201 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1202 {
1203         assert(obj);
1204
1205         v3f objectpos = obj->getBasePosition(); 
1206
1207         // The block in which the object resides in
1208         v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1209
1210         /*
1211                 Update the static data
1212         */
1213
1214         // Create new static object
1215         std::string staticdata = obj->getStaticData();
1216         StaticObject s_obj(obj->getType(), objectpos, staticdata);
1217         // Add to the block where the object is located in
1218         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1219         // Get or generate the block
1220         MapBlock *block = m_map->emergeBlock(blockpos);
1221
1222         bool succeeded = false;
1223
1224         if(block)
1225         {
1226                 block->m_static_objects.insert(0, s_obj);
1227                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1228                 succeeded = true;
1229         }
1230         else{
1231                 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1232                                 <<"Could not find or generate "
1233                                 <<"a block for storing static object"<<std::endl;
1234                 succeeded = false;
1235         }
1236
1237         delete obj;
1238
1239         return succeeded;
1240 }
1241
1242 /*
1243         Finds out what new objects have been added to
1244         inside a radius around a position
1245 */
1246 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1247                 core::map<u16, bool> &current_objects,
1248                 core::map<u16, bool> &added_objects)
1249 {
1250         v3f pos_f = intToFloat(pos, BS);
1251         f32 radius_f = radius * BS;
1252         /*
1253                 Go through the object list,
1254                 - discard m_removed objects,
1255                 - discard objects that are too far away,
1256                 - discard objects that are found in current_objects.
1257                 - add remaining objects to added_objects
1258         */
1259         for(core::map<u16, ServerActiveObject*>::Iterator
1260                         i = m_active_objects.getIterator();
1261                         i.atEnd()==false; i++)
1262         {
1263                 u16 id = i.getNode()->getKey();
1264                 // Get object
1265                 ServerActiveObject *object = i.getNode()->getValue();
1266                 if(object == NULL)
1267                         continue;
1268                 // Discard if removed
1269                 if(object->m_removed)
1270                         continue;
1271                 // Discard if too far
1272                 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1273                 if(distance_f > radius_f)
1274                         continue;
1275                 // Discard if already on current_objects
1276                 core::map<u16, bool>::Node *n;
1277                 n = current_objects.find(id);
1278                 if(n != NULL)
1279                         continue;
1280                 // Add to added_objects
1281                 added_objects.insert(id, false);
1282         }
1283 }
1284
1285 /*
1286         Finds out what objects have been removed from
1287         inside a radius around a position
1288 */
1289 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1290                 core::map<u16, bool> &current_objects,
1291                 core::map<u16, bool> &removed_objects)
1292 {
1293         v3f pos_f = intToFloat(pos, BS);
1294         f32 radius_f = radius * BS;
1295         /*
1296                 Go through current_objects; object is removed if:
1297                 - object is not found in m_active_objects (this is actually an
1298                   error condition; objects should be set m_removed=true and removed
1299                   only after all clients have been informed about removal), or
1300                 - object has m_removed=true, or
1301                 - object is too far away
1302         */
1303         for(core::map<u16, bool>::Iterator
1304                         i = current_objects.getIterator();
1305                         i.atEnd()==false; i++)
1306         {
1307                 u16 id = i.getNode()->getKey();
1308                 ServerActiveObject *object = getActiveObject(id);
1309                 if(object == NULL)
1310                 {
1311                         infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1312                                         <<" object in current_objects is NULL"<<std::endl;
1313                 }
1314                 else if(object->m_removed == false)
1315                 {
1316                         f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1317                         /*infostream<<"removed == false"
1318                                         <<"distance_f = "<<distance_f
1319                                         <<", radius_f = "<<radius_f<<std::endl;*/
1320                         if(distance_f < radius_f)
1321                         {
1322                                 // Not removed
1323                                 continue;
1324                         }
1325                 }
1326                 removed_objects.insert(id, false);
1327         }
1328 }
1329
1330 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1331 {
1332         if(m_active_object_messages.size() == 0)
1333                 return ActiveObjectMessage(0);
1334         
1335         return m_active_object_messages.pop_front();
1336 }
1337
1338 /*
1339         ************ Private methods *************
1340 */
1341
1342 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1343                 bool set_changed)
1344 {
1345         assert(object);
1346         if(object->getId() == 0){
1347                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1348                 if(new_id == 0)
1349                 {
1350                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1351                                         <<"no free ids available"<<std::endl;
1352                         delete object;
1353                         return 0;
1354                 }
1355                 object->setId(new_id);
1356         }
1357         else{
1358                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1359                                 <<"supplied with id "<<object->getId()<<std::endl;
1360         }
1361         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1362         {
1363                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1364                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1365                 delete object;
1366                 return 0;
1367         }
1368         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1369                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1370                         
1371         m_active_objects.insert(object->getId(), object);
1372   
1373         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1374                         <<"Added id="<<object->getId()<<"; there are now "
1375                         <<m_active_objects.size()<<" active objects."
1376                         <<std::endl;
1377         
1378         // Add static object to active static list of the block
1379         v3f objectpos = object->getBasePosition();
1380         std::string staticdata = object->getStaticData();
1381         StaticObject s_obj(object->getType(), objectpos, staticdata);
1382         // Add to the block where the object is located in
1383         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1384         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1385         if(block)
1386         {
1387                 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1388                 object->m_static_exists = true;
1389                 object->m_static_block = blockpos;
1390
1391                 if(set_changed)
1392                         block->setChangedFlag();
1393         }
1394         else{
1395                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1396                                 <<"could not find block for storing id="<<object->getId()
1397                                 <<" statically"<<std::endl;
1398         }
1399
1400         return object->getId();
1401 }
1402
1403 /*
1404         Remove objects that satisfy (m_removed && m_known_by_count==0)
1405 */
1406 void ServerEnvironment::removeRemovedObjects()
1407 {
1408         core::list<u16> objects_to_remove;
1409         for(core::map<u16, ServerActiveObject*>::Iterator
1410                         i = m_active_objects.getIterator();
1411                         i.atEnd()==false; i++)
1412         {
1413                 u16 id = i.getNode()->getKey();
1414                 ServerActiveObject* obj = i.getNode()->getValue();
1415                 // This shouldn't happen but check it
1416                 if(obj == NULL)
1417                 {
1418                         infostream<<"NULL object found in ServerEnvironment"
1419                                         <<" while finding removed objects. id="<<id<<std::endl;
1420                         // Id to be removed from m_active_objects
1421                         objects_to_remove.push_back(id);
1422                         continue;
1423                 }
1424
1425                 /*
1426                         We will delete objects that are marked as removed or thatare
1427                         waiting for deletion after deactivation
1428                 */
1429                 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1430                         continue;
1431
1432                 /*
1433                         Delete static data from block if is marked as removed
1434                 */
1435                 if(obj->m_static_exists && obj->m_removed)
1436                 {
1437                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1438                         if(block)
1439                         {
1440                                 block->m_static_objects.remove(id);
1441                                 block->setChangedFlag();
1442                         }
1443                 }
1444
1445                 // If m_known_by_count > 0, don't actually remove.
1446                 if(obj->m_known_by_count > 0)
1447                         continue;
1448                 
1449                 // Delete
1450                 delete obj;
1451                 // Id to be removed from m_active_objects
1452                 objects_to_remove.push_back(id);
1453         }
1454         // Remove references from m_active_objects
1455         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1456                         i != objects_to_remove.end(); i++)
1457         {
1458                 m_active_objects.remove(*i);
1459         }
1460 }
1461
1462 static void print_hexdump(std::ostream &o, const std::string &data)
1463 {
1464         const int linelength = 16;
1465         for(int l=0; ; l++){
1466                 int i0 = linelength * l;
1467                 bool at_end = false;
1468                 int thislinelength = linelength;
1469                 if(i0 + thislinelength > (int)data.size()){
1470                         thislinelength = data.size() - i0;
1471                         at_end = true;
1472                 }
1473                 for(int di=0; di<linelength; di++){
1474                         int i = i0 + di;
1475                         char buf[4];
1476                         if(di<thislinelength)
1477                                 snprintf(buf, 4, "%.2x ", data[i]);
1478                         else
1479                                 snprintf(buf, 4, "   ");
1480                         o<<buf;
1481                 }
1482                 o<<" ";
1483                 for(int di=0; di<thislinelength; di++){
1484                         int i = i0 + di;
1485                         if(data[i] >= 32)
1486                                 o<<data[i];
1487                         else
1488                                 o<<".";
1489                 }
1490                 o<<std::endl;
1491                 if(at_end)
1492                         break;
1493         }
1494 }
1495
1496 /*
1497         Convert stored objects from blocks near the players to active.
1498 */
1499 void ServerEnvironment::activateObjects(MapBlock *block)
1500 {
1501         if(block==NULL)
1502                 return;
1503         // Ignore if no stored objects (to not set changed flag)
1504         if(block->m_static_objects.m_stored.size() == 0)
1505                 return;
1506         verbosestream<<"ServerEnvironment::activateObjects(): "
1507                         <<"activating objects of block "<<PP(block->getPos())
1508                         <<" ("<<block->m_static_objects.m_stored.size()
1509                         <<" objects)"<<std::endl;
1510         bool large_amount = (block->m_static_objects.m_stored.size() >= 50);
1511         if(large_amount){
1512                 errorstream<<"suspiciously large amount of objects detected: "
1513                                 <<block->m_static_objects.m_stored.size()<<" in "
1514                                 <<PP(block->getPos())
1515                                 <<"; not activating."<<std::endl;
1516                 return;
1517         }
1518         // A list for objects that couldn't be converted to static for some
1519         // reason. They will be stored back.
1520         core::list<StaticObject> new_stored;
1521         // Loop through stored static objects
1522         for(core::list<StaticObject>::Iterator
1523                         i = block->m_static_objects.m_stored.begin();
1524                         i != block->m_static_objects.m_stored.end(); i++)
1525         {
1526                 /*infostream<<"Server: Creating an active object from "
1527                                 <<"static data"<<std::endl;*/
1528                 StaticObject &s_obj = *i;
1529                 // Create an active object from the data
1530                 ServerActiveObject *obj = ServerActiveObject::create
1531                                 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1532                 // If couldn't create object, store static data back.
1533                 if(obj==NULL)
1534                 {
1535                         errorstream<<"ServerEnvironment::activateObjects(): "
1536                                         <<"failed to create active object from static object "
1537                                         <<"in block "<<PP(s_obj.pos/BS)
1538                                         <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1539                         print_hexdump(verbosestream, s_obj.data);
1540                         
1541                         new_stored.push_back(s_obj);
1542                         continue;
1543                 }
1544                 verbosestream<<"ServerEnvironment::activateObjects(): "
1545                                 <<"activated static object pos="<<PP(s_obj.pos/BS)
1546                                 <<" type="<<(int)s_obj.type<<std::endl;
1547                 // This will also add the object to the active static list
1548                 addActiveObjectRaw(obj, false);
1549                 //u16 id = addActiveObjectRaw(obj, false);
1550         }
1551         // Clear stored list
1552         block->m_static_objects.m_stored.clear();
1553         // Add leftover failed stuff to stored list
1554         for(core::list<StaticObject>::Iterator
1555                         i = new_stored.begin();
1556                         i != new_stored.end(); i++)
1557         {
1558                 StaticObject &s_obj = *i;
1559                 block->m_static_objects.m_stored.push_back(s_obj);
1560         }
1561         // Block has been modified
1562         // NOTE: No it has not really. Save I/O here.
1563         //block->setChangedFlag();
1564 }
1565
1566 /*
1567         Convert objects that are not in active blocks to static.
1568
1569         If m_known_by_count != 0, active object is not deleted, but static
1570         data is still updated.
1571
1572         If force_delete is set, active object is deleted nevertheless. It
1573         shall only be set so in the destructor of the environment.
1574 */
1575 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1576 {
1577         core::list<u16> objects_to_remove;
1578         for(core::map<u16, ServerActiveObject*>::Iterator
1579                         i = m_active_objects.getIterator();
1580                         i.atEnd()==false; i++)
1581         {
1582                 ServerActiveObject* obj = i.getNode()->getValue();
1583
1584                 // This shouldn't happen but check it
1585                 if(obj == NULL)
1586                 {
1587                         infostream<<"NULL object found in ServerEnvironment"
1588                                         <<std::endl;
1589                         assert(0);
1590                         continue;
1591                 }
1592
1593                 u16 id = i.getNode()->getKey();         
1594                 v3f objectpos = obj->getBasePosition(); 
1595
1596                 // The block in which the object resides in
1597                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1598
1599                 // If block is active, don't remove
1600                 if(m_active_blocks.contains(blockpos_o))
1601                         continue;
1602
1603                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1604                                 <<"deactivating object id="<<id<<" on inactive block "
1605                                 <<PP(blockpos_o)<<std::endl;
1606
1607                 /*
1608                         Update the static data
1609                 */
1610
1611                 // Delete old static object
1612                 MapBlock *oldblock = NULL;
1613                 if(obj->m_static_exists)
1614                 {
1615                         MapBlock *block = m_map->getBlockNoCreateNoEx
1616                                         (obj->m_static_block);
1617                         if(block)
1618                         {
1619                                 block->m_static_objects.remove(id);
1620                                 oldblock = block;
1621                         }
1622                         obj->m_static_exists = false;
1623                 }
1624
1625                 // Create new static object
1626                 std::string staticdata = obj->getStaticData();
1627                 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1628                 // Add to the block where the object is located in
1629                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1630                 // Get or generate the block
1631                 MapBlock *block = m_map->emergeBlock(blockpos);
1632
1633                 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1634                 if(block == NULL)
1635                 {
1636                         // Block not found. Is the old block still ok?
1637                         if(oldblock)
1638                                 block = oldblock;
1639                         // Load from disk or generate
1640                         else
1641                                 block = m_map->emergeBlock(blockpos);
1642                 }*/
1643
1644                 if(block)
1645                 {
1646                         if(block->m_static_objects.m_stored.size() >= 50){
1647                                 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1648                                                 <<" statically but block "<<PP(blockpos)
1649                                                 <<" already contains over 50 objects."
1650                                                 <<" Forcing delete."<<std::endl;
1651                                 force_delete = true;
1652                         } else {
1653                                 block->m_static_objects.insert(0, s_obj);
1654                                 block->setChangedFlag();
1655                                 obj->m_static_exists = true;
1656                                 obj->m_static_block = block->getPos();
1657                         }
1658                 }
1659                 else{
1660                         errorstream<<"ServerEnv: Could not find or generate "
1661                                         <<"a block for storing id="<<obj->getId()
1662                                         <<" statically"<<std::endl;
1663                         continue;
1664                 }
1665
1666                 /*
1667                         Delete active object if not known by some client,
1668                         else set pending deactivation
1669                 */
1670
1671                 // If known by some client, don't delete.
1672                 if(obj->m_known_by_count > 0 && force_delete == false)
1673                 {
1674                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1675                                         <<"object id="<<id<<" is known by clients"
1676                                         <<"; not deleting yet"<<std::endl;
1677
1678                         obj->m_pending_deactivation = true;
1679                         continue;
1680                 }
1681                 
1682                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1683                                 <<"object id="<<id<<" is not known by clients"
1684                                 <<"; deleting"<<std::endl;
1685                 // Delete active object
1686                 delete obj;
1687                 // Id to be removed from m_active_objects
1688                 objects_to_remove.push_back(id);
1689         }
1690
1691         // Remove references from m_active_objects
1692         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1693                         i != objects_to_remove.end(); i++)
1694         {
1695                 m_active_objects.remove(*i);
1696         }
1697 }
1698
1699
1700 #ifndef SERVER
1701
1702 /*
1703         ClientEnvironment
1704 */
1705
1706 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1707         m_map(map),
1708         m_smgr(smgr)
1709 {
1710         assert(m_map);
1711         assert(m_smgr);
1712 }
1713
1714 ClientEnvironment::~ClientEnvironment()
1715 {
1716         // delete active objects
1717         for(core::map<u16, ClientActiveObject*>::Iterator
1718                         i = m_active_objects.getIterator();
1719                         i.atEnd()==false; i++)
1720         {
1721                 delete i.getNode()->getValue();
1722         }
1723
1724         // Drop/delete map
1725         m_map->drop();
1726 }
1727
1728 void ClientEnvironment::addPlayer(Player *player)
1729 {
1730         DSTACK(__FUNCTION_NAME);
1731         /*
1732                 It is a failure if player is local and there already is a local
1733                 player
1734         */
1735         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1736
1737         Environment::addPlayer(player);
1738 }
1739
1740 LocalPlayer * ClientEnvironment::getLocalPlayer()
1741 {
1742         for(core::list<Player*>::Iterator i = m_players.begin();
1743                         i != m_players.end(); i++)
1744         {
1745                 Player *player = *i;
1746                 if(player->isLocal())
1747                         return (LocalPlayer*)player;
1748         }
1749         return NULL;
1750 }
1751
1752 void ClientEnvironment::step(float dtime)
1753 {
1754         DSTACK(__FUNCTION_NAME);
1755
1756         // Get some settings
1757         bool free_move = g_settings->getBool("free_move");
1758         bool footprints = g_settings->getBool("footprints");
1759
1760         // Get local player
1761         LocalPlayer *lplayer = getLocalPlayer();
1762         assert(lplayer);
1763         // collision info queue
1764         core::list<CollisionInfo> player_collisions;
1765         
1766         /*
1767                 Get the speed the player is going
1768         */
1769         bool is_climbing = lplayer->is_climbing;
1770         
1771         f32 player_speed = 0.001; // just some small value
1772         player_speed = lplayer->getSpeed().getLength();
1773         
1774         /*
1775                 Maximum position increment
1776         */
1777         //f32 position_max_increment = 0.05*BS;
1778         f32 position_max_increment = 0.1*BS;
1779
1780         // Maximum time increment (for collision detection etc)
1781         // time = distance / speed
1782         f32 dtime_max_increment = position_max_increment / player_speed;
1783         
1784         // Maximum time increment is 10ms or lower
1785         if(dtime_max_increment > 0.01)
1786                 dtime_max_increment = 0.01;
1787         
1788         // Don't allow overly huge dtime
1789         if(dtime > 0.5)
1790                 dtime = 0.5;
1791         
1792         f32 dtime_downcount = dtime;
1793
1794         /*
1795                 Stuff that has a maximum time increment
1796         */
1797
1798         u32 loopcount = 0;
1799         do
1800         {
1801                 loopcount++;
1802
1803                 f32 dtime_part;
1804                 if(dtime_downcount > dtime_max_increment)
1805                 {
1806                         dtime_part = dtime_max_increment;
1807                         dtime_downcount -= dtime_part;
1808                 }
1809                 else
1810                 {
1811                         dtime_part = dtime_downcount;
1812                         /*
1813                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
1814                                 when dtime_part is so small that dtime_downcount -= dtime_part
1815                                 does nothing
1816                         */
1817                         dtime_downcount = 0;
1818                 }
1819                 
1820                 /*
1821                         Handle local player
1822                 */
1823                 
1824                 {
1825                         v3f lplayerpos = lplayer->getPosition();
1826                         
1827                         // Apply physics
1828                         if(free_move == false && is_climbing == false)
1829                         {
1830                                 // Gravity
1831                                 v3f speed = lplayer->getSpeed();
1832                                 if(lplayer->swimming_up == false)
1833                                         speed.Y -= 9.81 * BS * dtime_part * 2;
1834
1835                                 // Water resistance
1836                                 if(lplayer->in_water_stable || lplayer->in_water)
1837                                 {
1838                                         f32 max_down = 2.0*BS;
1839                                         if(speed.Y < -max_down) speed.Y = -max_down;
1840
1841                                         f32 max = 2.5*BS;
1842                                         if(speed.getLength() > max)
1843                                         {
1844                                                 speed = speed / speed.getLength() * max;
1845                                         }
1846                                 }
1847
1848                                 lplayer->setSpeed(speed);
1849                         }
1850
1851                         /*
1852                                 Move the lplayer.
1853                                 This also does collision detection.
1854                         */
1855                         lplayer->move(dtime_part, *m_map, position_max_increment,
1856                                         &player_collisions);
1857                 }
1858         }
1859         while(dtime_downcount > 0.001);
1860                 
1861         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1862         
1863         for(core::list<CollisionInfo>::Iterator
1864                         i = player_collisions.begin();
1865                         i != player_collisions.end(); i++)
1866         {
1867                 CollisionInfo &info = *i;
1868                 if(info.t == COLLISION_FALL)
1869                 {
1870                         //f32 tolerance = BS*10; // 2 without damage
1871                         f32 tolerance = BS*12; // 3 without damage
1872                         f32 factor = 1;
1873                         if(info.speed > tolerance)
1874                         {
1875                                 f32 damage_f = (info.speed - tolerance)/BS*factor;
1876                                 u16 damage = (u16)(damage_f+0.5);
1877                                 if(lplayer->hp > damage)
1878                                         lplayer->hp -= damage;
1879                                 else
1880                                         lplayer->hp = 0;
1881
1882                                 ClientEnvEvent event;
1883                                 event.type = CEE_PLAYER_DAMAGE;
1884                                 event.player_damage.amount = damage;
1885                                 m_client_event_queue.push_back(event);
1886                         }
1887                 }
1888         }
1889         
1890         /*
1891                 A quick draft of lava damage
1892         */
1893         if(m_lava_hurt_interval.step(dtime, 1.0))
1894         {
1895                 v3f pf = lplayer->getPosition();
1896                 
1897                 // Feet, middle and head
1898                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1899                 MapNode n1 = m_map->getNodeNoEx(p1);
1900                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1901                 MapNode n2 = m_map->getNodeNoEx(p2);
1902                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1903                 MapNode n3 = m_map->getNodeNoEx(p2);
1904
1905                 u32 damage_per_second = 0;
1906                 damage_per_second = MYMAX(damage_per_second,
1907                                 content_features(n1).damage_per_second);
1908                 damage_per_second = MYMAX(damage_per_second,
1909                                 content_features(n2).damage_per_second);
1910                 damage_per_second = MYMAX(damage_per_second,
1911                                 content_features(n3).damage_per_second);
1912                 
1913                 if(damage_per_second != 0)
1914                 {
1915                         ClientEnvEvent event;
1916                         event.type = CEE_PLAYER_DAMAGE;
1917                         event.player_damage.amount = damage_per_second;
1918                         m_client_event_queue.push_back(event);
1919                 }
1920         }
1921         
1922         /*
1923                 Stuff that can be done in an arbitarily large dtime
1924         */
1925         for(core::list<Player*>::Iterator i = m_players.begin();
1926                         i != m_players.end(); i++)
1927         {
1928                 Player *player = *i;
1929                 v3f playerpos = player->getPosition();
1930                 
1931                 /*
1932                         Handle non-local players
1933                 */
1934                 if(player->isLocal() == false)
1935                 {
1936                         // Move
1937                         player->move(dtime, *m_map, 100*BS);
1938
1939                 }
1940                 
1941                 // Update lighting on all players on client
1942                 u8 light = LIGHT_MAX;
1943                 try{
1944                         // Get node at head
1945                         v3s16 p = player->getLightPosition();
1946                         MapNode n = m_map->getNode(p);
1947                         light = n.getLightBlend(getDayNightRatio());
1948                 }
1949                 catch(InvalidPositionException &e) {}
1950                 player->updateLight(light);
1951
1952                 /*
1953                         Add footsteps to grass
1954                 */
1955                 if(footprints)
1956                 {
1957                         // Get node that is at BS/4 under player
1958                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1959                         try{
1960                                 MapNode n = m_map->getNode(bottompos);
1961                                 if(n.getContent() == CONTENT_GRASS)
1962                                 {
1963                                         n.setContent(CONTENT_GRASS_FOOTSTEPS);
1964                                         m_map->setNode(bottompos, n);
1965                                         // Update mesh on client
1966                                         if(m_map->mapType() == MAPTYPE_CLIENT)
1967                                         {
1968                                                 v3s16 p_blocks = getNodeBlockPos(bottompos);
1969                                                 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1970                                                 //b->updateMesh(getDayNightRatio());
1971                                                 b->setMeshExpired(true);
1972                                         }
1973                                 }
1974                         }
1975                         catch(InvalidPositionException &e)
1976                         {
1977                         }
1978                 }
1979         }
1980         
1981         /*
1982                 Step active objects and update lighting of them
1983         */
1984         
1985         for(core::map<u16, ClientActiveObject*>::Iterator
1986                         i = m_active_objects.getIterator();
1987                         i.atEnd()==false; i++)
1988         {
1989                 ClientActiveObject* obj = i.getNode()->getValue();
1990                 // Step object
1991                 obj->step(dtime, this);
1992
1993                 if(m_active_object_light_update_interval.step(dtime, 0.21))
1994                 {
1995                         // Update lighting
1996                         //u8 light = LIGHT_MAX;
1997                         u8 light = 0;
1998                         try{
1999                                 // Get node at head
2000                                 v3s16 p = obj->getLightPosition();
2001                                 MapNode n = m_map->getNode(p);
2002                                 light = n.getLightBlend(getDayNightRatio());
2003                         }
2004                         catch(InvalidPositionException &e) {}
2005                         obj->updateLight(light);
2006                 }
2007         }
2008 }
2009
2010 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2011 {
2012         m_map->updateMeshes(blockpos, getDayNightRatio());
2013 }
2014
2015 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2016 {
2017         m_map->expireMeshes(only_daynight_diffed);
2018 }
2019
2020 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2021 {
2022         core::map<u16, ClientActiveObject*>::Node *n;
2023         n = m_active_objects.find(id);
2024         if(n == NULL)
2025                 return NULL;
2026         return n->getValue();
2027 }
2028
2029 bool isFreeClientActiveObjectId(u16 id,
2030                 core::map<u16, ClientActiveObject*> &objects)
2031 {
2032         if(id == 0)
2033                 return false;
2034         
2035         for(core::map<u16, ClientActiveObject*>::Iterator
2036                         i = objects.getIterator();
2037                         i.atEnd()==false; i++)
2038         {
2039                 if(i.getNode()->getKey() == id)
2040                         return false;
2041         }
2042         return true;
2043 }
2044
2045 u16 getFreeClientActiveObjectId(
2046                 core::map<u16, ClientActiveObject*> &objects)
2047 {
2048         u16 new_id = 1;
2049         for(;;)
2050         {
2051                 if(isFreeClientActiveObjectId(new_id, objects))
2052                         return new_id;
2053                 
2054                 if(new_id == 65535)
2055                         return 0;
2056
2057                 new_id++;
2058         }
2059 }
2060
2061 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2062 {
2063         assert(object);
2064         if(object->getId() == 0)
2065         {
2066                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2067                 if(new_id == 0)
2068                 {
2069                         infostream<<"ClientEnvironment::addActiveObject(): "
2070                                         <<"no free ids available"<<std::endl;
2071                         delete object;
2072                         return 0;
2073                 }
2074                 object->setId(new_id);
2075         }
2076         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2077         {
2078                 infostream<<"ClientEnvironment::addActiveObject(): "
2079                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2080                 delete object;
2081                 return 0;
2082         }
2083         infostream<<"ClientEnvironment::addActiveObject(): "
2084                         <<"added (id="<<object->getId()<<")"<<std::endl;
2085         m_active_objects.insert(object->getId(), object);
2086         object->addToScene(m_smgr);
2087         return object->getId();
2088 }
2089
2090 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2091                 const std::string &init_data)
2092 {
2093         ClientActiveObject* obj = ClientActiveObject::create(type);
2094         if(obj == NULL)
2095         {
2096                 infostream<<"ClientEnvironment::addActiveObject(): "
2097                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2098                                 <<std::endl;
2099                 return;
2100         }
2101         
2102         obj->setId(id);
2103
2104         obj->initialize(init_data);
2105         
2106         addActiveObject(obj);
2107 }
2108
2109 void ClientEnvironment::removeActiveObject(u16 id)
2110 {
2111         infostream<<"ClientEnvironment::removeActiveObject(): "
2112                         <<"id="<<id<<std::endl;
2113         ClientActiveObject* obj = getActiveObject(id);
2114         if(obj == NULL)
2115         {
2116                 infostream<<"ClientEnvironment::removeActiveObject(): "
2117                                 <<"id="<<id<<" not found"<<std::endl;
2118                 return;
2119         }
2120         obj->removeFromScene();
2121         delete obj;
2122         m_active_objects.remove(id);
2123 }
2124
2125 void ClientEnvironment::processActiveObjectMessage(u16 id,
2126                 const std::string &data)
2127 {
2128         ClientActiveObject* obj = getActiveObject(id);
2129         if(obj == NULL)
2130         {
2131                 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2132                                 <<" got message for id="<<id<<", which doesn't exist."
2133                                 <<std::endl;
2134                 return;
2135         }
2136         obj->processMessage(data);
2137 }
2138
2139 /*
2140         Callbacks for activeobjects
2141 */
2142
2143 void ClientEnvironment::damageLocalPlayer(u8 damage)
2144 {
2145         LocalPlayer *lplayer = getLocalPlayer();
2146         assert(lplayer);
2147
2148         if(lplayer->hp > damage)
2149                 lplayer->hp -= damage;
2150         else
2151                 lplayer->hp = 0;
2152
2153         ClientEnvEvent event;
2154         event.type = CEE_PLAYER_DAMAGE;
2155         event.player_damage.amount = damage;
2156         m_client_event_queue.push_back(event);
2157 }
2158
2159 /*
2160         Client likes to call these
2161 */
2162         
2163 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2164                 core::array<DistanceSortedActiveObject> &dest)
2165 {
2166         for(core::map<u16, ClientActiveObject*>::Iterator
2167                         i = m_active_objects.getIterator();
2168                         i.atEnd()==false; i++)
2169         {
2170                 ClientActiveObject* obj = i.getNode()->getValue();
2171
2172                 f32 d = (obj->getPosition() - origin).getLength();
2173
2174                 if(d > max_d)
2175                         continue;
2176
2177                 DistanceSortedActiveObject dso(obj, d);
2178
2179                 dest.push_back(dso);
2180         }
2181 }
2182
2183 ClientEnvEvent ClientEnvironment::getClientEvent()
2184 {
2185         if(m_client_event_queue.size() == 0)
2186         {
2187                 ClientEnvEvent event;
2188                 event.type = CEE_NONE;
2189                 return event;
2190         }
2191         return m_client_event_queue.pop_front();
2192 }
2193
2194 #endif // #ifndef SERVER
2195
2196