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