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