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