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