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