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