]> git.lizzy.rs Git - dragonfireclient.git/blob - src/environment.cpp
71fceae28c7230ad7844c4ae969ab6d91f103564
[dragonfireclient.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                 u16 id = i.getNode()->getKey();
1390                 v3f objectpos = obj->getBasePosition();
1391
1392                 // This shouldn't happen but check it
1393                 if(obj == NULL)
1394                 {
1395                         dstream<<"WARNING: NULL object found in ServerEnvironment"
1396                                         <<std::endl;
1397                         assert(0);
1398                         continue;
1399                 }
1400
1401                 // The block in which the object resides in
1402                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1403                 
1404                 // If block is active, don't remove
1405                 if(m_active_blocks.contains(blockpos_o))
1406                         continue;
1407
1408                 /*
1409                         Update the static data
1410                 */
1411
1412                 // Delete old static object
1413                 MapBlock *oldblock = NULL;
1414                 if(obj->m_static_exists)
1415                 {
1416                         MapBlock *block = m_map->getBlockNoCreateNoEx
1417                                         (obj->m_static_block);
1418                         if(block)
1419                         {
1420                                 block->m_static_objects.remove(id);
1421                                 oldblock = block;
1422                         }
1423                 }
1424                 // Create new static object
1425                 std::string staticdata = obj->getStaticData();
1426                 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1427                 // Add to the block where the object is located in
1428                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1429                 // Get or generate the block
1430                 MapBlock *block = m_map->emergeBlock(blockpos);
1431
1432                 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1433                 if(block == NULL)
1434                 {
1435                         // Block not found. Is the old block still ok?
1436                         if(oldblock)
1437                                 block = oldblock;
1438                         // Load from disk or generate
1439                         else
1440                                 block = m_map->emergeBlock(blockpos);
1441                 }*/
1442
1443                 if(block)
1444                 {
1445                         block->m_static_objects.insert(0, s_obj);
1446                         block->setChangedFlag();
1447                         obj->m_static_exists = true;
1448                         obj->m_static_block = block->getPos();
1449                 }
1450                 else{
1451                         dstream<<"WARNING: ServerEnv: Could not find or generate "
1452                                         <<"a block for storing static object"<<std::endl;
1453                         obj->m_static_exists = false;
1454                         continue;
1455                 }
1456
1457                 /*
1458                         Delete active object if not known by some client,
1459                         else set pending deactivation
1460                 */
1461
1462                 // If known by some client, don't delete.
1463                 if(obj->m_known_by_count > 0 && force_delete == false)
1464                 {
1465                         obj->m_pending_deactivation = true;
1466                         continue;
1467                 }
1468                 
1469                 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1470                                 <<std::endl;*/
1471                 // Delete active object
1472                 delete obj;
1473                 // Id to be removed from m_active_objects
1474                 objects_to_remove.push_back(id);
1475         }
1476
1477         // Remove references from m_active_objects
1478         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1479                         i != objects_to_remove.end(); i++)
1480         {
1481                 m_active_objects.remove(*i);
1482         }
1483 }
1484
1485
1486 #ifndef SERVER
1487
1488 /*
1489         ClientEnvironment
1490 */
1491
1492 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1493         m_map(map),
1494         m_smgr(smgr)
1495 {
1496         assert(m_map);
1497         assert(m_smgr);
1498 }
1499
1500 ClientEnvironment::~ClientEnvironment()
1501 {
1502         // delete active objects
1503         for(core::map<u16, ClientActiveObject*>::Iterator
1504                         i = m_active_objects.getIterator();
1505                         i.atEnd()==false; i++)
1506         {
1507                 delete i.getNode()->getValue();
1508         }
1509
1510         // Drop/delete map
1511         m_map->drop();
1512 }
1513
1514 void ClientEnvironment::addPlayer(Player *player)
1515 {
1516         DSTACK(__FUNCTION_NAME);
1517         /*
1518                 It is a failure if player is local and there already is a local
1519                 player
1520         */
1521         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1522
1523         Environment::addPlayer(player);
1524 }
1525
1526 LocalPlayer * ClientEnvironment::getLocalPlayer()
1527 {
1528         for(core::list<Player*>::Iterator i = m_players.begin();
1529                         i != m_players.end(); i++)
1530         {
1531                 Player *player = *i;
1532                 if(player->isLocal())
1533                         return (LocalPlayer*)player;
1534         }
1535         return NULL;
1536 }
1537
1538 void ClientEnvironment::step(float dtime)
1539 {
1540         DSTACK(__FUNCTION_NAME);
1541
1542         // Get some settings
1543         bool free_move = g_settings.getBool("free_move");
1544         bool footprints = g_settings.getBool("footprints");
1545
1546         // Get local player
1547         LocalPlayer *lplayer = getLocalPlayer();
1548         assert(lplayer);
1549         // collision info queue
1550         core::list<CollisionInfo> player_collisions;
1551         
1552         /*
1553                 Get the speed the player is going
1554         */
1555         bool is_climbing = lplayer->is_climbing;
1556         
1557         /*
1558                 Check if the player is frozen (don't apply physics)
1559         */
1560         bool is_frozen = lplayer->is_frozen;
1561
1562         f32 player_speed = 0.001; // just some small value
1563         player_speed = lplayer->getSpeed().getLength();
1564         
1565         /*
1566                 Maximum position increment
1567         */
1568         //f32 position_max_increment = 0.05*BS;
1569         f32 position_max_increment = 0.1*BS;
1570
1571         // Maximum time increment (for collision detection etc)
1572         // time = distance / speed
1573         f32 dtime_max_increment = position_max_increment / player_speed;
1574         
1575         // Maximum time increment is 10ms or lower
1576         if(dtime_max_increment > 0.01)
1577                 dtime_max_increment = 0.01;
1578         
1579         // Don't allow overly huge dtime
1580         if(dtime > 0.5)
1581                 dtime = 0.5;
1582         
1583         f32 dtime_downcount = dtime;
1584
1585         /*
1586                 Stuff that has a maximum time increment
1587         */
1588
1589         u32 loopcount = 0;
1590         do
1591         {
1592                 loopcount++;
1593
1594                 f32 dtime_part;
1595                 if(dtime_downcount > dtime_max_increment)
1596                 {
1597                         dtime_part = dtime_max_increment;
1598                         dtime_downcount -= dtime_part;
1599                 }
1600                 else
1601                 {
1602                         dtime_part = dtime_downcount;
1603                         /*
1604                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
1605                                 when dtime_part is so small that dtime_downcount -= dtime_part
1606                                 does nothing
1607                         */
1608                         dtime_downcount = 0;
1609                 }
1610                 
1611                 /*
1612                         Handle local player
1613                 */
1614                 
1615                 {
1616                         v3f lplayerpos = lplayer->getPosition();
1617                         
1618                         // Apply physics
1619                         if(free_move == false && is_climbing == false && is_frozen == false)
1620                         {
1621                                 // Gravity
1622                                 v3f speed = lplayer->getSpeed();
1623                                 if(lplayer->swimming_up == false)
1624                                         speed.Y -= 9.81 * BS * dtime_part * 2;
1625
1626                                 // Water resistance
1627                                 if(lplayer->in_water_stable || lplayer->in_water)
1628                                 {
1629                                         f32 max_down = 2.0*BS;
1630                                         if(speed.Y < -max_down) speed.Y = -max_down;
1631
1632                                         f32 max = 2.5*BS;
1633                                         if(speed.getLength() > max)
1634                                         {
1635                                                 speed = speed / speed.getLength() * max;
1636                                         }
1637                                 }
1638
1639                                 lplayer->setSpeed(speed);
1640                         }
1641
1642                         /*
1643                                 Move the lplayer.
1644                                 This also does collision detection.
1645                         */
1646                         lplayer->move(dtime_part, *m_map, position_max_increment,
1647                                         &player_collisions);
1648                 }
1649         }
1650         while(dtime_downcount > 0.001);
1651                 
1652         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1653
1654         for(core::list<CollisionInfo>::Iterator
1655                         i = player_collisions.begin();
1656                         i != player_collisions.end(); i++)
1657         {
1658                 CollisionInfo &info = *i;
1659                 if(info.t == COLLISION_FALL)
1660                 {
1661                         //f32 tolerance = BS*10; // 2 without damage
1662                         f32 tolerance = BS*12; // 3 without damage
1663                         f32 factor = 1;
1664                         if(info.speed > tolerance)
1665                         {
1666                                 f32 damage_f = (info.speed - tolerance)/BS*factor;
1667                                 u16 damage = (u16)(damage_f+0.5);
1668                                 if(lplayer->hp > damage)
1669                                         lplayer->hp -= damage;
1670                                 else
1671                                         lplayer->hp = 0;
1672
1673                                 ClientEnvEvent event;
1674                                 event.type = CEE_PLAYER_DAMAGE;
1675                                 event.player_damage.amount = damage;
1676                                 m_client_event_queue.push_back(event);
1677                         }
1678                 }
1679         }
1680         
1681         /*
1682                 A quick draft of lava damage
1683         */
1684         if(m_lava_hurt_interval.step(dtime, 1.0))
1685         {
1686                 v3f pf = lplayer->getPosition();
1687                 
1688                 // Feet, middle and head
1689                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1690                 MapNode n1 = m_map->getNodeNoEx(p1);
1691                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1692                 MapNode n2 = m_map->getNodeNoEx(p2);
1693                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1694                 MapNode n3 = m_map->getNodeNoEx(p2);
1695
1696                 u32 damage_per_second = 0;
1697                 damage_per_second = MYMAX(damage_per_second,
1698                                 content_features(n1).damage_per_second);
1699                 damage_per_second = MYMAX(damage_per_second,
1700                                 content_features(n2).damage_per_second);
1701                 damage_per_second = MYMAX(damage_per_second,
1702                                 content_features(n3).damage_per_second);
1703                 
1704                 if(damage_per_second != 0)
1705                 {
1706                         ClientEnvEvent event;
1707                         event.type = CEE_PLAYER_DAMAGE;
1708                         event.player_damage.amount = damage_per_second;
1709                         m_client_event_queue.push_back(event);
1710                 }
1711         }
1712         
1713         /*
1714                 Stuff that can be done in an arbitarily large dtime
1715         */
1716         for(core::list<Player*>::Iterator i = m_players.begin();
1717                         i != m_players.end(); i++)
1718         {
1719                 Player *player = *i;
1720                 v3f playerpos = player->getPosition();
1721                 
1722                 /*
1723                         Handle non-local players
1724                 */
1725                 if(player->isLocal() == false)
1726                 {
1727                         // Move
1728                         player->move(dtime, *m_map, 100*BS);
1729
1730                         // Update lighting on remote players on client
1731                         u8 light = LIGHT_MAX;
1732                         try{
1733                                 // Get node at head
1734                                 v3s16 p = player->getLightPosition();
1735                                 MapNode n = m_map->getNode(p);
1736                                 light = n.getLightBlend(getDayNightRatio());
1737                         }
1738                         catch(InvalidPositionException &e) {}
1739                         player->updateLight(light);
1740                 }
1741                 
1742                 /*
1743                         Add footsteps to grass
1744                 */
1745                 if(footprints)
1746                 {
1747                         // Get node that is at BS/4 under player
1748                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1749                         try{
1750                                 MapNode n = m_map->getNode(bottompos);
1751                                 if(n.getContent() == CONTENT_GRASS)
1752                                 {
1753                                         n.setContent(CONTENT_GRASS_FOOTSTEPS);
1754                                         m_map->setNode(bottompos, n);
1755                                         // Update mesh on client
1756                                         if(m_map->mapType() == MAPTYPE_CLIENT)
1757                                         {
1758                                                 v3s16 p_blocks = getNodeBlockPos(bottompos);
1759                                                 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1760                                                 //b->updateMesh(getDayNightRatio());
1761                                                 b->setMeshExpired(true);
1762                                         }
1763                                 }
1764                         }
1765                         catch(InvalidPositionException &e)
1766                         {
1767                         }
1768                 }
1769         }
1770         
1771         /*
1772                 Step active objects and update lighting of them
1773         */
1774         
1775         for(core::map<u16, ClientActiveObject*>::Iterator
1776                         i = m_active_objects.getIterator();
1777                         i.atEnd()==false; i++)
1778         {
1779                 ClientActiveObject* obj = i.getNode()->getValue();
1780                 // Step object
1781                 obj->step(dtime, this);
1782
1783                 if(m_active_object_light_update_interval.step(dtime, 0.21))
1784                 {
1785                         // Update lighting
1786                         //u8 light = LIGHT_MAX;
1787                         u8 light = 0;
1788                         try{
1789                                 // Get node at head
1790                                 v3s16 p = obj->getLightPosition();
1791                                 MapNode n = m_map->getNode(p);
1792                                 light = n.getLightBlend(getDayNightRatio());
1793                         }
1794                         catch(InvalidPositionException &e) {}
1795                         obj->updateLight(light);
1796                 }
1797         }
1798 }
1799
1800 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1801 {
1802         m_map->updateMeshes(blockpos, getDayNightRatio());
1803 }
1804
1805 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1806 {
1807         m_map->expireMeshes(only_daynight_diffed);
1808 }
1809
1810 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1811 {
1812         core::map<u16, ClientActiveObject*>::Node *n;
1813         n = m_active_objects.find(id);
1814         if(n == NULL)
1815                 return NULL;
1816         return n->getValue();
1817 }
1818
1819 bool isFreeClientActiveObjectId(u16 id,
1820                 core::map<u16, ClientActiveObject*> &objects)
1821 {
1822         if(id == 0)
1823                 return false;
1824         
1825         for(core::map<u16, ClientActiveObject*>::Iterator
1826                         i = objects.getIterator();
1827                         i.atEnd()==false; i++)
1828         {
1829                 if(i.getNode()->getKey() == id)
1830                         return false;
1831         }
1832         return true;
1833 }
1834
1835 u16 getFreeClientActiveObjectId(
1836                 core::map<u16, ClientActiveObject*> &objects)
1837 {
1838         u16 new_id = 1;
1839         for(;;)
1840         {
1841                 if(isFreeClientActiveObjectId(new_id, objects))
1842                         return new_id;
1843                 
1844                 if(new_id == 65535)
1845                         return 0;
1846
1847                 new_id++;
1848         }
1849 }
1850
1851 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1852 {
1853         assert(object);
1854         if(object->getId() == 0)
1855         {
1856                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1857                 if(new_id == 0)
1858                 {
1859                         dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1860                                         <<"no free ids available"<<std::endl;
1861                         delete object;
1862                         return 0;
1863                 }
1864                 object->setId(new_id);
1865         }
1866         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1867         {
1868                 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1869                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1870                 delete object;
1871                 return 0;
1872         }
1873         dstream<<"INFO: ClientEnvironment::addActiveObject(): "
1874                         <<"added (id="<<object->getId()<<")"<<std::endl;
1875         m_active_objects.insert(object->getId(), object);
1876         object->addToScene(m_smgr);
1877         return object->getId();
1878 }
1879
1880 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1881                 const std::string &init_data)
1882 {
1883         ClientActiveObject* obj = ClientActiveObject::create(type);
1884         if(obj == NULL)
1885         {
1886                 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1887                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1888                                 <<std::endl;
1889                 return;
1890         }
1891         
1892         obj->setId(id);
1893
1894         addActiveObject(obj);
1895
1896         obj->initialize(init_data);
1897 }
1898
1899 void ClientEnvironment::removeActiveObject(u16 id)
1900 {
1901         dstream<<"ClientEnvironment::removeActiveObject(): "
1902                         <<"id="<<id<<std::endl;
1903         ClientActiveObject* obj = getActiveObject(id);
1904         if(obj == NULL)
1905         {
1906                 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1907                                 <<"id="<<id<<" not found"<<std::endl;
1908                 return;
1909         }
1910         obj->removeFromScene();
1911         delete obj;
1912         m_active_objects.remove(id);
1913 }
1914
1915 void ClientEnvironment::processActiveObjectMessage(u16 id,
1916                 const std::string &data)
1917 {
1918         ClientActiveObject* obj = getActiveObject(id);
1919         if(obj == NULL)
1920         {
1921                 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1922                                 <<" got message for id="<<id<<", which doesn't exist."
1923                                 <<std::endl;
1924                 return;
1925         }
1926         obj->processMessage(data);
1927 }
1928
1929 /*
1930         Callbacks for activeobjects
1931 */
1932
1933 void ClientEnvironment::damageLocalPlayer(u8 damage)
1934 {
1935         LocalPlayer *lplayer = getLocalPlayer();
1936         assert(lplayer);
1937
1938         if(lplayer->hp > damage)
1939                 lplayer->hp -= damage;
1940         else
1941                 lplayer->hp = 0;
1942
1943         ClientEnvEvent event;
1944         event.type = CEE_PLAYER_DAMAGE;
1945         event.player_damage.amount = damage;
1946         m_client_event_queue.push_back(event);
1947 }
1948
1949 /*
1950         Client likes to call these
1951 */
1952         
1953 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1954                 core::array<DistanceSortedActiveObject> &dest)
1955 {
1956         for(core::map<u16, ClientActiveObject*>::Iterator
1957                         i = m_active_objects.getIterator();
1958                         i.atEnd()==false; i++)
1959         {
1960                 ClientActiveObject* obj = i.getNode()->getValue();
1961
1962                 f32 d = (obj->getPosition() - origin).getLength();
1963
1964                 if(d > max_d)
1965                         continue;
1966
1967                 DistanceSortedActiveObject dso(obj, d);
1968
1969                 dest.push_back(dso);
1970         }
1971 }
1972
1973 ClientEnvEvent ClientEnvironment::getClientEvent()
1974 {
1975         if(m_client_event_queue.size() == 0)
1976         {
1977                 ClientEnvEvent event;
1978                 event.type = CEE_NONE;
1979                 return event;
1980         }
1981         return m_client_event_queue.pop_front();
1982 }
1983
1984 void ClientEnvironment::drawPostFx(video::IVideoDriver* driver, v3f camera_pos)
1985 {
1986         /*LocalPlayer *player = getLocalPlayer();
1987         assert(player);
1988         v3f pos_f = player->getPosition() + v3f(0,BS*1.625,0);*/
1989         v3f pos_f = camera_pos;
1990         v3s16 p_nodes = floatToInt(pos_f, BS);
1991         MapNode n = m_map->getNodeNoEx(p_nodes);
1992         if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
1993         {
1994                 v2u32 ss = driver->getScreenSize();
1995                 core::rect<s32> rect(0,0, ss.X, ss.Y);
1996                 driver->draw2DRectangle(video::SColor(64, 100, 100, 200), rect);
1997         }
1998         else if(content_features(n).solidness == 2 &&
1999                         g_settings.getBool("free_move") == false)
2000         {
2001                 v2u32 ss = driver->getScreenSize();
2002                 core::rect<s32> rect(0,0, ss.X, ss.Y);
2003                 driver->draw2DRectangle(video::SColor(255, 0, 0, 0), rect);
2004         }
2005 }
2006
2007 #endif // #ifndef SERVER
2008
2009