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