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