]> git.lizzy.rs Git - minetest.git/blob - src/environment.cpp
work-in-progress texture atlas optimization
[minetest.git] / src / environment.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 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
23 Environment::Environment(Map *map, std::ostream &dout):
24                 m_dout(dout)
25 {
26         m_map = map;
27         m_daynight_ratio = 0.2;
28 }
29
30 Environment::~Environment()
31 {
32         // Deallocate players
33         for(core::list<Player*>::Iterator i = m_players.begin();
34                         i != m_players.end(); i++)
35         {
36                 delete (*i);
37         }
38         
39         // The map is removed by the SceneManager
40         m_map->drop();
41         //delete m_map;
42 }
43
44 void Environment::step(float dtime)
45 {
46         DSTACK(__FUNCTION_NAME);
47         /*
48                 Run Map's timers
49         */
50         //TimeTaker maptimerupdatetimer("m_map->timerUpdate()", g_device);
51         // 0ms
52         m_map->timerUpdate(dtime);
53         //maptimerupdatetimer.stop();
54
55         /*
56                 Get the highest speed some player is going
57         */
58         //TimeTaker playerspeed("playerspeed", g_device);
59         // 0ms
60         f32 maximum_player_speed = 0.001; // just some small value
61         for(core::list<Player*>::Iterator i = m_players.begin();
62                         i != m_players.end(); i++)
63         {
64                 f32 speed = (*i)->getSpeed().getLength();
65                 if(speed > maximum_player_speed)
66                         maximum_player_speed = speed;
67         }
68         //playerspeed.stop();
69         
70         // Maximum time increment (for collision detection etc)
71         // Allow 0.1 blocks per increment
72         // time = distance / speed
73         f32 dtime_max_increment = 0.1*BS / maximum_player_speed;
74         // Maximum time increment is 10ms or lower
75         if(dtime_max_increment > 0.01)
76                 dtime_max_increment = 0.01;
77         
78         //TimeTaker playerupdate("playerupdate", g_device);
79         
80         /*
81                 Stuff that has a maximum time increment
82         */
83         // Don't allow overly huge dtime
84         if(dtime > 0.5)
85                 dtime = 0.5;
86
87         u32 loopcount = 0;
88         do
89         {
90                 loopcount++;
91
92                 f32 dtime_part;
93                 if(dtime > dtime_max_increment)
94                         dtime_part = dtime_max_increment;
95                 else
96                         dtime_part = dtime;
97                 dtime -= dtime_part;
98                 
99                 /*
100                         Handle players
101                 */
102                 for(core::list<Player*>::Iterator i = m_players.begin();
103                                 i != m_players.end(); i++)
104                 {
105                         Player *player = *i;
106
107                         v3f playerpos = player->getPosition();
108                         
109                         // Apply physics to local player
110                         bool free_move = g_settings.getBool("free_move");
111                         if(player->isLocal() && free_move == false)
112                         {
113                                 // Apply gravity to local player
114                                 v3f speed = player->getSpeed();
115                                 if(player->swimming_up == false)
116                                         speed.Y -= 9.81 * BS * dtime_part * 2;
117
118                                 /*
119                                         Apply water resistance
120                                 */
121                                 if(player->in_water_stable || player->in_water)
122                                 {
123                                         f32 max_down = 2.0*BS;
124                                         if(speed.Y < -max_down) speed.Y = -max_down;
125
126                                         f32 max = 2.5*BS;
127                                         if(speed.getLength() > max)
128                                         {
129                                                 speed = speed / speed.getLength() * max;
130                                         }
131                                 }
132
133                                 player->setSpeed(speed);
134                         }
135
136                         /*
137                                 Move the player.
138                                 For local player, this also calculates collision detection.
139                         */
140                         player->move(dtime_part, *m_map);
141                         
142                         /*
143                                 Update lighting on remote players on client
144                         */
145                         u8 light = LIGHT_MAX;
146                         try{
147                                 // Get node at feet
148                                 v3s16 p = floatToInt(playerpos + v3f(0,BS/4,0));
149                                 MapNode n = m_map->getNode(p);
150                                 light = n.getLightBlend(m_daynight_ratio);
151                         }
152                         catch(InvalidPositionException &e) {}
153                         player->updateLight(light);
154
155                         /*
156                                 Add footsteps to grass
157                         */
158                         if(g_settings.getBool("footprints"))
159                         {
160                                 // Get node that is at BS/4 under player
161                                 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0));
162                                 try{
163                                         MapNode n = m_map->getNode(bottompos);
164                                         if(n.d == CONTENT_GRASS)
165                                         {
166                                                 n.d = CONTENT_GRASS_FOOTSTEPS;
167                                                 m_map->setNode(bottompos, n);
168 #ifndef SERVER
169                                                 // Update mesh on client
170                                                 if(m_map->mapType() == MAPTYPE_CLIENT)
171                                                 {
172                                                         v3s16 p_blocks = getNodeBlockPos(bottompos);
173                                                         MapBlock *b = m_map->getBlockNoCreate(p_blocks);
174                                                         b->updateMesh(m_daynight_ratio);
175                                                 }
176 #endif
177                                         }
178                                 }
179                                 catch(InvalidPositionException &e)
180                                 {
181                                 }
182                         }
183                 }
184         }
185         while(dtime > 0.001);
186         
187         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
188 }
189
190 Map & Environment::getMap()
191 {
192         return *m_map;
193 }
194
195 void Environment::addPlayer(Player *player)
196 {
197         DSTACK(__FUNCTION_NAME);
198         /*
199                 Check that only one local player exists and peer_ids are unique.
200                 Also check that names are unique.
201                 Exception: there can be multiple players with peer_id=0
202         */
203 #ifndef SERVER
204         /*
205                 It is a failure if player is local and there already is a local
206                 player
207         */
208         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
209 #endif
210         // If peer id is non-zero, it has to be unique.
211         if(player->peer_id != 0)
212                 assert(getPlayer(player->peer_id) == NULL);
213         // Name has to be unique.
214         assert(getPlayer(player->getName()) == NULL);
215         // Add.
216         m_players.push_back(player);
217 }
218
219 void Environment::removePlayer(u16 peer_id)
220 {
221         DSTACK(__FUNCTION_NAME);
222 re_search:
223         for(core::list<Player*>::Iterator i = m_players.begin();
224                         i != m_players.end(); i++)
225         {
226                 Player *player = *i;
227                 if(player->peer_id != peer_id)
228                         continue;
229                 
230                 delete player;
231                 m_players.erase(i);
232                 // See if there is an another one
233                 // (shouldn't be, but just to be sure)
234                 goto re_search;
235         }
236 }
237
238 #ifndef SERVER
239 LocalPlayer * Environment::getLocalPlayer()
240 {
241         for(core::list<Player*>::Iterator i = m_players.begin();
242                         i != m_players.end(); i++)
243         {
244                 Player *player = *i;
245                 if(player->isLocal())
246                         return (LocalPlayer*)player;
247         }
248         return NULL;
249 }
250 #endif
251
252 Player * Environment::getPlayer(u16 peer_id)
253 {
254         for(core::list<Player*>::Iterator i = m_players.begin();
255                         i != m_players.end(); i++)
256         {
257                 Player *player = *i;
258                 if(player->peer_id == peer_id)
259                         return player;
260         }
261         return NULL;
262 }
263
264 Player * Environment::getPlayer(const char *name)
265 {
266         for(core::list<Player*>::Iterator i = m_players.begin();
267                         i != m_players.end(); i++)
268         {
269                 Player *player = *i;
270                 if(strcmp(player->getName(), name) == 0)
271                         return player;
272         }
273         return NULL;
274 }
275
276 core::list<Player*> Environment::getPlayers()
277 {
278         return m_players;
279 }
280
281 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
282 {
283         core::list<Player*> newlist;
284         for(core::list<Player*>::Iterator
285                         i = m_players.begin();
286                         i != m_players.end(); i++)
287         {
288                 Player *player = *i;
289                 
290                 if(ignore_disconnected)
291                 {
292                         // Ignore disconnected players
293                         if(player->peer_id == 0)
294                                 continue;
295                 }
296
297                 newlist.push_back(player);
298         }
299         return newlist;
300 }
301
302 void Environment::printPlayers(std::ostream &o)
303 {
304         o<<"Players in environment:"<<std::endl;
305         for(core::list<Player*>::Iterator i = m_players.begin();
306                         i != m_players.end(); i++)
307         {
308                 Player *player = *i;
309                 o<<"Player peer_id="<<player->peer_id<<std::endl;
310         }
311 }
312
313 void Environment::serializePlayers(const std::string &savedir)
314 {
315         std::string players_path = savedir + "/players";
316         fs::CreateDir(players_path);
317
318         core::map<Player*, bool> saved_players;
319
320         std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
321         for(u32 i=0; i<player_files.size(); i++)
322         {
323                 if(player_files[i].dir)
324                         continue;
325                 
326                 // Full path to this file
327                 std::string path = players_path + "/" + player_files[i].name;
328
329                 //dstream<<"Checking player file "<<path<<std::endl;
330
331                 // Load player to see what is its name
332                 ServerRemotePlayer testplayer;
333                 {
334                         // Open file and deserialize
335                         std::ifstream is(path.c_str(), std::ios_base::binary);
336                         if(is.good() == false)
337                         {
338                                 dstream<<"Failed to read "<<path<<std::endl;
339                                 continue;
340                         }
341                         testplayer.deSerialize(is);
342                 }
343
344                 //dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
345                 
346                 // Search for the player
347                 std::string playername = testplayer.getName();
348                 Player *player = getPlayer(playername.c_str());
349                 if(player == NULL)
350                 {
351                         dstream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
352                         continue;
353                 }
354
355                 //dstream<<"Found matching player, overwriting."<<std::endl;
356
357                 // OK, found. Save player there.
358                 {
359                         // Open file and serialize
360                         std::ofstream os(path.c_str(), std::ios_base::binary);
361                         if(os.good() == false)
362                         {
363                                 dstream<<"Failed to overwrite "<<path<<std::endl;
364                                 continue;
365                         }
366                         player->serialize(os);
367                         saved_players.insert(player, true);
368                 }
369         }
370
371         for(core::list<Player*>::Iterator i = m_players.begin();
372                         i != m_players.end(); i++)
373         {
374                 Player *player = *i;
375                 if(saved_players.find(player) != NULL)
376                 {
377                         /*dstream<<"Player "<<player->getName()
378                                         <<" was already saved."<<std::endl;*/
379                         continue;
380                 }
381                 std::string playername = player->getName();
382                 // Don't save unnamed player
383                 if(playername == "")
384                 {
385                         //dstream<<"Not saving unnamed player."<<std::endl;
386                         continue;
387                 }
388                 /*
389                         Find a sane filename
390                 */
391                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
392                         playername = "player";
393                 std::string path = players_path + "/" + playername;
394                 bool found = false;
395                 for(u32 i=0; i<1000; i++)
396                 {
397                         if(fs::PathExists(path) == false)
398                         {
399                                 found = true;
400                                 break;
401                         }
402                         path = players_path + "/" + playername + itos(i);
403                 }
404                 if(found == false)
405                 {
406                         dstream<<"WARNING: Didn't find free file for player"<<std::endl;
407                         continue;
408                 }
409
410                 {
411                         /*dstream<<"Saving player "<<player->getName()<<" to "
412                                         <<path<<std::endl;*/
413                         // Open file and serialize
414                         std::ofstream os(path.c_str(), std::ios_base::binary);
415                         if(os.good() == false)
416                         {
417                                 dstream<<"WARNING: Failed to overwrite "<<path<<std::endl;
418                                 continue;
419                         }
420                         player->serialize(os);
421                         saved_players.insert(player, true);
422                 }
423         }
424
425         //dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
426 }
427
428 void Environment::deSerializePlayers(const std::string &savedir)
429 {
430         std::string players_path = savedir + "/players";
431
432         core::map<Player*, bool> saved_players;
433
434         std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
435         for(u32 i=0; i<player_files.size(); i++)
436         {
437                 if(player_files[i].dir)
438                         continue;
439                 
440                 // Full path to this file
441                 std::string path = players_path + "/" + player_files[i].name;
442
443                 dstream<<"Checking player file "<<path<<std::endl;
444
445                 // Load player to see what is its name
446                 ServerRemotePlayer testplayer;
447                 {
448                         // Open file and deserialize
449                         std::ifstream is(path.c_str(), std::ios_base::binary);
450                         if(is.good() == false)
451                         {
452                                 dstream<<"Failed to read "<<path<<std::endl;
453                                 continue;
454                         }
455                         testplayer.deSerialize(is);
456                 }
457
458                 dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
459                 
460                 // Search for the player
461                 std::string playername = testplayer.getName();
462                 Player *player = getPlayer(playername.c_str());
463                 bool newplayer = false;
464                 if(player == NULL)
465                 {
466                         dstream<<"Is a new player"<<std::endl;
467                         player = new ServerRemotePlayer();
468                         newplayer = true;
469                 }
470
471                 // Load player
472                 {
473                         dstream<<"Reading player "<<testplayer.getName()<<" from "
474                                         <<path<<std::endl;
475                         // Open file and deserialize
476                         std::ifstream is(path.c_str(), std::ios_base::binary);
477                         if(is.good() == false)
478                         {
479                                 dstream<<"Failed to read "<<path<<std::endl;
480                                 continue;
481                         }
482                         player->deSerialize(is);
483                 }
484
485                 if(newplayer)
486                         addPlayer(player);
487         }
488 }
489
490 #ifndef SERVER
491 void Environment::updateMeshes(v3s16 blockpos)
492 {
493         m_map->updateMeshes(blockpos, m_daynight_ratio);
494 }
495
496 void Environment::expireMeshes(bool only_daynight_diffed)
497 {
498         m_map->expireMeshes(only_daynight_diffed);
499 }
500 #endif
501
502 void Environment::setDayNightRatio(u32 r)
503 {
504         m_daynight_ratio = r;
505 }
506
507 u32 Environment::getDayNightRatio()
508 {
509         return m_daynight_ratio;
510 }
511