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