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