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