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