]> git.lizzy.rs Git - dragonfireclient.git/blob - src/environment.cpp
fixed block unloading from memory (a better fix coming next)
[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 #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                         // Reset block usage timer
806                         block->resetUsageTimer();
807                         
808                         // Set current time as timestamp
809                         block->setTimestampNoChangedFlag(m_game_time);
810
811                         // Run node metadata
812                         bool changed = block->m_node_metadata.step(dtime);
813                         if(changed)
814                         {
815                                 MapEditEvent event;
816                                 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
817                                 event.p = p;
818                                 m_map->dispatchEvent(&event);
819
820                                 block->setChangedFlag();
821                         }
822                 }
823         }
824         if(m_active_blocks_test_interval.step(dtime, 10.0))
825         {
826                 //float dtime = 10.0;
827
828                 for(core::map<v3s16, bool>::Iterator
829                                 i = m_active_blocks.m_list.getIterator();
830                                 i.atEnd()==false; i++)
831                 {
832                         v3s16 p = i.getNode()->getKey();
833                         
834                         /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
835                                         <<") being handled"<<std::endl;*/
836
837                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
838                         if(block==NULL)
839                                 continue;
840                         
841                         // Set current time as timestamp
842                         block->setTimestampNoChangedFlag(m_game_time);
843
844                         /*
845                                 Do stuff!
846
847                                 Note that map modifications should be done using the event-
848                                 making map methods so that the server gets information
849                                 about them.
850
851                                 Reading can be done quickly directly from the block.
852
853                                 Everything should bind to inside this single content
854                                 searching loop to keep things fast.
855                         */
856                         // TODO: Implement usage of ActiveBlockModifier
857
858                         v3s16 p0;
859                         for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
860                         for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
861                         for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
862                         {
863                                 v3s16 p = p0 + block->getPosRelative();
864                                 MapNode n = block->getNodeNoEx(p0);
865
866                                 /*
867                                         Test something:
868                                         Convert mud under proper lighting to grass
869                                 */
870                                 if(n.d == CONTENT_MUD)
871                                 {
872                                         if(myrand()%20 == 0)
873                                         {
874                                                 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
875                                                 if(content_features(n_top.d).air_equivalent &&
876                                                                 n_top.getLightBlend(getDayNightRatio()) >= 13)
877                                                 {
878                                                         n.d = CONTENT_GRASS;
879                                                         m_map->addNodeWithEvent(p, n);
880                                                 }
881                                         }
882                                 }
883                                 /*
884                                         Convert grass into mud if under something else than air
885                                 */
886                                 else if(n.d == CONTENT_GRASS)
887                                 {
888                                         //if(myrand()%20 == 0)
889                                         {
890                                                 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
891                                                 if(n_top.d != CONTENT_AIR
892                                                                 && n_top.d != CONTENT_IGNORE)
893                                                 {
894                                                         n.d = CONTENT_MUD;
895                                                         m_map->addNodeWithEvent(p, n);
896                                                 }
897                                         }
898                                 }
899                         }
900                 }
901         }
902         
903         /*
904                 Step active objects
905         */
906         {
907                 //TimeTaker timer("Step active objects");
908                 
909                 // This helps the objects to send data at the same time
910                 bool send_recommended = false;
911                 m_send_recommended_timer += dtime;
912                 if(m_send_recommended_timer > 0.15)
913                 {
914                         m_send_recommended_timer = 0;
915                         send_recommended = true;
916                 }
917
918                 for(core::map<u16, ServerActiveObject*>::Iterator
919                                 i = m_active_objects.getIterator();
920                                 i.atEnd()==false; i++)
921                 {
922                         ServerActiveObject* obj = i.getNode()->getValue();
923                         // Don't step if is to be removed or stored statically
924                         if(obj->m_removed || obj->m_pending_deactivation)
925                                 continue;
926                         // Step object
927                         obj->step(dtime, send_recommended);
928                         // Read messages from object
929                         while(obj->m_messages_out.size() > 0)
930                         {
931                                 m_active_object_messages.push_back(
932                                                 obj->m_messages_out.pop_front());
933                         }
934                 }
935         }
936         
937         /*
938                 Manage active objects
939         */
940         if(m_object_management_interval.step(dtime, 0.5))
941         {
942                 /*
943                         Remove objects that satisfy (m_removed && m_known_by_count==0)
944                 */
945                 removeRemovedObjects();
946         }
947
948         if(g_settings.getBool("enable_experimental"))
949         {
950
951         /*
952                 TEST CODE
953         */
954 #if 1
955         m_random_spawn_timer -= dtime;
956         if(m_random_spawn_timer < 0)
957         {
958                 //m_random_spawn_timer += myrand_range(2.0, 20.0);
959                 //m_random_spawn_timer += 2.0;
960                 m_random_spawn_timer += 200.0;
961
962                 /*
963                         Find some position
964                 */
965
966                 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
967                 s16 y = 1 + getServerMap().findGroundLevel(p2d);
968                 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
969                 
970                 Player *player = getRandomConnectedPlayer();
971                 v3f pos(0,0,0);
972                 if(player)
973                         pos = player->getPosition();
974                 pos += v3f(
975                         myrand_range(-3,3)*BS,
976                         0,
977                         myrand_range(-3,3)*BS
978                 );
979
980                 /*
981                         Create a ServerActiveObject
982                 */
983
984                 //TestSAO *obj = new TestSAO(this, 0, pos);
985                 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
986                 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
987                 ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
988                 addActiveObject(obj);
989         }
990 #endif
991
992         } // enable_experimental
993 }
994
995 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
996 {
997         core::map<u16, ServerActiveObject*>::Node *n;
998         n = m_active_objects.find(id);
999         if(n == NULL)
1000                 return NULL;
1001         return n->getValue();
1002 }
1003
1004 bool isFreeServerActiveObjectId(u16 id,
1005                 core::map<u16, ServerActiveObject*> &objects)
1006 {
1007         if(id == 0)
1008                 return false;
1009         
1010         for(core::map<u16, ServerActiveObject*>::Iterator
1011                         i = objects.getIterator();
1012                         i.atEnd()==false; i++)
1013         {
1014                 if(i.getNode()->getKey() == id)
1015                         return false;
1016         }
1017         return true;
1018 }
1019
1020 u16 getFreeServerActiveObjectId(
1021                 core::map<u16, ServerActiveObject*> &objects)
1022 {
1023         u16 new_id = 1;
1024         for(;;)
1025         {
1026                 if(isFreeServerActiveObjectId(new_id, objects))
1027                         return new_id;
1028                 
1029                 if(new_id == 65535)
1030                         return 0;
1031
1032                 new_id++;
1033         }
1034 }
1035
1036 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1037 {
1038         assert(object);
1039         u16 id = addActiveObjectRaw(object, true);
1040         return id;
1041 }
1042
1043 /*
1044         Finds out what new objects have been added to
1045         inside a radius around a position
1046 */
1047 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1048                 core::map<u16, bool> &current_objects,
1049                 core::map<u16, bool> &added_objects)
1050 {
1051         v3f pos_f = intToFloat(pos, BS);
1052         f32 radius_f = radius * BS;
1053         /*
1054                 Go through the object list,
1055                 - discard m_removed objects,
1056                 - discard objects that are too far away,
1057                 - discard objects that are found in current_objects.
1058                 - add remaining objects to added_objects
1059         */
1060         for(core::map<u16, ServerActiveObject*>::Iterator
1061                         i = m_active_objects.getIterator();
1062                         i.atEnd()==false; i++)
1063         {
1064                 u16 id = i.getNode()->getKey();
1065                 // Get object
1066                 ServerActiveObject *object = i.getNode()->getValue();
1067                 if(object == NULL)
1068                         continue;
1069                 // Discard if removed
1070                 if(object->m_removed)
1071                         continue;
1072                 // Discard if too far
1073                 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1074                 if(distance_f > radius_f)
1075                         continue;
1076                 // Discard if already on current_objects
1077                 core::map<u16, bool>::Node *n;
1078                 n = current_objects.find(id);
1079                 if(n != NULL)
1080                         continue;
1081                 // Add to added_objects
1082                 added_objects.insert(id, false);
1083         }
1084 }
1085
1086 /*
1087         Finds out what objects have been removed from
1088         inside a radius around a position
1089 */
1090 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1091                 core::map<u16, bool> &current_objects,
1092                 core::map<u16, bool> &removed_objects)
1093 {
1094         v3f pos_f = intToFloat(pos, BS);
1095         f32 radius_f = radius * BS;
1096         /*
1097                 Go through current_objects; object is removed if:
1098                 - object is not found in m_active_objects (this is actually an
1099                   error condition; objects should be set m_removed=true and removed
1100                   only after all clients have been informed about removal), or
1101                 - object has m_removed=true, or
1102                 - object is too far away
1103         */
1104         for(core::map<u16, bool>::Iterator
1105                         i = current_objects.getIterator();
1106                         i.atEnd()==false; i++)
1107         {
1108                 u16 id = i.getNode()->getKey();
1109                 ServerActiveObject *object = getActiveObject(id);
1110                 if(object == NULL)
1111                 {
1112                         dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1113                                         <<" object in current_objects is NULL"<<std::endl;
1114                 }
1115                 else if(object->m_removed == false)
1116                 {
1117                         f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1118                         /*dstream<<"removed == false"
1119                                         <<"distance_f = "<<distance_f
1120                                         <<", radius_f = "<<radius_f<<std::endl;*/
1121                         if(distance_f < radius_f)
1122                         {
1123                                 // Not removed
1124                                 continue;
1125                         }
1126                 }
1127                 removed_objects.insert(id, false);
1128         }
1129 }
1130
1131 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1132 {
1133         if(m_active_object_messages.size() == 0)
1134                 return ActiveObjectMessage(0);
1135         
1136         return m_active_object_messages.pop_front();
1137 }
1138
1139 /*
1140         ************ Private methods *************
1141 */
1142
1143 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1144                 bool set_changed)
1145 {
1146         assert(object);
1147         if(object->getId() == 0)
1148         {
1149                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1150                 if(new_id == 0)
1151                 {
1152                         dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1153                                         <<"no free ids available"<<std::endl;
1154                         delete object;
1155                         return 0;
1156                 }
1157                 object->setId(new_id);
1158         }
1159         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1160         {
1161                 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1162                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1163                 delete object;
1164                 return 0;
1165         }
1166         /*dstream<<"INGO: ServerEnvironment::addActiveObjectRaw(): "
1167                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1168                         
1169         m_active_objects.insert(object->getId(), object);
1170
1171         // Add static object to active static list of the block
1172         v3f objectpos = object->getBasePosition();
1173         std::string staticdata = object->getStaticData();
1174         StaticObject s_obj(object->getType(), objectpos, staticdata);
1175         // Add to the block where the object is located in
1176         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1177         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1178         if(block)
1179         {
1180                 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1181                 object->m_static_exists = true;
1182                 object->m_static_block = blockpos;
1183
1184                 if(set_changed)
1185                         block->setChangedFlag();
1186         }
1187         else{
1188                 dstream<<"WARNING: Server: Could not find a block for "
1189                                 <<"storing newly added static active object"<<std::endl;
1190         }
1191
1192         return object->getId();
1193 }
1194
1195 /*
1196         Remove objects that satisfy (m_removed && m_known_by_count==0)
1197 */
1198 void ServerEnvironment::removeRemovedObjects()
1199 {
1200         core::list<u16> objects_to_remove;
1201         for(core::map<u16, ServerActiveObject*>::Iterator
1202                         i = m_active_objects.getIterator();
1203                         i.atEnd()==false; i++)
1204         {
1205                 u16 id = i.getNode()->getKey();
1206                 ServerActiveObject* obj = i.getNode()->getValue();
1207                 // This shouldn't happen but check it
1208                 if(obj == NULL)
1209                 {
1210                         dstream<<"WARNING: NULL object found in ServerEnvironment"
1211                                         <<" while finding removed objects. id="<<id<<std::endl;
1212                         // Id to be removed from m_active_objects
1213                         objects_to_remove.push_back(id);
1214                         continue;
1215                 }
1216
1217                 /*
1218                         We will delete objects that are marked as removed or thatare
1219                         waiting for deletion after deactivation
1220                 */
1221                 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1222                         continue;
1223
1224                 /*
1225                         Delete static data from block if is marked as removed
1226                 */
1227                 if(obj->m_static_exists && obj->m_removed)
1228                 {
1229                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1230                         if(block)
1231                         {
1232                                 block->m_static_objects.remove(id);
1233                                 block->setChangedFlag();
1234                         }
1235                 }
1236
1237                 // If m_known_by_count > 0, don't actually remove.
1238                 if(obj->m_known_by_count > 0)
1239                         continue;
1240                 
1241                 // Delete
1242                 delete obj;
1243                 // Id to be removed from m_active_objects
1244                 objects_to_remove.push_back(id);
1245         }
1246         // Remove references from m_active_objects
1247         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1248                         i != objects_to_remove.end(); i++)
1249         {
1250                 m_active_objects.remove(*i);
1251         }
1252 }
1253
1254 /*
1255         Convert stored objects from blocks near the players to active.
1256 */
1257 void ServerEnvironment::activateObjects(MapBlock *block)
1258 {
1259         if(block==NULL)
1260                 return;
1261         // Ignore if no stored objects (to not set changed flag)
1262         if(block->m_static_objects.m_stored.size() == 0)
1263                 return;
1264         // A list for objects that couldn't be converted to static for some
1265         // reason. They will be stored back.
1266         core::list<StaticObject> new_stored;
1267         // Loop through stored static objects
1268         for(core::list<StaticObject>::Iterator
1269                         i = block->m_static_objects.m_stored.begin();
1270                         i != block->m_static_objects.m_stored.end(); i++)
1271         {
1272                 /*dstream<<"INFO: Server: Creating an active object from "
1273                                 <<"static data"<<std::endl;*/
1274                 StaticObject &s_obj = *i;
1275                 // Create an active object from the data
1276                 ServerActiveObject *obj = ServerActiveObject::create
1277                                 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1278                 // If couldn't create object, store static data back.
1279                 if(obj==NULL)
1280                 {
1281                         new_stored.push_back(s_obj);
1282                         continue;
1283                 }
1284                 // This will also add the object to the active static list
1285                 addActiveObjectRaw(obj, false);
1286                 //u16 id = addActiveObjectRaw(obj, false);
1287         }
1288         // Clear stored list
1289         block->m_static_objects.m_stored.clear();
1290         // Add leftover failed stuff to stored list
1291         for(core::list<StaticObject>::Iterator
1292                         i = new_stored.begin();
1293                         i != new_stored.end(); i++)
1294         {
1295                 StaticObject &s_obj = *i;
1296                 block->m_static_objects.m_stored.push_back(s_obj);
1297         }
1298         // Block has been modified
1299         // NOTE: No it has not really. Save I/O here.
1300         //block->setChangedFlag();
1301 }
1302
1303 /*
1304         Convert objects that are not in active blocks to static.
1305
1306         If m_known_by_count != 0, active object is not deleted, but static
1307         data is still updated.
1308
1309         If force_delete is set, active object is deleted nevertheless. It
1310         shall only be set so in the destructor of the environment.
1311 */
1312 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1313 {
1314         core::list<u16> objects_to_remove;
1315         for(core::map<u16, ServerActiveObject*>::Iterator
1316                         i = m_active_objects.getIterator();
1317                         i.atEnd()==false; i++)
1318         {
1319                 ServerActiveObject* obj = i.getNode()->getValue();
1320                 u16 id = i.getNode()->getKey();
1321                 v3f objectpos = obj->getBasePosition();
1322
1323                 // This shouldn't happen but check it
1324                 if(obj == NULL)
1325                 {
1326                         dstream<<"WARNING: NULL object found in ServerEnvironment"
1327                                         <<std::endl;
1328                         assert(0);
1329                         continue;
1330                 }
1331
1332                 // The block in which the object resides in
1333                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1334                 
1335                 // If block is active, don't remove
1336                 if(m_active_blocks.contains(blockpos_o))
1337                         continue;
1338
1339                 /*
1340                         Update the static data
1341                 */
1342
1343                 // Delete old static object
1344                 MapBlock *oldblock = NULL;
1345                 if(obj->m_static_exists)
1346                 {
1347                         MapBlock *block = m_map->getBlockNoCreateNoEx
1348                                         (obj->m_static_block);
1349                         if(block)
1350                         {
1351                                 block->m_static_objects.remove(id);
1352                                 oldblock = block;
1353                         }
1354                 }
1355                 // Create new static object
1356                 std::string staticdata = obj->getStaticData();
1357                 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1358                 // Add to the block where the object is located in
1359                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1360                 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1361                 if(block)
1362                 {
1363                         block->m_static_objects.insert(0, s_obj);
1364                         block->setChangedFlag();
1365                         obj->m_static_exists = true;
1366                         obj->m_static_block = block->getPos();
1367                 }
1368                 // If not possible, add back to previous block
1369                 else if(oldblock)
1370                 {
1371                         oldblock->m_static_objects.insert(0, s_obj);
1372                         oldblock->setChangedFlag();
1373                         obj->m_static_exists = true;
1374                         obj->m_static_block = oldblock->getPos();
1375                 }
1376                 else{
1377                         dstream<<"WARNING: Server: Could not find a block for "
1378                                         <<"storing static object"<<std::endl;
1379                         obj->m_static_exists = false;
1380                         continue;
1381                 }
1382
1383                 /*
1384                         Delete active object if not known by some client,
1385                         else set pending deactivation
1386                 */
1387
1388                 // If known by some client, don't delete.
1389                 if(obj->m_known_by_count > 0 && force_delete == false)
1390                 {
1391                         obj->m_pending_deactivation = true;
1392                         continue;
1393                 }
1394                 
1395                 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1396                                 <<std::endl;*/
1397                 // Delete active object
1398                 delete obj;
1399                 // Id to be removed from m_active_objects
1400                 objects_to_remove.push_back(id);
1401         }
1402
1403         // Remove references from m_active_objects
1404         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1405                         i != objects_to_remove.end(); i++)
1406         {
1407                 m_active_objects.remove(*i);
1408         }
1409 }
1410
1411
1412 #ifndef SERVER
1413
1414 /*
1415         ClientEnvironment
1416 */
1417
1418 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1419         m_map(map),
1420         m_smgr(smgr)
1421 {
1422         assert(m_map);
1423         assert(m_smgr);
1424 }
1425
1426 ClientEnvironment::~ClientEnvironment()
1427 {
1428         // delete active objects
1429         for(core::map<u16, ClientActiveObject*>::Iterator
1430                         i = m_active_objects.getIterator();
1431                         i.atEnd()==false; i++)
1432         {
1433                 delete i.getNode()->getValue();
1434         }
1435
1436         // Drop/delete map
1437         m_map->drop();
1438 }
1439
1440 void ClientEnvironment::addPlayer(Player *player)
1441 {
1442         DSTACK(__FUNCTION_NAME);
1443         /*
1444                 It is a failure if player is local and there already is a local
1445                 player
1446         */
1447         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1448
1449         Environment::addPlayer(player);
1450 }
1451
1452 LocalPlayer * ClientEnvironment::getLocalPlayer()
1453 {
1454         for(core::list<Player*>::Iterator i = m_players.begin();
1455                         i != m_players.end(); i++)
1456         {
1457                 Player *player = *i;
1458                 if(player->isLocal())
1459                         return (LocalPlayer*)player;
1460         }
1461         return NULL;
1462 }
1463
1464 void ClientEnvironment::step(float dtime)
1465 {
1466         DSTACK(__FUNCTION_NAME);
1467
1468         // Get some settings
1469         bool free_move = g_settings.getBool("free_move");
1470         bool footprints = g_settings.getBool("footprints");
1471
1472         {
1473                 //TimeTaker timer("Client m_map->timerUpdate()");
1474                 m_map->timerUpdate(dtime);
1475         }
1476         
1477         // Get local player
1478         LocalPlayer *lplayer = getLocalPlayer();
1479         assert(lplayer);
1480         // collision info queue
1481         core::list<CollisionInfo> player_collisions;
1482         
1483         /*
1484                 Get the speed the player is going
1485         */
1486         f32 player_speed = 0.001; // just some small value
1487         player_speed = lplayer->getSpeed().getLength();
1488         
1489         /*
1490                 Maximum position increment
1491         */
1492         //f32 position_max_increment = 0.05*BS;
1493         f32 position_max_increment = 0.1*BS;
1494
1495         // Maximum time increment (for collision detection etc)
1496         // time = distance / speed
1497         f32 dtime_max_increment = position_max_increment / player_speed;
1498         
1499         // Maximum time increment is 10ms or lower
1500         if(dtime_max_increment > 0.01)
1501                 dtime_max_increment = 0.01;
1502         
1503         // Don't allow overly huge dtime
1504         if(dtime > 0.5)
1505                 dtime = 0.5;
1506         
1507         f32 dtime_downcount = dtime;
1508
1509         /*
1510                 Stuff that has a maximum time increment
1511         */
1512
1513         u32 loopcount = 0;
1514         do
1515         {
1516                 loopcount++;
1517
1518                 f32 dtime_part;
1519                 if(dtime_downcount > dtime_max_increment)
1520                 {
1521                         dtime_part = dtime_max_increment;
1522                         dtime_downcount -= dtime_part;
1523                 }
1524                 else
1525                 {
1526                         dtime_part = dtime_downcount;
1527                         /*
1528                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
1529                                 when dtime_part is so small that dtime_downcount -= dtime_part
1530                                 does nothing
1531                         */
1532                         dtime_downcount = 0;
1533                 }
1534                 
1535                 /*
1536                         Handle local player
1537                 */
1538                 
1539                 {
1540                         v3f lplayerpos = lplayer->getPosition();
1541                         
1542                         // Apply physics
1543                         if(free_move == false)
1544                         {
1545                                 // Gravity
1546                                 v3f speed = lplayer->getSpeed();
1547                                 if(lplayer->swimming_up == false)
1548                                         speed.Y -= 9.81 * BS * dtime_part * 2;
1549
1550                                 // Water resistance
1551                                 if(lplayer->in_water_stable || lplayer->in_water)
1552                                 {
1553                                         f32 max_down = 2.0*BS;
1554                                         if(speed.Y < -max_down) speed.Y = -max_down;
1555
1556                                         f32 max = 2.5*BS;
1557                                         if(speed.getLength() > max)
1558                                         {
1559                                                 speed = speed / speed.getLength() * max;
1560                                         }
1561                                 }
1562
1563                                 lplayer->setSpeed(speed);
1564                         }
1565
1566                         /*
1567                                 Move the lplayer.
1568                                 This also does collision detection.
1569                         */
1570                         lplayer->move(dtime_part, *m_map, position_max_increment,
1571                                         &player_collisions);
1572                 }
1573         }
1574         while(dtime_downcount > 0.001);
1575                 
1576         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1577
1578         for(core::list<CollisionInfo>::Iterator
1579                         i = player_collisions.begin();
1580                         i != player_collisions.end(); i++)
1581         {
1582                 CollisionInfo &info = *i;
1583                 if(info.t == COLLISION_FALL)
1584                 {
1585                         //f32 tolerance = BS*10; // 2 without damage
1586                         f32 tolerance = BS*12; // 3 without damage
1587                         f32 factor = 1;
1588                         if(info.speed > tolerance)
1589                         {
1590                                 f32 damage_f = (info.speed - tolerance)/BS*factor;
1591                                 u16 damage = (u16)(damage_f+0.5);
1592                                 if(lplayer->hp > damage)
1593                                         lplayer->hp -= damage;
1594                                 else
1595                                         lplayer->hp = 0;
1596
1597                                 ClientEnvEvent event;
1598                                 event.type = CEE_PLAYER_DAMAGE;
1599                                 event.player_damage.amount = damage;
1600                                 m_client_event_queue.push_back(event);
1601                         }
1602                 }
1603         }
1604         
1605         /*
1606                 Stuff that can be done in an arbitarily large dtime
1607         */
1608         for(core::list<Player*>::Iterator i = m_players.begin();
1609                         i != m_players.end(); i++)
1610         {
1611                 Player *player = *i;
1612                 v3f playerpos = player->getPosition();
1613                 
1614                 /*
1615                         Handle non-local players
1616                 */
1617                 if(player->isLocal() == false)
1618                 {
1619                         // Move
1620                         player->move(dtime, *m_map, 100*BS);
1621
1622                         // Update lighting on remote players on client
1623                         u8 light = LIGHT_MAX;
1624                         try{
1625                                 // Get node at head
1626                                 v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
1627                                 MapNode n = m_map->getNode(p);
1628                                 light = n.getLightBlend(getDayNightRatio());
1629                         }
1630                         catch(InvalidPositionException &e) {}
1631                         player->updateLight(light);
1632                 }
1633                 
1634                 /*
1635                         Add footsteps to grass
1636                 */
1637                 if(footprints)
1638                 {
1639                         // Get node that is at BS/4 under player
1640                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1641                         try{
1642                                 MapNode n = m_map->getNode(bottompos);
1643                                 if(n.d == CONTENT_GRASS)
1644                                 {
1645                                         n.d = CONTENT_GRASS_FOOTSTEPS;
1646                                         m_map->setNode(bottompos, n);
1647                                         // Update mesh on client
1648                                         if(m_map->mapType() == MAPTYPE_CLIENT)
1649                                         {
1650                                                 v3s16 p_blocks = getNodeBlockPos(bottompos);
1651                                                 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1652                                                 //b->updateMesh(getDayNightRatio());
1653                                                 b->setMeshExpired(true);
1654                                         }
1655                                 }
1656                         }
1657                         catch(InvalidPositionException &e)
1658                         {
1659                         }
1660                 }
1661         }
1662         
1663         /*
1664                 Step active objects and update lighting of them
1665         */
1666         
1667         for(core::map<u16, ClientActiveObject*>::Iterator
1668                         i = m_active_objects.getIterator();
1669                         i.atEnd()==false; i++)
1670         {
1671                 ClientActiveObject* obj = i.getNode()->getValue();
1672                 // Step object
1673                 obj->step(dtime, this);
1674
1675                 if(m_active_object_light_update_interval.step(dtime, 0.5))
1676                 {
1677                         // Update lighting
1678                         //u8 light = LIGHT_MAX;
1679                         u8 light = 0;
1680                         try{
1681                                 // Get node at head
1682                                 v3s16 p = obj->getLightPosition();
1683                                 MapNode n = m_map->getNode(p);
1684                                 light = n.getLightBlend(getDayNightRatio());
1685                         }
1686                         catch(InvalidPositionException &e) {}
1687                         obj->updateLight(light);
1688                 }
1689         }
1690 }
1691
1692 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1693 {
1694         m_map->updateMeshes(blockpos, getDayNightRatio());
1695 }
1696
1697 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1698 {
1699         m_map->expireMeshes(only_daynight_diffed);
1700 }
1701
1702 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1703 {
1704         core::map<u16, ClientActiveObject*>::Node *n;
1705         n = m_active_objects.find(id);
1706         if(n == NULL)
1707                 return NULL;
1708         return n->getValue();
1709 }
1710
1711 bool isFreeClientActiveObjectId(u16 id,
1712                 core::map<u16, ClientActiveObject*> &objects)
1713 {
1714         if(id == 0)
1715                 return false;
1716         
1717         for(core::map<u16, ClientActiveObject*>::Iterator
1718                         i = objects.getIterator();
1719                         i.atEnd()==false; i++)
1720         {
1721                 if(i.getNode()->getKey() == id)
1722                         return false;
1723         }
1724         return true;
1725 }
1726
1727 u16 getFreeClientActiveObjectId(
1728                 core::map<u16, ClientActiveObject*> &objects)
1729 {
1730         u16 new_id = 1;
1731         for(;;)
1732         {
1733                 if(isFreeClientActiveObjectId(new_id, objects))
1734                         return new_id;
1735                 
1736                 if(new_id == 65535)
1737                         return 0;
1738
1739                 new_id++;
1740         }
1741 }
1742
1743 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1744 {
1745         assert(object);
1746         if(object->getId() == 0)
1747         {
1748                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1749                 if(new_id == 0)
1750                 {
1751                         dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1752                                         <<"no free ids available"<<std::endl;
1753                         delete object;
1754                         return 0;
1755                 }
1756                 object->setId(new_id);
1757         }
1758         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1759         {
1760                 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1761                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1762                 delete object;
1763                 return 0;
1764         }
1765         dstream<<"INGO: ClientEnvironment::addActiveObject(): "
1766                         <<"added (id="<<object->getId()<<")"<<std::endl;
1767         m_active_objects.insert(object->getId(), object);
1768         object->addToScene(m_smgr);
1769         return object->getId();
1770 }
1771
1772 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1773                 const std::string &init_data)
1774 {
1775         ClientActiveObject* obj = ClientActiveObject::create(type);
1776         if(obj == NULL)
1777         {
1778                 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1779                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1780                                 <<std::endl;
1781                 return;
1782         }
1783         
1784         obj->setId(id);
1785
1786         addActiveObject(obj);
1787
1788         obj->initialize(init_data);
1789 }
1790
1791 void ClientEnvironment::removeActiveObject(u16 id)
1792 {
1793         dstream<<"ClientEnvironment::removeActiveObject(): "
1794                         <<"id="<<id<<std::endl;
1795         ClientActiveObject* obj = getActiveObject(id);
1796         if(obj == NULL)
1797         {
1798                 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1799                                 <<"id="<<id<<" not found"<<std::endl;
1800                 return;
1801         }
1802         obj->removeFromScene();
1803         delete obj;
1804         m_active_objects.remove(id);
1805 }
1806
1807 void ClientEnvironment::processActiveObjectMessage(u16 id,
1808                 const std::string &data)
1809 {
1810         ClientActiveObject* obj = getActiveObject(id);
1811         if(obj == NULL)
1812         {
1813                 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1814                                 <<" got message for id="<<id<<", which doesn't exist."
1815                                 <<std::endl;
1816                 return;
1817         }
1818         obj->processMessage(data);
1819 }
1820
1821 /*
1822         Callbacks for activeobjects
1823 */
1824
1825 void ClientEnvironment::damageLocalPlayer(u8 damage)
1826 {
1827         LocalPlayer *lplayer = getLocalPlayer();
1828         assert(lplayer);
1829
1830         if(lplayer->hp > damage)
1831                 lplayer->hp -= damage;
1832         else
1833                 lplayer->hp = 0;
1834
1835         ClientEnvEvent event;
1836         event.type = CEE_PLAYER_DAMAGE;
1837         event.player_damage.amount = damage;
1838         m_client_event_queue.push_back(event);
1839 }
1840
1841 /*
1842         Client likes to call these
1843 */
1844         
1845 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1846                 core::array<DistanceSortedActiveObject> &dest)
1847 {
1848         for(core::map<u16, ClientActiveObject*>::Iterator
1849                         i = m_active_objects.getIterator();
1850                         i.atEnd()==false; i++)
1851         {
1852                 ClientActiveObject* obj = i.getNode()->getValue();
1853
1854                 f32 d = (obj->getPosition() - origin).getLength();
1855
1856                 if(d > max_d)
1857                         continue;
1858
1859                 DistanceSortedActiveObject dso(obj, d);
1860
1861                 dest.push_back(dso);
1862         }
1863 }
1864
1865 ClientEnvEvent ClientEnvironment::getClientEvent()
1866 {
1867         if(m_client_event_queue.size() == 0)
1868         {
1869                 ClientEnvEvent event;
1870                 event.type = CEE_NONE;
1871                 return event;
1872         }
1873         return m_client_event_queue.pop_front();
1874 }
1875
1876 #endif // #ifndef SERVER
1877
1878