]> git.lizzy.rs Git - dragonfireclient.git/blob - src/environment.cpp
f5f20d0e5e0b7832889e9c60f0013bb92058735c
[dragonfireclient.git] / src / environment.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "environment.h"
21 #include "filesys.h"
22 #include "porting.h"
23 #include "collision.h"
24
25
26 Environment::Environment():
27         m_time_of_day(9000)
28 {
29 }
30
31 Environment::~Environment()
32 {
33         // Deallocate players
34         for(core::list<Player*>::Iterator i = m_players.begin();
35                         i != m_players.end(); i++)
36         {
37                 delete (*i);
38         }
39 }
40
41 void Environment::addPlayer(Player *player)
42 {
43         DSTACK(__FUNCTION_NAME);
44         /*
45                 Check that peer_ids are unique.
46                 Also check that names are unique.
47                 Exception: there can be multiple players with peer_id=0
48         */
49         // If peer id is non-zero, it has to be unique.
50         if(player->peer_id != 0)
51                 assert(getPlayer(player->peer_id) == NULL);
52         // Name has to be unique.
53         assert(getPlayer(player->getName()) == NULL);
54         // Add.
55         m_players.push_back(player);
56 }
57
58 void Environment::removePlayer(u16 peer_id)
59 {
60         DSTACK(__FUNCTION_NAME);
61 re_search:
62         for(core::list<Player*>::Iterator i = m_players.begin();
63                         i != m_players.end(); i++)
64         {
65                 Player *player = *i;
66                 if(player->peer_id != peer_id)
67                         continue;
68                 
69                 delete player;
70                 m_players.erase(i);
71                 // See if there is an another one
72                 // (shouldn't be, but just to be sure)
73                 goto re_search;
74         }
75 }
76
77 Player * Environment::getPlayer(u16 peer_id)
78 {
79         for(core::list<Player*>::Iterator i = m_players.begin();
80                         i != m_players.end(); i++)
81         {
82                 Player *player = *i;
83                 if(player->peer_id == peer_id)
84                         return player;
85         }
86         return NULL;
87 }
88
89 Player * Environment::getPlayer(const char *name)
90 {
91         for(core::list<Player*>::Iterator i = m_players.begin();
92                         i != m_players.end(); i++)
93         {
94                 Player *player = *i;
95                 if(strcmp(player->getName(), name) == 0)
96                         return player;
97         }
98         return NULL;
99 }
100
101 Player * Environment::getRandomConnectedPlayer()
102 {
103         core::list<Player*> connected_players = getPlayers(true);
104         u32 chosen_one = myrand() % connected_players.size();
105         u32 j = 0;
106         for(core::list<Player*>::Iterator
107                         i = connected_players.begin();
108                         i != connected_players.end(); i++)
109         {
110                 if(j == chosen_one)
111                 {
112                         Player *player = *i;
113                         return player;
114                 }
115                 j++;
116         }
117         return NULL;
118 }
119
120 Player * Environment::getNearestConnectedPlayer(v3f pos)
121 {
122         core::list<Player*> connected_players = getPlayers(true);
123         f32 nearest_d = 0;
124         Player *nearest_player = NULL;
125         for(core::list<Player*>::Iterator
126                         i = connected_players.begin();
127                         i != connected_players.end(); i++)
128         {
129                 Player *player = *i;
130                 f32 d = player->getPosition().getDistanceFrom(pos);
131                 if(d < nearest_d || nearest_player == NULL)
132                 {
133                         nearest_d = d;
134                         nearest_player = player;
135                 }
136         }
137         return nearest_player;
138 }
139
140 core::list<Player*> Environment::getPlayers()
141 {
142         return m_players;
143 }
144
145 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
146 {
147         core::list<Player*> newlist;
148         for(core::list<Player*>::Iterator
149                         i = m_players.begin();
150                         i != m_players.end(); i++)
151         {
152                 Player *player = *i;
153                 
154                 if(ignore_disconnected)
155                 {
156                         // Ignore disconnected players
157                         if(player->peer_id == 0)
158                                 continue;
159                 }
160
161                 newlist.push_back(player);
162         }
163         return newlist;
164 }
165
166 void Environment::printPlayers(std::ostream &o)
167 {
168         o<<"Players in environment:"<<std::endl;
169         for(core::list<Player*>::Iterator i = m_players.begin();
170                         i != m_players.end(); i++)
171         {
172                 Player *player = *i;
173                 o<<"Player peer_id="<<player->peer_id<<std::endl;
174         }
175 }
176
177 /*void Environment::setDayNightRatio(u32 r)
178 {
179         getDayNightRatio() = r;
180 }*/
181
182 u32 Environment::getDayNightRatio()
183 {
184         //return getDayNightRatio();
185         return time_to_daynight_ratio(m_time_of_day);
186 }
187
188 /*
189         ActiveBlockList
190 */
191
192 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
193 {
194         v3s16 p;
195         for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
196         for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
197         for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
198         {
199                 // Set in list
200                 list[p] = true;
201         }
202 }
203
204 void ActiveBlockList::update(core::list<v3s16> &active_positions,
205                 s16 radius,
206                 core::map<v3s16, bool> &blocks_removed,
207                 core::map<v3s16, bool> &blocks_added)
208 {
209         /*
210                 Create the new list
211         */
212         core::map<v3s16, bool> newlist;
213         for(core::list<v3s16>::Iterator i = active_positions.begin();
214                         i != active_positions.end(); i++)
215         {
216                 fillRadiusBlock(*i, radius, newlist);
217         }
218
219         /*
220                 Find out which blocks on the old list are not on the new list
221         */
222         // Go through old list
223         for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
224                         i.atEnd()==false; i++)
225         {
226                 v3s16 p = i.getNode()->getKey();
227                 // If not on new list, it's been removed
228                 if(newlist.find(p) == NULL)
229                         blocks_removed.insert(p, true);
230         }
231
232         /*
233                 Find out which blocks on the new list are not on the old list
234         */
235         // Go through new list
236         for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
237                         i.atEnd()==false; i++)
238         {
239                 v3s16 p = i.getNode()->getKey();
240                 // If not on old list, it's been added
241                 if(m_list.find(p) == NULL)
242                         blocks_added.insert(p, true);
243         }
244
245         /*
246                 Update m_list
247         */
248         m_list.clear();
249         for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
250                         i.atEnd()==false; i++)
251         {
252                 v3s16 p = i.getNode()->getKey();
253                 m_list.insert(p, true);
254         }
255 }
256
257 /*
258         ServerEnvironment
259 */
260
261 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
262         m_map(map),
263         m_server(server),
264         m_random_spawn_timer(3),
265         m_send_recommended_timer(0),
266         m_game_time(0),
267         m_game_time_fraction_counter(0)
268 {
269 }
270
271 ServerEnvironment::~ServerEnvironment()
272 {
273         // Clear active block list.
274         // This makes the next one delete all active objects.
275         m_active_blocks.clear();
276
277         // Convert all objects to static and delete the active objects
278         deactivateFarObjects(true);
279
280         // Drop/delete map
281         m_map->drop();
282 }
283
284 void ServerEnvironment::serializePlayers(const std::string &savedir)
285 {
286         std::string players_path = savedir + "/players";
287         fs::CreateDir(players_path);
288
289         core::map<Player*, bool> saved_players;
290
291         std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
292         for(u32 i=0; i<player_files.size(); i++)
293         {
294                 if(player_files[i].dir)
295                         continue;
296                 
297                 // Full path to this file
298                 std::string path = players_path + "/" + player_files[i].name;
299
300                 //dstream<<"Checking player file "<<path<<std::endl;
301
302                 // Load player to see what is its name
303                 ServerRemotePlayer testplayer;
304                 {
305                         // Open file and deserialize
306                         std::ifstream is(path.c_str(), std::ios_base::binary);
307                         if(is.good() == false)
308                         {
309                                 dstream<<"Failed to read "<<path<<std::endl;
310                                 continue;
311                         }
312                         testplayer.deSerialize(is);
313                 }
314
315                 //dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
316                 
317                 // Search for the player
318                 std::string playername = testplayer.getName();
319                 Player *player = getPlayer(playername.c_str());
320                 if(player == NULL)
321                 {
322                         dstream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
323                         continue;
324                 }
325
326                 //dstream<<"Found matching player, overwriting."<<std::endl;
327
328                 // OK, found. Save player there.
329                 {
330                         // Open file and serialize
331                         std::ofstream os(path.c_str(), std::ios_base::binary);
332                         if(os.good() == false)
333                         {
334                                 dstream<<"Failed to overwrite "<<path<<std::endl;
335                                 continue;
336                         }
337                         player->serialize(os);
338                         saved_players.insert(player, true);
339                 }
340         }
341
342         for(core::list<Player*>::Iterator i = m_players.begin();
343                         i != m_players.end(); i++)
344         {
345                 Player *player = *i;
346                 if(saved_players.find(player) != NULL)
347                 {
348                         /*dstream<<"Player "<<player->getName()
349                                         <<" was already saved."<<std::endl;*/
350                         continue;
351                 }
352                 std::string playername = player->getName();
353                 // Don't save unnamed player
354                 if(playername == "")
355                 {
356                         //dstream<<"Not saving unnamed player."<<std::endl;
357                         continue;
358                 }
359                 /*
360                         Find a sane filename
361                 */
362                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
363                         playername = "player";
364                 std::string path = players_path + "/" + playername;
365                 bool found = false;
366                 for(u32 i=0; i<1000; i++)
367                 {
368                         if(fs::PathExists(path) == false)
369                         {
370                                 found = true;
371                                 break;
372                         }
373                         path = players_path + "/" + playername + itos(i);
374                 }
375                 if(found == false)
376                 {
377                         dstream<<"WARNING: Didn't find free file for player"<<std::endl;
378                         continue;
379                 }
380
381                 {
382                         /*dstream<<"Saving player "<<player->getName()<<" to "
383                                         <<path<<std::endl;*/
384                         // Open file and serialize
385                         std::ofstream os(path.c_str(), std::ios_base::binary);
386                         if(os.good() == false)
387                         {
388                                 dstream<<"WARNING: Failed to overwrite "<<path<<std::endl;
389                                 continue;
390                         }
391                         player->serialize(os);
392                         saved_players.insert(player, true);
393                 }
394         }
395
396         //dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
397 }
398
399 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
400 {
401         std::string players_path = savedir + "/players";
402
403         core::map<Player*, bool> saved_players;
404
405         std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
406         for(u32 i=0; i<player_files.size(); i++)
407         {
408                 if(player_files[i].dir)
409                         continue;
410                 
411                 // Full path to this file
412                 std::string path = players_path + "/" + player_files[i].name;
413
414                 dstream<<"Checking player file "<<path<<std::endl;
415
416                 // Load player to see what is its name
417                 ServerRemotePlayer testplayer;
418                 {
419                         // Open file and deserialize
420                         std::ifstream is(path.c_str(), std::ios_base::binary);
421                         if(is.good() == false)
422                         {
423                                 dstream<<"Failed to read "<<path<<std::endl;
424                                 continue;
425                         }
426                         testplayer.deSerialize(is);
427                 }
428
429                 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
430                 {
431                         dstream<<"Not loading player with invalid name: "
432                                         <<testplayer.getName()<<std::endl;
433                 }
434
435                 dstream<<"Loaded test player with name "<<testplayer.getName()
436                                 <<std::endl;
437                 
438                 // Search for the player
439                 std::string playername = testplayer.getName();
440                 Player *player = getPlayer(playername.c_str());
441                 bool newplayer = false;
442                 if(player == NULL)
443                 {
444                         dstream<<"Is a new player"<<std::endl;
445                         player = new ServerRemotePlayer();
446                         newplayer = true;
447                 }
448
449                 // Load player
450                 {
451                         dstream<<"Reading player "<<testplayer.getName()<<" from "
452                                         <<path<<std::endl;
453                         // Open file and deserialize
454                         std::ifstream is(path.c_str(), std::ios_base::binary);
455                         if(is.good() == false)
456                         {
457                                 dstream<<"Failed to read "<<path<<std::endl;
458                                 continue;
459                         }
460                         player->deSerialize(is);
461                 }
462
463                 if(newplayer)
464                         addPlayer(player);
465         }
466 }
467
468 void ServerEnvironment::saveMeta(const std::string &savedir)
469 {
470         std::string path = savedir + "/env_meta.txt";
471
472         // Open file and serialize
473         std::ofstream os(path.c_str(), std::ios_base::binary);
474         if(os.good() == false)
475         {
476                 dstream<<"WARNING: ServerEnvironment::saveMeta(): Failed to open "
477                                 <<path<<std::endl;
478                 throw SerializationError("Couldn't save env meta");
479         }
480
481         Settings args;
482         args.setU64("game_time", m_game_time);
483         args.setU64("time_of_day", getTimeOfDay());
484         args.writeLines(os);
485         os<<"EnvArgsEnd\n";
486 }
487
488 void ServerEnvironment::loadMeta(const std::string &savedir)
489 {
490         std::string path = savedir + "/env_meta.txt";
491
492         // Open file and deserialize
493         std::ifstream is(path.c_str(), std::ios_base::binary);
494         if(is.good() == false)
495         {
496                 dstream<<"WARNING: ServerEnvironment::loadMeta(): Failed to open "
497                                 <<path<<std::endl;
498                 throw SerializationError("Couldn't load env meta");
499         }
500
501         Settings args;
502         
503         for(;;)
504         {
505                 if(is.eof())
506                         throw SerializationError
507                                         ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
508                 std::string line;
509                 std::getline(is, line);
510                 std::string trimmedline = trim(line);
511                 if(trimmedline == "EnvArgsEnd")
512                         break;
513                 args.parseConfigLine(line);
514         }
515         
516         try{
517                 m_game_time = args.getU64("game_time");
518         }catch(SettingNotFoundException &e){
519                 // Getting this is crucial, otherwise timestamps are useless
520                 throw SerializationError("Couldn't load env meta game_time");
521         }
522
523         try{
524                 m_time_of_day = args.getU64("time_of_day");
525         }catch(SettingNotFoundException &e){
526                 // This is not as important
527                 m_time_of_day = 9000;
528         }
529 }
530
531 #if 0
532 // This is probably very useless
533 void spawnRandomObjects(MapBlock *block)
534 {
535         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
536         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
537         {
538                 bool last_node_walkable = false;
539                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
540                 {
541                         v3s16 p(x0,y0,z0);
542                         MapNode n = block->getNodeNoEx(p);
543                         if(n.d == CONTENT_IGNORE)
544                                 continue;
545                         if(content_features(n.d).liquid_type != LIQUID_NONE)
546                                 continue;
547                         if(content_features(n.d).walkable)
548                         {
549                                 last_node_walkable = true;
550                                 continue;
551                         }
552                         if(last_node_walkable)
553                         {
554                                 // If block contains light information
555                                 if(content_features(n.d).param_type == CPT_LIGHT)
556                                 {
557                                         if(n.getLight(LIGHTBANK_DAY) <= 5)
558                                         {
559                                                 if(myrand() % 1000 == 0)
560                                                 {
561                                                         v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
562                                                         pos_f.Y -= BS*0.4;
563                                                         ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
564                                                         std::string data = obj->getStaticData();
565                                                         StaticObject s_obj(obj->getType(),
566                                                                         obj->getBasePosition(), data);
567                                                         // Add one
568                                                         block->m_static_objects.insert(0, s_obj);
569                                                         delete obj;
570                                                         block->setChangedFlag();
571                                                 }
572                                         }
573                                 }
574                         }
575                         last_node_walkable = false;
576                 }
577         }
578 }
579 #endif
580
581 void ServerEnvironment::step(float dtime)
582 {
583         DSTACK(__FUNCTION_NAME);
584         
585         //TimeTaker timer("ServerEnv step");
586
587         // Get some settings
588         bool footprints = g_settings.getBool("footprints");
589
590         /*
591                 Increment game time
592         */
593         {
594                 m_game_time_fraction_counter += dtime;
595                 u32 inc_i = (u32)m_game_time_fraction_counter;
596                 m_game_time += inc_i;
597                 m_game_time_fraction_counter -= (float)inc_i;
598         }
599         
600         /*
601                 Let map update it's timers
602         */
603         {
604                 //TimeTaker timer("Server m_map->timerUpdate()");
605                 m_map->timerUpdate(dtime);
606         }
607
608         /*
609                 Handle players
610         */
611         for(core::list<Player*>::Iterator i = m_players.begin();
612                         i != m_players.end(); i++)
613         {
614                 Player *player = *i;
615                 
616                 // Ignore disconnected players
617                 if(player->peer_id == 0)
618                         continue;
619
620                 v3f playerpos = player->getPosition();
621                 
622                 // Move
623                 player->move(dtime, *m_map, 100*BS);
624                 
625                 /*
626                         Add footsteps to grass
627                 */
628                 if(footprints)
629                 {
630                         // Get node that is at BS/4 under player
631                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
632                         try{
633                                 MapNode n = m_map->getNode(bottompos);
634                                 if(n.d == CONTENT_GRASS)
635                                 {
636                                         n.d = CONTENT_GRASS_FOOTSTEPS;
637                                         m_map->setNode(bottompos, n);
638                                 }
639                         }
640                         catch(InvalidPositionException &e)
641                         {
642                         }
643                 }
644         }
645
646         /*
647                 Manage active block list
648         */
649         if(m_active_blocks_management_interval.step(dtime, 2.0))
650         {
651                 /*
652                         Get player block positions
653                 */
654                 core::list<v3s16> players_blockpos;
655                 for(core::list<Player*>::Iterator
656                                 i = m_players.begin();
657                                 i != m_players.end(); i++)
658                 {
659                         Player *player = *i;
660                         // Ignore disconnected players
661                         if(player->peer_id == 0)
662                                 continue;
663                         v3s16 blockpos = getNodeBlockPos(
664                                         floatToInt(player->getPosition(), BS));
665                         players_blockpos.push_back(blockpos);
666                 }
667                 
668                 /*
669                         Update list of active blocks, collecting changes
670                 */
671                 const s16 active_block_range = 5;
672                 core::map<v3s16, bool> blocks_removed;
673                 core::map<v3s16, bool> blocks_added;
674                 m_active_blocks.update(players_blockpos, active_block_range,
675                                 blocks_removed, blocks_added);
676
677                 /*
678                         Handle removed blocks
679                 */
680
681                 // Convert active objects that are no more in active blocks to static
682                 deactivateFarObjects(false);
683                 
684                 for(core::map<v3s16, bool>::Iterator
685                                 i = blocks_removed.getIterator();
686                                 i.atEnd()==false; i++)
687                 {
688                         v3s16 p = i.getNode()->getKey();
689
690                         /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
691                                         <<") became inactive"<<std::endl;*/
692                         
693                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
694                         if(block==NULL)
695                                 continue;
696                         
697                         // Set current time as timestamp
698                         block->setTimestamp(m_game_time);
699                 }
700
701                 /*
702                         Handle added blocks
703                 */
704
705                 for(core::map<v3s16, bool>::Iterator
706                                 i = blocks_added.getIterator();
707                                 i.atEnd()==false; i++)
708                 {
709                         v3s16 p = i.getNode()->getKey();
710                         
711                         /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
712                                         <<") became active"<<std::endl;*/
713
714                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
715                         if(block==NULL)
716                                 continue;
717                         
718                         // Get time difference
719                         u32 dtime_s = 0;
720                         u32 stamp = block->getTimestamp();
721                         if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
722                                 dtime_s = m_game_time - block->getTimestamp();
723
724                         // Set current time as timestamp
725                         block->setTimestamp(m_game_time);
726
727                         //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
728                         
729                         // Activate stored objects
730                         activateObjects(block);
731
732                         // Run node metadata
733                         bool changed = block->m_node_metadata.step((float)dtime_s);
734                         if(changed)
735                         {
736                                 MapEditEvent event;
737                                 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
738                                 event.p = p;
739                                 m_map->dispatchEvent(&event);
740                         }
741
742                         // TODO: Do something
743                         // TODO: Implement usage of ActiveBlockModifier
744                         
745                         // Here's a quick demonstration
746                         v3s16 p0;
747                         for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
748                         for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
749                         for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
750                         {
751                                 v3s16 p = p0 + block->getPosRelative();
752                                 MapNode n = block->getNodeNoEx(p0);
753                                 // Test something:
754                                 // Convert all mud under proper day lighting to grass
755                                 if(n.d == CONTENT_MUD)
756                                 {
757                                         if(dtime_s > 300)
758                                         {
759                                                 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
760                                                 if(content_features(n_top.d).air_equivalent &&
761                                                                 n_top.getLight(LIGHTBANK_DAY) >= 13)
762                                                 {
763                                                         n.d = CONTENT_GRASS;
764                                                         m_map->addNodeWithEvent(p, n);
765                                                 }
766                                         }
767                                 }
768                         }
769                 }
770         }
771
772         /*
773                 Mess around in active blocks
774         */
775         if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
776         {
777                 float dtime = 1.0;
778
779                 for(core::map<v3s16, bool>::Iterator
780                                 i = m_active_blocks.m_list.getIterator();
781                                 i.atEnd()==false; i++)
782                 {
783                         v3s16 p = i.getNode()->getKey();
784                         
785                         /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
786                                         <<") being handled"<<std::endl;*/
787
788                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
789                         if(block==NULL)
790                                 continue;
791                         
792                         // Set current time as timestamp
793                         block->setTimestamp(m_game_time);
794
795                         // Run node metadata
796                         bool changed = block->m_node_metadata.step(dtime);
797                         if(changed)
798                         {
799                                 MapEditEvent event;
800                                 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
801                                 event.p = p;
802                                 m_map->dispatchEvent(&event);
803                         }
804                 }
805         }
806         if(m_active_blocks_test_interval.step(dtime, 10.0))
807         {
808                 //float dtime = 10.0;
809
810                 for(core::map<v3s16, bool>::Iterator
811                                 i = m_active_blocks.m_list.getIterator();
812                                 i.atEnd()==false; i++)
813                 {
814                         v3s16 p = i.getNode()->getKey();
815                         
816                         /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
817                                         <<") being handled"<<std::endl;*/
818
819                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
820                         if(block==NULL)
821                                 continue;
822                         
823                         // Set current time as timestamp
824                         block->setTimestamp(m_game_time);
825
826                         /*
827                                 Do stuff!
828
829                                 Note that map modifications should be done using the event-
830                                 making map methods so that the server gets information
831                                 about them.
832
833                                 Reading can be done quickly directly from the block.
834
835                                 Everything should bind to inside this single content
836                                 searching loop to keep things fast.
837                         */
838                         // TODO: Implement usage of ActiveBlockModifier
839
840                         v3s16 p0;
841                         for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
842                         for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
843                         for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
844                         {
845                                 v3s16 p = p0 + block->getPosRelative();
846                                 MapNode n = block->getNodeNoEx(p0);
847
848                                 /*
849                                         Test something:
850                                         Convert mud under proper lighting to grass
851                                 */
852                                 if(n.d == CONTENT_MUD)
853                                 {
854                                         if(myrand()%20 == 0)
855                                         {
856                                                 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
857                                                 if(content_features(n_top.d).air_equivalent &&
858                                                                 n_top.getLightBlend(getDayNightRatio()) >= 13)
859                                                 {
860                                                         n.d = CONTENT_GRASS;
861                                                         m_map->addNodeWithEvent(p, n);
862                                                 }
863                                         }
864                                 }
865                         }
866                 }
867         }
868         
869         /*
870                 Step active objects
871         */
872         {
873                 //TimeTaker timer("Step active objects");
874                 
875                 // This helps the objects to send data at the same time
876                 bool send_recommended = false;
877                 m_send_recommended_timer += dtime;
878                 if(m_send_recommended_timer > 0.15)
879                 {
880                         m_send_recommended_timer = 0;
881                         send_recommended = true;
882                 }
883
884                 for(core::map<u16, ServerActiveObject*>::Iterator
885                                 i = m_active_objects.getIterator();
886                                 i.atEnd()==false; i++)
887                 {
888                         ServerActiveObject* obj = i.getNode()->getValue();
889                         // Don't step if is to be removed or stored statically
890                         if(obj->m_removed || obj->m_pending_deactivation)
891                                 continue;
892                         // Step object, putting messages directly to the queue
893                         obj->step(dtime, m_active_object_messages, send_recommended);
894                 }
895         }
896         
897         /*
898                 Manage active objects
899         */
900         if(m_object_management_interval.step(dtime, 0.5))
901         {
902                 /*
903                         Remove objects that satisfy (m_removed && m_known_by_count==0)
904                 */
905                 removeRemovedObjects();
906         }
907
908         if(g_settings.getBool("enable_experimental"))
909         {
910
911         /*
912                 TEST CODE
913         */
914 #if 1
915         m_random_spawn_timer -= dtime;
916         if(m_random_spawn_timer < 0)
917         {
918                 //m_random_spawn_timer += myrand_range(2.0, 20.0);
919                 //m_random_spawn_timer += 2.0;
920                 m_random_spawn_timer += 200.0;
921
922                 /*
923                         Find some position
924                 */
925
926                 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
927                 s16 y = 1 + getServerMap().findGroundLevel(p2d);
928                 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
929                 
930                 Player *player = getRandomConnectedPlayer();
931                 v3f pos(0,0,0);
932                 if(player)
933                         pos = player->getPosition();
934                 pos += v3f(
935                         myrand_range(-3,3)*BS,
936                         0,
937                         myrand_range(-3,3)*BS
938                 );
939
940                 /*
941                         Create a ServerActiveObject
942                 */
943
944                 //TestSAO *obj = new TestSAO(this, 0, pos);
945                 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
946                 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
947                 ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
948                 addActiveObject(obj);
949         }
950 #endif
951
952         } // enable_experimental
953 }
954
955 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
956 {
957         core::map<u16, ServerActiveObject*>::Node *n;
958         n = m_active_objects.find(id);
959         if(n == NULL)
960                 return NULL;
961         return n->getValue();
962 }
963
964 bool isFreeServerActiveObjectId(u16 id,
965                 core::map<u16, ServerActiveObject*> &objects)
966 {
967         if(id == 0)
968                 return false;
969         
970         for(core::map<u16, ServerActiveObject*>::Iterator
971                         i = objects.getIterator();
972                         i.atEnd()==false; i++)
973         {
974                 if(i.getNode()->getKey() == id)
975                         return false;
976         }
977         return true;
978 }
979
980 u16 getFreeServerActiveObjectId(
981                 core::map<u16, ServerActiveObject*> &objects)
982 {
983         u16 new_id = 1;
984         for(;;)
985         {
986                 if(isFreeServerActiveObjectId(new_id, objects))
987                         return new_id;
988                 
989                 if(new_id == 65535)
990                         return 0;
991
992                 new_id++;
993         }
994 }
995
996 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
997 {
998         assert(object);
999         if(object->getId() == 0)
1000         {
1001                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1002                 if(new_id == 0)
1003                 {
1004                         dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
1005                                         <<"no free ids available"<<std::endl;
1006                         delete object;
1007                         return 0;
1008                 }
1009                 object->setId(new_id);
1010         }
1011         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1012         {
1013                 dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
1014                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1015                 delete object;
1016                 return 0;
1017         }
1018         /*dstream<<"INGO: ServerEnvironment::addActiveObject(): "
1019                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1020                         
1021         m_active_objects.insert(object->getId(), object);
1022
1023         // Add static object to active static list of the block
1024         v3f objectpos = object->getBasePosition();
1025         std::string staticdata = object->getStaticData();
1026         StaticObject s_obj(object->getType(), objectpos, staticdata);
1027         // Add to the block where the object is located in
1028         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1029         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1030         if(block)
1031         {
1032                 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1033                 object->m_static_exists = true;
1034                 object->m_static_block = blockpos;
1035         }
1036         else{
1037                 dstream<<"WARNING: Server: Could not find a block for "
1038                                 <<"storing newly added static active object"<<std::endl;
1039         }
1040
1041         return object->getId();
1042 }
1043
1044 /*
1045         Finds out what new objects have been added to
1046         inside a radius around a position
1047 */
1048 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1049                 core::map<u16, bool> &current_objects,
1050                 core::map<u16, bool> &added_objects)
1051 {
1052         v3f pos_f = intToFloat(pos, BS);
1053         f32 radius_f = radius * BS;
1054         /*
1055                 Go through the object list,
1056                 - discard m_removed objects,
1057                 - discard objects that are too far away,
1058                 - discard objects that are found in current_objects.
1059                 - add remaining objects to added_objects
1060         */
1061         for(core::map<u16, ServerActiveObject*>::Iterator
1062                         i = m_active_objects.getIterator();
1063                         i.atEnd()==false; i++)
1064         {
1065                 u16 id = i.getNode()->getKey();
1066                 // Get object
1067                 ServerActiveObject *object = i.getNode()->getValue();
1068                 if(object == NULL)
1069                         continue;
1070                 // Discard if removed
1071                 if(object->m_removed)
1072                         continue;
1073                 // Discard if too far
1074                 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1075                 if(distance_f > radius_f)
1076                         continue;
1077                 // Discard if already on current_objects
1078                 core::map<u16, bool>::Node *n;
1079                 n = current_objects.find(id);
1080                 if(n != NULL)
1081                         continue;
1082                 // Add to added_objects
1083                 added_objects.insert(id, false);
1084         }
1085 }
1086
1087 /*
1088         Finds out what objects have been removed from
1089         inside a radius around a position
1090 */
1091 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1092                 core::map<u16, bool> &current_objects,
1093                 core::map<u16, bool> &removed_objects)
1094 {
1095         v3f pos_f = intToFloat(pos, BS);
1096         f32 radius_f = radius * BS;
1097         /*
1098                 Go through current_objects; object is removed if:
1099                 - object is not found in m_active_objects (this is actually an
1100                   error condition; objects should be set m_removed=true and removed
1101                   only after all clients have been informed about removal), or
1102                 - object has m_removed=true, or
1103                 - object is too far away
1104         */
1105         for(core::map<u16, bool>::Iterator
1106                         i = current_objects.getIterator();
1107                         i.atEnd()==false; i++)
1108         {
1109                 u16 id = i.getNode()->getKey();
1110                 ServerActiveObject *object = getActiveObject(id);
1111                 if(object == NULL)
1112                 {
1113                         dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1114                                         <<" object in current_objects is NULL"<<std::endl;
1115                 }
1116                 else if(object->m_removed == false)
1117                 {
1118                         f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1119                         /*dstream<<"removed == false"
1120                                         <<"distance_f = "<<distance_f
1121                                         <<", radius_f = "<<radius_f<<std::endl;*/
1122                         if(distance_f < radius_f)
1123                         {
1124                                 // Not removed
1125                                 continue;
1126                         }
1127                 }
1128                 removed_objects.insert(id, false);
1129         }
1130 }
1131
1132 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1133 {
1134         if(m_active_object_messages.size() == 0)
1135                 return ActiveObjectMessage(0);
1136         
1137         return m_active_object_messages.pop_front();
1138 }
1139
1140 /*
1141         ************ Private methods *************
1142 */
1143
1144 /*
1145         Remove objects that satisfy (m_removed && m_known_by_count==0)
1146 */
1147 void ServerEnvironment::removeRemovedObjects()
1148 {
1149         core::list<u16> objects_to_remove;
1150         for(core::map<u16, ServerActiveObject*>::Iterator
1151                         i = m_active_objects.getIterator();
1152                         i.atEnd()==false; i++)
1153         {
1154                 u16 id = i.getNode()->getKey();
1155                 ServerActiveObject* obj = i.getNode()->getValue();
1156                 // This shouldn't happen but check it
1157                 if(obj == NULL)
1158                 {
1159                         dstream<<"WARNING: NULL object found in ServerEnvironment"
1160                                         <<" while finding removed objects. id="<<id<<std::endl;
1161                         // Id to be removed from m_active_objects
1162                         objects_to_remove.push_back(id);
1163                         continue;
1164                 }
1165
1166                 /*
1167                         We will delete objects that are marked as removed or thatare
1168                         waiting for deletion after deactivation
1169                 */
1170                 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1171                         continue;
1172
1173                 /*
1174                         Delete static data from block if is marked as removed
1175                 */
1176                 if(obj->m_static_exists && obj->m_removed)
1177                 {
1178                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1179                         if(block)
1180                         {
1181                                 block->m_static_objects.remove(id);
1182                                 block->setChangedFlag();
1183                         }
1184                 }
1185
1186                 // If m_known_by_count > 0, don't actually remove.
1187                 if(obj->m_known_by_count > 0)
1188                         continue;
1189                 
1190                 // Delete
1191                 delete obj;
1192                 // Id to be removed from m_active_objects
1193                 objects_to_remove.push_back(id);
1194         }
1195         // Remove references from m_active_objects
1196         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1197                         i != objects_to_remove.end(); i++)
1198         {
1199                 m_active_objects.remove(*i);
1200         }
1201 }
1202
1203 /*
1204         Convert stored objects from blocks near the players to active.
1205 */
1206 void ServerEnvironment::activateObjects(MapBlock *block)
1207 {
1208         if(block==NULL)
1209                 return;
1210         // Ignore if no stored objects (to not set changed flag)
1211         if(block->m_static_objects.m_stored.size() == 0)
1212                 return;
1213         // A list for objects that couldn't be converted to static for some
1214         // reason. They will be stored back.
1215         core::list<StaticObject> new_stored;
1216         // Loop through stored static objects
1217         for(core::list<StaticObject>::Iterator
1218                         i = block->m_static_objects.m_stored.begin();
1219                         i != block->m_static_objects.m_stored.end(); i++)
1220         {
1221                 /*dstream<<"INFO: Server: Creating an active object from "
1222                                 <<"static data"<<std::endl;*/
1223                 StaticObject &s_obj = *i;
1224                 // Create an active object from the data
1225                 ServerActiveObject *obj = ServerActiveObject::create
1226                                 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1227                 // If couldn't create object, store static data back.
1228                 if(obj==NULL)
1229                 {
1230                         new_stored.push_back(s_obj);
1231                         continue;
1232                 }
1233                 // This will also add the object to the active static list
1234                 addActiveObject(obj);
1235                 //u16 id = addActiveObject(obj);
1236         }
1237         // Clear stored list
1238         block->m_static_objects.m_stored.clear();
1239         // Add leftover failed stuff to stored list
1240         for(core::list<StaticObject>::Iterator
1241                         i = new_stored.begin();
1242                         i != new_stored.end(); i++)
1243         {
1244                 StaticObject &s_obj = *i;
1245                 block->m_static_objects.m_stored.push_back(s_obj);
1246         }
1247         // Block has been modified
1248         block->setChangedFlag();
1249 }
1250
1251 /*
1252         Convert objects that are not in active blocks to static.
1253
1254         If m_known_by_count != 0, active object is not deleted, but static
1255         data is still updated.
1256
1257         If force_delete is set, active object is deleted nevertheless. It
1258         shall only be set so in the destructor of the environment.
1259 */
1260 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1261 {
1262         core::list<u16> objects_to_remove;
1263         for(core::map<u16, ServerActiveObject*>::Iterator
1264                         i = m_active_objects.getIterator();
1265                         i.atEnd()==false; i++)
1266         {
1267                 ServerActiveObject* obj = i.getNode()->getValue();
1268                 u16 id = i.getNode()->getKey();
1269                 v3f objectpos = obj->getBasePosition();
1270
1271                 // This shouldn't happen but check it
1272                 if(obj == NULL)
1273                 {
1274                         dstream<<"WARNING: NULL object found in ServerEnvironment"
1275                                         <<std::endl;
1276                         assert(0);
1277                         continue;
1278                 }
1279
1280                 // The block in which the object resides in
1281                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1282                 
1283                 // If block is active, don't remove
1284                 if(m_active_blocks.contains(blockpos_o))
1285                         continue;
1286
1287                 /*
1288                         Update the static data
1289                 */
1290
1291                 // Delete old static object
1292                 MapBlock *oldblock = NULL;
1293                 if(obj->m_static_exists)
1294                 {
1295                         MapBlock *block = m_map->getBlockNoCreateNoEx
1296                                         (obj->m_static_block);
1297                         if(block)
1298                         {
1299                                 block->m_static_objects.remove(id);
1300                                 oldblock = block;
1301                         }
1302                 }
1303                 // Create new static object
1304                 std::string staticdata = obj->getStaticData();
1305                 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1306                 // Add to the block where the object is located in
1307                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1308                 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1309                 if(block)
1310                 {
1311                         block->m_static_objects.insert(0, s_obj);
1312                         block->setChangedFlag();
1313                         obj->m_static_exists = true;
1314                         obj->m_static_block = block->getPos();
1315                 }
1316                 // If not possible, add back to previous block
1317                 else if(oldblock)
1318                 {
1319                         oldblock->m_static_objects.insert(0, s_obj);
1320                         oldblock->setChangedFlag();
1321                         obj->m_static_exists = true;
1322                         obj->m_static_block = oldblock->getPos();
1323                 }
1324                 else{
1325                         dstream<<"WARNING: Server: Could not find a block for "
1326                                         <<"storing static object"<<std::endl;
1327                         obj->m_static_exists = false;
1328                         continue;
1329                 }
1330
1331                 /*
1332                         Delete active object if not known by some client,
1333                         else set pending deactivation
1334                 */
1335
1336                 // If known by some client, don't delete.
1337                 if(obj->m_known_by_count > 0 && force_delete == false)
1338                 {
1339                         obj->m_pending_deactivation = true;
1340                         continue;
1341                 }
1342                 
1343                 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1344                                 <<std::endl;*/
1345                 // Delete active object
1346                 delete obj;
1347                 // Id to be removed from m_active_objects
1348                 objects_to_remove.push_back(id);
1349         }
1350
1351         // Remove references from m_active_objects
1352         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1353                         i != objects_to_remove.end(); i++)
1354         {
1355                 m_active_objects.remove(*i);
1356         }
1357 }
1358
1359
1360 #ifndef SERVER
1361
1362 /*
1363         ClientEnvironment
1364 */
1365
1366 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1367         m_map(map),
1368         m_smgr(smgr)
1369 {
1370         assert(m_map);
1371         assert(m_smgr);
1372 }
1373
1374 ClientEnvironment::~ClientEnvironment()
1375 {
1376         // delete active objects
1377         for(core::map<u16, ClientActiveObject*>::Iterator
1378                         i = m_active_objects.getIterator();
1379                         i.atEnd()==false; i++)
1380         {
1381                 delete i.getNode()->getValue();
1382         }
1383
1384         // Drop/delete map
1385         m_map->drop();
1386 }
1387
1388 void ClientEnvironment::addPlayer(Player *player)
1389 {
1390         DSTACK(__FUNCTION_NAME);
1391         /*
1392                 It is a failure if player is local and there already is a local
1393                 player
1394         */
1395         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1396
1397         Environment::addPlayer(player);
1398 }
1399
1400 LocalPlayer * ClientEnvironment::getLocalPlayer()
1401 {
1402         for(core::list<Player*>::Iterator i = m_players.begin();
1403                         i != m_players.end(); i++)
1404         {
1405                 Player *player = *i;
1406                 if(player->isLocal())
1407                         return (LocalPlayer*)player;
1408         }
1409         return NULL;
1410 }
1411
1412 void ClientEnvironment::step(float dtime)
1413 {
1414         DSTACK(__FUNCTION_NAME);
1415
1416         // Get some settings
1417         bool free_move = g_settings.getBool("free_move");
1418         bool footprints = g_settings.getBool("footprints");
1419
1420         {
1421                 //TimeTaker timer("Client m_map->timerUpdate()");
1422                 m_map->timerUpdate(dtime);
1423         }
1424         
1425         // Get local player
1426         LocalPlayer *lplayer = getLocalPlayer();
1427         assert(lplayer);
1428         // collision info queue
1429         core::list<CollisionInfo> player_collisions;
1430         
1431         /*
1432                 Get the speed the player is going
1433         */
1434         f32 player_speed = 0.001; // just some small value
1435         player_speed = lplayer->getSpeed().getLength();
1436         
1437         /*
1438                 Maximum position increment
1439         */
1440         //f32 position_max_increment = 0.05*BS;
1441         f32 position_max_increment = 0.1*BS;
1442
1443         // Maximum time increment (for collision detection etc)
1444         // time = distance / speed
1445         f32 dtime_max_increment = position_max_increment / player_speed;
1446         
1447         // Maximum time increment is 10ms or lower
1448         if(dtime_max_increment > 0.01)
1449                 dtime_max_increment = 0.01;
1450         
1451         // Don't allow overly huge dtime
1452         if(dtime > 0.5)
1453                 dtime = 0.5;
1454         
1455         f32 dtime_downcount = dtime;
1456
1457         /*
1458                 Stuff that has a maximum time increment
1459         */
1460
1461         u32 loopcount = 0;
1462         do
1463         {
1464                 loopcount++;
1465
1466                 f32 dtime_part;
1467                 if(dtime_downcount > dtime_max_increment)
1468                 {
1469                         dtime_part = dtime_max_increment;
1470                         dtime_downcount -= dtime_part;
1471                 }
1472                 else
1473                 {
1474                         dtime_part = dtime_downcount;
1475                         /*
1476                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
1477                                 when dtime_part is so small that dtime_downcount -= dtime_part
1478                                 does nothing
1479                         */
1480                         dtime_downcount = 0;
1481                 }
1482                 
1483                 /*
1484                         Handle local player
1485                 */
1486                 
1487                 {
1488                         v3f lplayerpos = lplayer->getPosition();
1489                         
1490                         // Apply physics
1491                         if(free_move == false)
1492                         {
1493                                 // Gravity
1494                                 v3f speed = lplayer->getSpeed();
1495                                 if(lplayer->swimming_up == false)
1496                                         speed.Y -= 9.81 * BS * dtime_part * 2;
1497
1498                                 // Water resistance
1499                                 if(lplayer->in_water_stable || lplayer->in_water)
1500                                 {
1501                                         f32 max_down = 2.0*BS;
1502                                         if(speed.Y < -max_down) speed.Y = -max_down;
1503
1504                                         f32 max = 2.5*BS;
1505                                         if(speed.getLength() > max)
1506                                         {
1507                                                 speed = speed / speed.getLength() * max;
1508                                         }
1509                                 }
1510
1511                                 lplayer->setSpeed(speed);
1512                         }
1513
1514                         /*
1515                                 Move the lplayer.
1516                                 This also does collision detection.
1517                         */
1518                         lplayer->move(dtime_part, *m_map, position_max_increment,
1519                                         &player_collisions);
1520                 }
1521         }
1522         while(dtime_downcount > 0.001);
1523                 
1524         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1525
1526         for(core::list<CollisionInfo>::Iterator
1527                         i = player_collisions.begin();
1528                         i != player_collisions.end(); i++)
1529         {
1530                 CollisionInfo &info = *i;
1531                 if(info.t == COLLISION_FALL)
1532                 {
1533                         //f32 tolerance = BS*10; // 2 without damage
1534                         f32 tolerance = BS*12; // 3 without damage
1535                         f32 factor = 1;
1536                         if(info.speed > tolerance)
1537                         {
1538                                 f32 damage_f = (info.speed - tolerance)/BS*factor;
1539                                 u16 damage = (u16)(damage_f+0.5);
1540                                 if(lplayer->hp > damage)
1541                                         lplayer->hp -= damage;
1542                                 else
1543                                         lplayer->hp = 0;
1544
1545                                 ClientEnvEvent event;
1546                                 event.type = CEE_PLAYER_DAMAGE;
1547                                 event.player_damage.amount = damage;
1548                                 m_client_event_queue.push_back(event);
1549                         }
1550                 }
1551         }
1552         
1553         /*
1554                 Stuff that can be done in an arbitarily large dtime
1555         */
1556         for(core::list<Player*>::Iterator i = m_players.begin();
1557                         i != m_players.end(); i++)
1558         {
1559                 Player *player = *i;
1560                 v3f playerpos = player->getPosition();
1561                 
1562                 /*
1563                         Handle non-local players
1564                 */
1565                 if(player->isLocal() == false)
1566                 {
1567                         // Move
1568                         player->move(dtime, *m_map, 100*BS);
1569
1570                         // Update lighting on remote players on client
1571                         u8 light = LIGHT_MAX;
1572                         try{
1573                                 // Get node at head
1574                                 v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
1575                                 MapNode n = m_map->getNode(p);
1576                                 light = n.getLightBlend(getDayNightRatio());
1577                         }
1578                         catch(InvalidPositionException &e) {}
1579                         player->updateLight(light);
1580                 }
1581                 
1582                 /*
1583                         Add footsteps to grass
1584                 */
1585                 if(footprints)
1586                 {
1587                         // Get node that is at BS/4 under player
1588                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1589                         try{
1590                                 MapNode n = m_map->getNode(bottompos);
1591                                 if(n.d == CONTENT_GRASS)
1592                                 {
1593                                         n.d = CONTENT_GRASS_FOOTSTEPS;
1594                                         m_map->setNode(bottompos, n);
1595                                         // Update mesh on client
1596                                         if(m_map->mapType() == MAPTYPE_CLIENT)
1597                                         {
1598                                                 v3s16 p_blocks = getNodeBlockPos(bottompos);
1599                                                 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1600                                                 //b->updateMesh(getDayNightRatio());
1601                                                 b->setMeshExpired(true);
1602                                         }
1603                                 }
1604                         }
1605                         catch(InvalidPositionException &e)
1606                         {
1607                         }
1608                 }
1609         }
1610         
1611         /*
1612                 Step active objects and update lighting of them
1613         */
1614         
1615         for(core::map<u16, ClientActiveObject*>::Iterator
1616                         i = m_active_objects.getIterator();
1617                         i.atEnd()==false; i++)
1618         {
1619                 ClientActiveObject* obj = i.getNode()->getValue();
1620                 // Step object
1621                 obj->step(dtime, this);
1622                 // Update lighting
1623                 //u8 light = LIGHT_MAX;
1624                 u8 light = 0;
1625                 try{
1626                         // Get node at head
1627                         v3s16 p = obj->getLightPosition();
1628                         MapNode n = m_map->getNode(p);
1629                         light = n.getLightBlend(getDayNightRatio());
1630                 }
1631                 catch(InvalidPositionException &e) {}
1632                 obj->updateLight(light);
1633         }
1634 }
1635
1636 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1637 {
1638         m_map->updateMeshes(blockpos, getDayNightRatio());
1639 }
1640
1641 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1642 {
1643         m_map->expireMeshes(only_daynight_diffed);
1644 }
1645
1646 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1647 {
1648         core::map<u16, ClientActiveObject*>::Node *n;
1649         n = m_active_objects.find(id);
1650         if(n == NULL)
1651                 return NULL;
1652         return n->getValue();
1653 }
1654
1655 bool isFreeClientActiveObjectId(u16 id,
1656                 core::map<u16, ClientActiveObject*> &objects)
1657 {
1658         if(id == 0)
1659                 return false;
1660         
1661         for(core::map<u16, ClientActiveObject*>::Iterator
1662                         i = objects.getIterator();
1663                         i.atEnd()==false; i++)
1664         {
1665                 if(i.getNode()->getKey() == id)
1666                         return false;
1667         }
1668         return true;
1669 }
1670
1671 u16 getFreeClientActiveObjectId(
1672                 core::map<u16, ClientActiveObject*> &objects)
1673 {
1674         u16 new_id = 1;
1675         for(;;)
1676         {
1677                 if(isFreeClientActiveObjectId(new_id, objects))
1678                         return new_id;
1679                 
1680                 if(new_id == 65535)
1681                         return 0;
1682
1683                 new_id++;
1684         }
1685 }
1686
1687 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1688 {
1689         assert(object);
1690         if(object->getId() == 0)
1691         {
1692                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1693                 if(new_id == 0)
1694                 {
1695                         dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1696                                         <<"no free ids available"<<std::endl;
1697                         delete object;
1698                         return 0;
1699                 }
1700                 object->setId(new_id);
1701         }
1702         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1703         {
1704                 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1705                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1706                 delete object;
1707                 return 0;
1708         }
1709         dstream<<"INGO: ClientEnvironment::addActiveObject(): "
1710                         <<"added (id="<<object->getId()<<")"<<std::endl;
1711         m_active_objects.insert(object->getId(), object);
1712         object->addToScene(m_smgr);
1713         return object->getId();
1714 }
1715
1716 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1717                 const std::string &init_data)
1718 {
1719         ClientActiveObject* obj = ClientActiveObject::create(type);
1720         if(obj == NULL)
1721         {
1722                 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1723                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1724                                 <<std::endl;
1725                 return;
1726         }
1727         
1728         obj->setId(id);
1729
1730         addActiveObject(obj);
1731
1732         obj->initialize(init_data);
1733 }
1734
1735 void ClientEnvironment::removeActiveObject(u16 id)
1736 {
1737         dstream<<"ClientEnvironment::removeActiveObject(): "
1738                         <<"id="<<id<<std::endl;
1739         ClientActiveObject* obj = getActiveObject(id);
1740         if(obj == NULL)
1741         {
1742                 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1743                                 <<"id="<<id<<" not found"<<std::endl;
1744                 return;
1745         }
1746         obj->removeFromScene();
1747         delete obj;
1748         m_active_objects.remove(id);
1749 }
1750
1751 void ClientEnvironment::processActiveObjectMessage(u16 id,
1752                 const std::string &data)
1753 {
1754         ClientActiveObject* obj = getActiveObject(id);
1755         if(obj == NULL)
1756         {
1757                 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1758                                 <<" got message for id="<<id<<", which doesn't exist."
1759                                 <<std::endl;
1760                 return;
1761         }
1762         obj->processMessage(data);
1763 }
1764
1765 /*
1766         Callbacks for activeobjects
1767 */
1768
1769 void ClientEnvironment::damageLocalPlayer(u8 damage)
1770 {
1771         LocalPlayer *lplayer = getLocalPlayer();
1772         assert(lplayer);
1773
1774         if(lplayer->hp > damage)
1775                 lplayer->hp -= damage;
1776         else
1777                 lplayer->hp = 0;
1778
1779         ClientEnvEvent event;
1780         event.type = CEE_PLAYER_DAMAGE;
1781         event.player_damage.amount = damage;
1782         m_client_event_queue.push_back(event);
1783 }
1784
1785 /*
1786         Client likes to call these
1787 */
1788         
1789 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1790                 core::array<DistanceSortedActiveObject> &dest)
1791 {
1792         for(core::map<u16, ClientActiveObject*>::Iterator
1793                         i = m_active_objects.getIterator();
1794                         i.atEnd()==false; i++)
1795         {
1796                 ClientActiveObject* obj = i.getNode()->getValue();
1797
1798                 f32 d = (obj->getPosition() - origin).getLength();
1799
1800                 if(d > max_d)
1801                         continue;
1802
1803                 DistanceSortedActiveObject dso(obj, d);
1804
1805                 dest.push_back(dso);
1806         }
1807 }
1808
1809 ClientEnvEvent ClientEnvironment::getClientEvent()
1810 {
1811         if(m_client_event_queue.size() == 0)
1812         {
1813                 ClientEnvEvent event;
1814                 event.type = CEE_NONE;
1815                 return event;
1816         }
1817         return m_client_event_queue.pop_front();
1818 }
1819
1820 #endif // #ifndef SERVER
1821
1822