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