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