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