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