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