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