]> git.lizzy.rs Git - dragonfireclient.git/blob - src/environment.cpp
24ec667574814cd08efeaba27995986860c3193d
[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
25
26 Environment::Environment()
27 {
28         m_daynight_ratio = 0.5;
29 }
30
31 Environment::~Environment()
32 {
33         // Deallocate players
34         for(core::list<Player*>::Iterator i = m_players.begin();
35                         i != m_players.end(); i++)
36         {
37                 delete (*i);
38         }
39 }
40
41 void Environment::addPlayer(Player *player)
42 {
43         DSTACK(__FUNCTION_NAME);
44         /*
45                 Check that peer_ids are unique.
46                 Also check that names are unique.
47                 Exception: there can be multiple players with peer_id=0
48         */
49         // If peer id is non-zero, it has to be unique.
50         if(player->peer_id != 0)
51                 assert(getPlayer(player->peer_id) == NULL);
52         // Name has to be unique.
53         assert(getPlayer(player->getName()) == NULL);
54         // Add.
55         m_players.push_back(player);
56 }
57
58 void Environment::removePlayer(u16 peer_id)
59 {
60         DSTACK(__FUNCTION_NAME);
61 re_search:
62         for(core::list<Player*>::Iterator i = m_players.begin();
63                         i != m_players.end(); i++)
64         {
65                 Player *player = *i;
66                 if(player->peer_id != peer_id)
67                         continue;
68                 
69                 delete player;
70                 m_players.erase(i);
71                 // See if there is an another one
72                 // (shouldn't be, but just to be sure)
73                 goto re_search;
74         }
75 }
76
77 Player * Environment::getPlayer(u16 peer_id)
78 {
79         for(core::list<Player*>::Iterator i = m_players.begin();
80                         i != m_players.end(); i++)
81         {
82                 Player *player = *i;
83                 if(player->peer_id == peer_id)
84                         return player;
85         }
86         return NULL;
87 }
88
89 Player * Environment::getPlayer(const char *name)
90 {
91         for(core::list<Player*>::Iterator i = m_players.begin();
92                         i != m_players.end(); i++)
93         {
94                 Player *player = *i;
95                 if(strcmp(player->getName(), name) == 0)
96                         return player;
97         }
98         return NULL;
99 }
100
101 Player * Environment::getRandomConnectedPlayer()
102 {
103         core::list<Player*> connected_players = getPlayers(true);
104         u32 chosen_one = myrand() % connected_players.size();
105         u32 j = 0;
106         for(core::list<Player*>::Iterator
107                         i = connected_players.begin();
108                         i != connected_players.end(); i++)
109         {
110                 if(j == chosen_one)
111                 {
112                         Player *player = *i;
113                         return player;
114                 }
115                 j++;
116         }
117         return NULL;
118 }
119
120 Player * Environment::getNearestConnectedPlayer(v3f pos)
121 {
122         core::list<Player*> connected_players = getPlayers(true);
123         f32 nearest_d = 0;
124         Player *nearest_player = NULL;
125         for(core::list<Player*>::Iterator
126                         i = connected_players.begin();
127                         i != connected_players.end(); i++)
128         {
129                 Player *player = *i;
130                 f32 d = player->getPosition().getDistanceFrom(pos);
131                 if(d < nearest_d || nearest_player == NULL)
132                 {
133                         nearest_d = d;
134                         nearest_player = player;
135                 }
136         }
137         return nearest_player;
138 }
139
140 core::list<Player*> Environment::getPlayers()
141 {
142         return m_players;
143 }
144
145 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
146 {
147         core::list<Player*> newlist;
148         for(core::list<Player*>::Iterator
149                         i = m_players.begin();
150                         i != m_players.end(); i++)
151         {
152                 Player *player = *i;
153                 
154                 if(ignore_disconnected)
155                 {
156                         // Ignore disconnected players
157                         if(player->peer_id == 0)
158                                 continue;
159                 }
160
161                 newlist.push_back(player);
162         }
163         return newlist;
164 }
165
166 void Environment::printPlayers(std::ostream &o)
167 {
168         o<<"Players in environment:"<<std::endl;
169         for(core::list<Player*>::Iterator i = m_players.begin();
170                         i != m_players.end(); i++)
171         {
172                 Player *player = *i;
173                 o<<"Player peer_id="<<player->peer_id<<std::endl;
174         }
175 }
176
177 void Environment::setDayNightRatio(u32 r)
178 {
179         m_daynight_ratio = r;
180 }
181
182 u32 Environment::getDayNightRatio()
183 {
184         return m_daynight_ratio;
185 }
186
187 /*
188         ServerEnvironment
189 */
190
191 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
192         m_map(map),
193         m_server(server),
194         m_random_spawn_timer(3),
195         m_send_recommended_timer(0)
196 {
197 }
198
199 ServerEnvironment::~ServerEnvironment()
200 {
201         /*
202                 Convert all objects to static (thus delete the active objects)
203         */
204         deactivateFarObjects(-1);
205
206         // Drop/delete map
207         m_map->drop();
208 }
209
210 void ServerEnvironment::serializePlayers(const std::string &savedir)
211 {
212         std::string players_path = savedir + "/players";
213         fs::CreateDir(players_path);
214
215         core::map<Player*, bool> saved_players;
216
217         std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
218         for(u32 i=0; i<player_files.size(); i++)
219         {
220                 if(player_files[i].dir)
221                         continue;
222                 
223                 // Full path to this file
224                 std::string path = players_path + "/" + player_files[i].name;
225
226                 //dstream<<"Checking player file "<<path<<std::endl;
227
228                 // Load player to see what is its name
229                 ServerRemotePlayer testplayer;
230                 {
231                         // Open file and deserialize
232                         std::ifstream is(path.c_str(), std::ios_base::binary);
233                         if(is.good() == false)
234                         {
235                                 dstream<<"Failed to read "<<path<<std::endl;
236                                 continue;
237                         }
238                         testplayer.deSerialize(is);
239                 }
240
241                 //dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
242                 
243                 // Search for the player
244                 std::string playername = testplayer.getName();
245                 Player *player = getPlayer(playername.c_str());
246                 if(player == NULL)
247                 {
248                         dstream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
249                         continue;
250                 }
251
252                 //dstream<<"Found matching player, overwriting."<<std::endl;
253
254                 // OK, found. Save player there.
255                 {
256                         // Open file and serialize
257                         std::ofstream os(path.c_str(), std::ios_base::binary);
258                         if(os.good() == false)
259                         {
260                                 dstream<<"Failed to overwrite "<<path<<std::endl;
261                                 continue;
262                         }
263                         player->serialize(os);
264                         saved_players.insert(player, true);
265                 }
266         }
267
268         for(core::list<Player*>::Iterator i = m_players.begin();
269                         i != m_players.end(); i++)
270         {
271                 Player *player = *i;
272                 if(saved_players.find(player) != NULL)
273                 {
274                         /*dstream<<"Player "<<player->getName()
275                                         <<" was already saved."<<std::endl;*/
276                         continue;
277                 }
278                 std::string playername = player->getName();
279                 // Don't save unnamed player
280                 if(playername == "")
281                 {
282                         //dstream<<"Not saving unnamed player."<<std::endl;
283                         continue;
284                 }
285                 /*
286                         Find a sane filename
287                 */
288                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
289                         playername = "player";
290                 std::string path = players_path + "/" + playername;
291                 bool found = false;
292                 for(u32 i=0; i<1000; i++)
293                 {
294                         if(fs::PathExists(path) == false)
295                         {
296                                 found = true;
297                                 break;
298                         }
299                         path = players_path + "/" + playername + itos(i);
300                 }
301                 if(found == false)
302                 {
303                         dstream<<"WARNING: Didn't find free file for player"<<std::endl;
304                         continue;
305                 }
306
307                 {
308                         /*dstream<<"Saving player "<<player->getName()<<" to "
309                                         <<path<<std::endl;*/
310                         // Open file and serialize
311                         std::ofstream os(path.c_str(), std::ios_base::binary);
312                         if(os.good() == false)
313                         {
314                                 dstream<<"WARNING: Failed to overwrite "<<path<<std::endl;
315                                 continue;
316                         }
317                         player->serialize(os);
318                         saved_players.insert(player, true);
319                 }
320         }
321
322         //dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
323 }
324
325 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
326 {
327         std::string players_path = savedir + "/players";
328
329         core::map<Player*, bool> saved_players;
330
331         std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
332         for(u32 i=0; i<player_files.size(); i++)
333         {
334                 if(player_files[i].dir)
335                         continue;
336                 
337                 // Full path to this file
338                 std::string path = players_path + "/" + player_files[i].name;
339
340                 dstream<<"Checking player file "<<path<<std::endl;
341
342                 // Load player to see what is its name
343                 ServerRemotePlayer testplayer;
344                 {
345                         // Open file and deserialize
346                         std::ifstream is(path.c_str(), std::ios_base::binary);
347                         if(is.good() == false)
348                         {
349                                 dstream<<"Failed to read "<<path<<std::endl;
350                                 continue;
351                         }
352                         testplayer.deSerialize(is);
353                 }
354
355                 dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
356                 
357                 // Search for the player
358                 std::string playername = testplayer.getName();
359                 Player *player = getPlayer(playername.c_str());
360                 bool newplayer = false;
361                 if(player == NULL)
362                 {
363                         dstream<<"Is a new player"<<std::endl;
364                         player = new ServerRemotePlayer();
365                         newplayer = true;
366                 }
367
368                 // Load player
369                 {
370                         dstream<<"Reading player "<<testplayer.getName()<<" from "
371                                         <<path<<std::endl;
372                         // Open file and deserialize
373                         std::ifstream is(path.c_str(), std::ios_base::binary);
374                         if(is.good() == false)
375                         {
376                                 dstream<<"Failed to read "<<path<<std::endl;
377                                 continue;
378                         }
379                         player->deSerialize(is);
380                 }
381
382                 if(newplayer)
383                         addPlayer(player);
384         }
385 }
386
387 #if 0
388 void spawnRandomObjects(MapBlock *block)
389 {
390         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
391         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
392         {
393                 bool last_node_walkable = false;
394                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
395                 {
396                         v3s16 p(x0,y0,z0);
397                         MapNode n = block->getNodeNoEx(p);
398                         if(n.d == CONTENT_IGNORE)
399                                 continue;
400                         if(content_features(n.d).liquid_type != LIQUID_NONE)
401                                 continue;
402                         if(content_features(n.d).walkable)
403                         {
404                                 last_node_walkable = true;
405                                 continue;
406                         }
407                         if(last_node_walkable)
408                         {
409                                 // If block contains light information
410                                 if(content_features(n.d).param_type == CPT_LIGHT)
411                                 {
412                                         if(n.getLight(LIGHTBANK_DAY) <= 5)
413                                         {
414                                                 if(myrand() % 1000 == 0)
415                                                 {
416                                                         v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
417                                                         pos_f.Y -= BS*0.4;
418                                                         ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
419                                                         std::string data = obj->getStaticData();
420                                                         StaticObject s_obj(obj->getType(),
421                                                                         obj->getBasePosition(), data);
422                                                         // Add one
423                                                         block->m_static_objects.insert(0, s_obj);
424                                                         delete obj;
425                                                         block->setChangedFlag();
426                                                 }
427                                         }
428                                 }
429                         }
430                         last_node_walkable = false;
431                 }
432         }
433 }
434 #endif
435
436 void ServerEnvironment::step(float dtime)
437 {
438         DSTACK(__FUNCTION_NAME);
439         
440         //TimeTaker timer("ServerEnv step");
441
442         // Get some settings
443         //bool free_move = g_settings.getBool("free_move");
444         bool footprints = g_settings.getBool("footprints");
445
446         {
447                 //TimeTaker timer("Server m_map->timerUpdate()");
448                 m_map->timerUpdate(dtime);
449         }
450
451         /*
452                 Handle players
453         */
454         for(core::list<Player*>::Iterator i = m_players.begin();
455                         i != m_players.end(); i++)
456         {
457                 Player *player = *i;
458                 
459                 // Ignore disconnected players
460                 if(player->peer_id == 0)
461                         continue;
462
463                 v3f playerpos = player->getPosition();
464                 
465                 // Move
466                 player->move(dtime, *m_map, 100*BS);
467                 
468                 /*
469                         Add footsteps to grass
470                 */
471                 if(footprints)
472                 {
473                         // Get node that is at BS/4 under player
474                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
475                         try{
476                                 MapNode n = m_map->getNode(bottompos);
477                                 if(n.d == CONTENT_GRASS)
478                                 {
479                                         n.d = CONTENT_GRASS_FOOTSTEPS;
480                                         m_map->setNode(bottompos, n);
481                                 }
482                         }
483                         catch(InvalidPositionException &e)
484                         {
485                         }
486                 }
487         }
488
489         /*
490                 Step active objects
491         */
492         {
493                 //TimeTaker timer("Step active objects");
494
495                 bool send_recommended = false;
496                 m_send_recommended_timer += dtime;
497                 if(m_send_recommended_timer > 0.15)
498                 {
499                         m_send_recommended_timer = 0;
500                         send_recommended = true;
501                 }
502
503                 for(core::map<u16, ServerActiveObject*>::Iterator
504                                 i = m_active_objects.getIterator();
505                                 i.atEnd()==false; i++)
506                 {
507                         ServerActiveObject* obj = i.getNode()->getValue();
508                         // Step object, putting messages directly to the queue
509                         obj->step(dtime, m_active_object_messages, send_recommended);
510                 }
511         }
512
513         if(m_object_management_interval.step(dtime, 0.5))
514         {
515                 //TimeTaker timer("ServerEnv object management");
516
517                 const s16 to_active_range_blocks = 3;
518                 const s16 to_static_range_blocks = 4;
519                 //const f32 to_static_max_f = (to_active_max_blocks+2)*MAP_BLOCKSIZE*BS;
520
521                 /*
522                         Remove objects that satisfy (m_removed && m_known_by_count==0)
523                 */
524                 removeRemovedObjects();
525                 
526                 /*
527                         Convert stored objects from blocks near the players to active.
528                 */
529                 activateNearObjects(to_active_range_blocks);
530
531                 /*
532                         Convert objects that are far away from all the players to static.
533                 */
534                 deactivateFarObjects(to_static_range_blocks);
535         }
536
537         if(g_settings.getBool("enable_experimental"))
538         {
539
540         /*
541                 TEST CODE
542         */
543 #if 1
544         m_random_spawn_timer -= dtime;
545         if(m_random_spawn_timer < 0)
546         {
547                 //m_random_spawn_timer += myrand_range(2.0, 20.0);
548                 //m_random_spawn_timer += 2.0;
549                 m_random_spawn_timer += 200.0;
550
551                 /*
552                         Find some position
553                 */
554
555                 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
556                 s16 y = 1 + getServerMap().findGroundLevel(p2d);
557                 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
558                 
559                 Player *player = getRandomConnectedPlayer();
560                 v3f pos(0,0,0);
561                 if(player)
562                         pos = player->getPosition();
563                 pos += v3f(
564                         myrand_range(-3,3)*BS,
565                         0,
566                         myrand_range(-3,3)*BS
567                 );
568
569                 /*
570                         Create a ServerActiveObject
571                 */
572
573                 //TestSAO *obj = new TestSAO(this, 0, pos);
574                 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
575                 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
576                 ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
577                 addActiveObject(obj);
578         }
579 #endif
580
581         } // enable_experimental
582 }
583
584 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
585 {
586         core::map<u16, ServerActiveObject*>::Node *n;
587         n = m_active_objects.find(id);
588         if(n == NULL)
589                 return NULL;
590         return n->getValue();
591 }
592
593 bool isFreeServerActiveObjectId(u16 id,
594                 core::map<u16, ServerActiveObject*> &objects)
595 {
596         if(id == 0)
597                 return false;
598         
599         for(core::map<u16, ServerActiveObject*>::Iterator
600                         i = objects.getIterator();
601                         i.atEnd()==false; i++)
602         {
603                 if(i.getNode()->getKey() == id)
604                         return false;
605         }
606         return true;
607 }
608
609 u16 getFreeServerActiveObjectId(
610                 core::map<u16, ServerActiveObject*> &objects)
611 {
612         u16 new_id = 1;
613         for(;;)
614         {
615                 if(isFreeServerActiveObjectId(new_id, objects))
616                         return new_id;
617                 
618                 if(new_id == 65535)
619                         return 0;
620
621                 new_id++;
622         }
623 }
624
625 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
626 {
627         assert(object);
628         if(object->getId() == 0)
629         {
630                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
631                 if(new_id == 0)
632                 {
633                         dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
634                                         <<"no free ids available"<<std::endl;
635                         delete object;
636                         return 0;
637                 }
638                 object->setId(new_id);
639         }
640         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
641         {
642                 dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
643                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
644                 delete object;
645                 return 0;
646         }
647         /*dstream<<"INGO: ServerEnvironment::addActiveObject(): "
648                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
649                         
650         m_active_objects.insert(object->getId(), object);
651
652         // Add static object to active static list of the block
653         v3f objectpos = object->getBasePosition();
654         std::string staticdata = object->getStaticData();
655         StaticObject s_obj(object->getType(), objectpos, staticdata);
656         // Add to the block where the object is located in
657         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
658         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
659         if(block)
660         {
661                 block->m_static_objects.m_active.insert(object->getId(), s_obj);
662                 object->m_static_exists = true;
663                 object->m_static_block = blockpos;
664         }
665         else{
666                 dstream<<"WARNING: Server: Could not find a block for "
667                                 <<"storing newly added static active object"<<std::endl;
668         }
669
670         return object->getId();
671 }
672
673 /*
674         Finds out what new objects have been added to
675         inside a radius around a position
676 */
677 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
678                 core::map<u16, bool> &current_objects,
679                 core::map<u16, bool> &added_objects)
680 {
681         v3f pos_f = intToFloat(pos, BS);
682         f32 radius_f = radius * BS;
683         /*
684                 Go through the object list,
685                 - discard m_removed objects,
686                 - discard objects that are too far away,
687                 - discard objects that are found in current_objects.
688                 - add remaining objects to added_objects
689         */
690         for(core::map<u16, ServerActiveObject*>::Iterator
691                         i = m_active_objects.getIterator();
692                         i.atEnd()==false; i++)
693         {
694                 u16 id = i.getNode()->getKey();
695                 // Get object
696                 ServerActiveObject *object = i.getNode()->getValue();
697                 if(object == NULL)
698                         continue;
699                 // Discard if removed
700                 if(object->m_removed)
701                         continue;
702                 // Discard if too far
703                 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
704                 if(distance_f > radius_f)
705                         continue;
706                 // Discard if already on current_objects
707                 core::map<u16, bool>::Node *n;
708                 n = current_objects.find(id);
709                 if(n != NULL)
710                         continue;
711                 // Add to added_objects
712                 added_objects.insert(id, false);
713         }
714 }
715
716 /*
717         Finds out what objects have been removed from
718         inside a radius around a position
719 */
720 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
721                 core::map<u16, bool> &current_objects,
722                 core::map<u16, bool> &removed_objects)
723 {
724         v3f pos_f = intToFloat(pos, BS);
725         f32 radius_f = radius * BS;
726         /*
727                 Go through current_objects; object is removed if:
728                 - object is not found in m_active_objects (this is actually an
729                   error condition; objects should be set m_removed=true and removed
730                   only after all clients have been informed about removal), or
731                 - object has m_removed=true, or
732                 - object is too far away
733         */
734         for(core::map<u16, bool>::Iterator
735                         i = current_objects.getIterator();
736                         i.atEnd()==false; i++)
737         {
738                 u16 id = i.getNode()->getKey();
739                 ServerActiveObject *object = getActiveObject(id);
740                 if(object == NULL)
741                 {
742                         dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
743                                         <<" object in current_objects is NULL"<<std::endl;
744                 }
745                 else if(object->m_removed == false)
746                 {
747                         f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
748                         /*dstream<<"removed == false"
749                                         <<"distance_f = "<<distance_f
750                                         <<", radius_f = "<<radius_f<<std::endl;*/
751                         if(distance_f < radius_f)
752                         {
753                                 // Not removed
754                                 continue;
755                         }
756                 }
757                 removed_objects.insert(id, false);
758         }
759 }
760
761 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
762 {
763         if(m_active_object_messages.size() == 0)
764                 return ActiveObjectMessage(0);
765         
766         return m_active_object_messages.pop_front();
767 }
768
769 /*
770         ************ Private methods *************
771 */
772
773 /*
774         Remove objects that satisfy (m_removed && m_known_by_count==0)
775 */
776 void ServerEnvironment::removeRemovedObjects()
777 {
778         core::list<u16> objects_to_remove;
779         for(core::map<u16, ServerActiveObject*>::Iterator
780                         i = m_active_objects.getIterator();
781                         i.atEnd()==false; i++)
782         {
783                 u16 id = i.getNode()->getKey();
784                 ServerActiveObject* obj = i.getNode()->getValue();
785                 // This shouldn't happen but check it
786                 if(obj == NULL)
787                 {
788                         dstream<<"WARNING: NULL object found in ServerEnvironment"
789                                         <<" while finding removed objects. id="<<id<<std::endl;
790                         // Id to be removed from m_active_objects
791                         objects_to_remove.push_back(id);
792                         continue;
793                 }
794                 // If not m_removed, don't remove.
795                 if(obj->m_removed == false)
796                         continue;
797                 // Delete static data from block
798                 if(obj->m_static_exists)
799                 {
800                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
801                         if(block)
802                         {
803                                 block->m_static_objects.remove(id);
804                                 block->setChangedFlag();
805                         }
806                 }
807                 // If m_known_by_count > 0, don't actually remove.
808                 if(obj->m_known_by_count > 0)
809                         continue;
810                 // Delete
811                 delete obj;
812                 // Id to be removed from m_active_objects
813                 objects_to_remove.push_back(id);
814         }
815         // Remove references from m_active_objects
816         for(core::list<u16>::Iterator i = objects_to_remove.begin();
817                         i != objects_to_remove.end(); i++)
818         {
819                 m_active_objects.remove(*i);
820         }
821 }
822
823 /*
824         Convert stored objects from blocks near the players to active.
825 */
826 void ServerEnvironment::activateNearObjects(s16 range_blocks)
827 {
828         for(core::list<Player*>::Iterator i = m_players.begin();
829                         i != m_players.end(); i++)
830         {
831                 Player *player = *i;
832                 
833                 // Ignore disconnected players
834                 if(player->peer_id == 0)
835                         continue;
836
837                 v3f playerpos = player->getPosition();
838                 
839                 v3s16 blockpos0 = getNodeBlockPos(floatToInt(playerpos, BS));
840                 v3s16 bpmin = blockpos0 - v3s16(1,1,1)*range_blocks;
841                 v3s16 bpmax = blockpos0 + v3s16(1,1,1)*range_blocks;
842                 // Loop through all nearby blocks
843                 for(s16 x=bpmin.X; x<=bpmax.X; x++)
844                 for(s16 y=bpmin.Y; y<=bpmax.Y; y++)
845                 for(s16 z=bpmin.Z; z<=bpmax.Z; z++)
846                 {
847                         v3s16 blockpos(x,y,z);
848                         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
849                         if(block==NULL)
850                                 continue;
851                         // Ignore if no stored objects (to not set changed flag)
852                         if(block->m_static_objects.m_stored.size() == 0)
853                                 continue;
854                         // This will contain the leftovers of the stored list
855                         core::list<StaticObject> new_stored;
856                         // Loop through stored static objects
857                         for(core::list<StaticObject>::Iterator
858                                         i = block->m_static_objects.m_stored.begin();
859                                         i != block->m_static_objects.m_stored.end(); i++)
860                         {
861                                 /*dstream<<"INFO: Server: Creating an active object from "
862                                                 <<"static data"<<std::endl;*/
863                                 StaticObject &s_obj = *i;
864                                 // Create an active object from the data
865                                 ServerActiveObject *obj = ServerActiveObject::create
866                                                 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
867                                 if(obj==NULL)
868                                 {
869                                         // This is necessary to preserve stuff during bugs
870                                         // and errors
871                                         new_stored.push_back(s_obj);
872                                         continue;
873                                 }
874                                 // This will also add the object to the active static list
875                                 addActiveObject(obj);
876                                 //u16 id = addActiveObject(obj);
877                         }
878                         // Clear stored list
879                         block->m_static_objects.m_stored.clear();
880                         // Add leftover stuff to stored list
881                         for(core::list<StaticObject>::Iterator
882                                         i = new_stored.begin();
883                                         i != new_stored.end(); i++)
884                         {
885                                 StaticObject &s_obj = *i;
886                                 block->m_static_objects.m_stored.push_back(s_obj);
887                         }
888                         block->setChangedFlag();
889                 }
890         }
891 }
892
893 /*
894         Convert objects that are far away from all the players to static.
895
896         If range_blocks == -1, convert everything to static even if known
897         by a player.
898 */
899 void ServerEnvironment::deactivateFarObjects(s16 range_blocks)
900 {
901         bool force_everything = (range_blocks == -1);
902         core::list<u16> objects_to_remove;
903         for(core::map<u16, ServerActiveObject*>::Iterator
904                         i = m_active_objects.getIterator();
905                         i.atEnd()==false; i++)
906         {
907                 ServerActiveObject* obj = i.getNode()->getValue();
908                 u16 id = i.getNode()->getKey();
909                 v3f objectpos = obj->getBasePosition();
910
911                 // This shouldn't happen but check it
912                 if(obj == NULL)
913                 {
914                         dstream<<"WARNING: NULL object found in ServerEnvironment"
915                                         <<std::endl;
916                         assert(0);
917                         continue;
918                 }
919
920                 if(force_everything == false)
921                 {
922                         // If known by some client, don't convert to static.
923                         if(obj->m_known_by_count > 0)
924                                 continue;
925                         
926                         // If close to some player, don't convert to static.
927                         bool close_to_player = false;
928                         for(core::list<Player*>::Iterator i = m_players.begin();
929                                         i != m_players.end(); i++)
930                         {
931                                 Player *player = *i;
932                                 
933                                 // Ignore disconnected players
934                                 if(player->peer_id == 0)
935                                         continue;
936
937                                 v3f playerpos = player->getPosition();
938
939                                 v3s16 blockpos_p = getNodeBlockPos(floatToInt(playerpos, BS));
940                                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
941
942                                 if(blockpos_p.X >= blockpos_o.X - range_blocks
943                                         && blockpos_p.Y >= blockpos_o.Y - range_blocks
944                                         && blockpos_p.Z >= blockpos_o.Z - range_blocks
945                                         && blockpos_p.X <= blockpos_o.X + range_blocks
946                                         && blockpos_p.Y <= blockpos_o.Y + range_blocks
947                                         && blockpos_p.Z <= blockpos_o.Z + range_blocks)
948                                 {
949                                         close_to_player = true;
950                                         break;
951                                 }
952                         }
953
954                         if(close_to_player)
955                                 continue;
956                 }
957
958                 /*
959                         Update the static data and remove the active object.
960                 */
961
962                 // Delete old static object
963                 MapBlock *oldblock = NULL;
964                 if(obj->m_static_exists)
965                 {
966                         MapBlock *block = m_map->getBlockNoCreateNoEx
967                                         (obj->m_static_block);
968                         if(block)
969                         {
970                                 block->m_static_objects.remove(id);
971                                 oldblock = block;
972                         }
973                 }
974                 // Add new static object
975                 std::string staticdata = obj->getStaticData();
976                 StaticObject s_obj(obj->getType(), objectpos, staticdata);
977                 // Add to the block where the object is located in
978                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
979                 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
980                 if(block)
981                 {
982                         block->m_static_objects.insert(0, s_obj);
983                         block->setChangedFlag();
984                         obj->m_static_exists = true;
985                         obj->m_static_block = block->getPos();
986                 }
987                 // If not possible, add back to previous block
988                 else if(oldblock)
989                 {
990                         oldblock->m_static_objects.insert(0, s_obj);
991                         oldblock->setChangedFlag();
992                         obj->m_static_exists = true;
993                         obj->m_static_block = oldblock->getPos();
994                 }
995                 else{
996                         dstream<<"WARNING: Server: Could not find a block for "
997                                         <<"storing static object"<<std::endl;
998                         obj->m_static_exists = false;
999                         continue;
1000                 }
1001                 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1002                                 <<std::endl;*/
1003                 // Delete active object
1004                 delete obj;
1005                 // Id to be removed from m_active_objects
1006                 objects_to_remove.push_back(id);
1007         }
1008         // Remove references from m_active_objects
1009         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1010                         i != objects_to_remove.end(); i++)
1011         {
1012                 m_active_objects.remove(*i);
1013         }
1014 }
1015
1016
1017 #ifndef SERVER
1018
1019 /*
1020         ClientEnvironment
1021 */
1022
1023 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1024         m_map(map),
1025         m_smgr(smgr)
1026 {
1027         assert(m_map);
1028         assert(m_smgr);
1029 }
1030
1031 ClientEnvironment::~ClientEnvironment()
1032 {
1033         // delete active objects
1034         for(core::map<u16, ClientActiveObject*>::Iterator
1035                         i = m_active_objects.getIterator();
1036                         i.atEnd()==false; i++)
1037         {
1038                 delete i.getNode()->getValue();
1039         }
1040
1041         // Drop/delete map
1042         m_map->drop();
1043 }
1044
1045 void ClientEnvironment::addPlayer(Player *player)
1046 {
1047         DSTACK(__FUNCTION_NAME);
1048         /*
1049                 It is a failure if player is local and there already is a local
1050                 player
1051         */
1052         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1053
1054         Environment::addPlayer(player);
1055 }
1056
1057 LocalPlayer * ClientEnvironment::getLocalPlayer()
1058 {
1059         for(core::list<Player*>::Iterator i = m_players.begin();
1060                         i != m_players.end(); i++)
1061         {
1062                 Player *player = *i;
1063                 if(player->isLocal())
1064                         return (LocalPlayer*)player;
1065         }
1066         return NULL;
1067 }
1068
1069 void ClientEnvironment::step(float dtime)
1070 {
1071         DSTACK(__FUNCTION_NAME);
1072
1073         // Get some settings
1074         bool free_move = g_settings.getBool("free_move");
1075         bool footprints = g_settings.getBool("footprints");
1076
1077         {
1078                 //TimeTaker timer("Client m_map->timerUpdate()");
1079                 m_map->timerUpdate(dtime);
1080         }
1081         
1082         // Get local player
1083         LocalPlayer *lplayer = getLocalPlayer();
1084         assert(lplayer);
1085         // collision info queue
1086         core::list<CollisionInfo> player_collisions;
1087         
1088         /*
1089                 Get the speed the player is going
1090         */
1091         f32 player_speed = 0.001; // just some small value
1092         player_speed = lplayer->getSpeed().getLength();
1093         
1094         /*
1095                 Maximum position increment
1096         */
1097         //f32 position_max_increment = 0.05*BS;
1098         f32 position_max_increment = 0.1*BS;
1099
1100         // Maximum time increment (for collision detection etc)
1101         // time = distance / speed
1102         f32 dtime_max_increment = position_max_increment / player_speed;
1103         
1104         // Maximum time increment is 10ms or lower
1105         if(dtime_max_increment > 0.01)
1106                 dtime_max_increment = 0.01;
1107         
1108         // Don't allow overly huge dtime
1109         if(dtime > 0.5)
1110                 dtime = 0.5;
1111         
1112         f32 dtime_downcount = dtime;
1113
1114         /*
1115                 Stuff that has a maximum time increment
1116         */
1117
1118         u32 loopcount = 0;
1119         do
1120         {
1121                 loopcount++;
1122
1123                 f32 dtime_part;
1124                 if(dtime_downcount > dtime_max_increment)
1125                 {
1126                         dtime_part = dtime_max_increment;
1127                         dtime_downcount -= dtime_part;
1128                 }
1129                 else
1130                 {
1131                         dtime_part = dtime_downcount;
1132                         /*
1133                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
1134                                 when dtime_part is so small that dtime_downcount -= dtime_part
1135                                 does nothing
1136                         */
1137                         dtime_downcount = 0;
1138                 }
1139                 
1140                 /*
1141                         Handle local player
1142                 */
1143                 
1144                 {
1145                         v3f lplayerpos = lplayer->getPosition();
1146                         
1147                         // Apply physics
1148                         if(free_move == false)
1149                         {
1150                                 // Gravity
1151                                 v3f speed = lplayer->getSpeed();
1152                                 if(lplayer->swimming_up == false)
1153                                         speed.Y -= 9.81 * BS * dtime_part * 2;
1154
1155                                 // Water resistance
1156                                 if(lplayer->in_water_stable || lplayer->in_water)
1157                                 {
1158                                         f32 max_down = 2.0*BS;
1159                                         if(speed.Y < -max_down) speed.Y = -max_down;
1160
1161                                         f32 max = 2.5*BS;
1162                                         if(speed.getLength() > max)
1163                                         {
1164                                                 speed = speed / speed.getLength() * max;
1165                                         }
1166                                 }
1167
1168                                 lplayer->setSpeed(speed);
1169                         }
1170
1171                         /*
1172                                 Move the lplayer.
1173                                 This also does collision detection.
1174                         */
1175                         lplayer->move(dtime_part, *m_map, position_max_increment,
1176                                         &player_collisions);
1177                 }
1178         }
1179         while(dtime_downcount > 0.001);
1180                 
1181         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1182
1183         for(core::list<CollisionInfo>::Iterator
1184                         i = player_collisions.begin();
1185                         i != player_collisions.end(); i++)
1186         {
1187                 CollisionInfo &info = *i;
1188                 if(info.t == COLLISION_FALL)
1189                 {
1190                         //f32 tolerance = BS*10; // 2 without damage
1191                         f32 tolerance = BS*12; // 3 without damage
1192                         f32 factor = 1;
1193                         if(info.speed > tolerance)
1194                         {
1195                                 f32 damage_f = (info.speed - tolerance)/BS*factor;
1196                                 u16 damage = (u16)(damage_f+0.5);
1197                                 if(lplayer->hp > damage)
1198                                         lplayer->hp -= damage;
1199                                 else
1200                                         lplayer->hp = 0;
1201
1202                                 ClientEnvEvent event;
1203                                 event.type = CEE_PLAYER_DAMAGE;
1204                                 event.player_damage.amount = damage;
1205                                 m_client_event_queue.push_back(event);
1206                         }
1207                 }
1208         }
1209         
1210         /*
1211                 Stuff that can be done in an arbitarily large dtime
1212         */
1213         for(core::list<Player*>::Iterator i = m_players.begin();
1214                         i != m_players.end(); i++)
1215         {
1216                 Player *player = *i;
1217                 v3f playerpos = player->getPosition();
1218                 
1219                 /*
1220                         Handle non-local players
1221                 */
1222                 if(player->isLocal() == false)
1223                 {
1224                         // Move
1225                         player->move(dtime, *m_map, 100*BS);
1226
1227                         // Update lighting on remote players on client
1228                         u8 light = LIGHT_MAX;
1229                         try{
1230                                 // Get node at head
1231                                 v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
1232                                 MapNode n = m_map->getNode(p);
1233                                 light = n.getLightBlend(m_daynight_ratio);
1234                         }
1235                         catch(InvalidPositionException &e) {}
1236                         player->updateLight(light);
1237                 }
1238                 
1239                 /*
1240                         Add footsteps to grass
1241                 */
1242                 if(footprints)
1243                 {
1244                         // Get node that is at BS/4 under player
1245                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1246                         try{
1247                                 MapNode n = m_map->getNode(bottompos);
1248                                 if(n.d == CONTENT_GRASS)
1249                                 {
1250                                         n.d = CONTENT_GRASS_FOOTSTEPS;
1251                                         m_map->setNode(bottompos, n);
1252                                         // Update mesh on client
1253                                         if(m_map->mapType() == MAPTYPE_CLIENT)
1254                                         {
1255                                                 v3s16 p_blocks = getNodeBlockPos(bottompos);
1256                                                 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1257                                                 //b->updateMesh(m_daynight_ratio);
1258                                                 b->setMeshExpired(true);
1259                                         }
1260                                 }
1261                         }
1262                         catch(InvalidPositionException &e)
1263                         {
1264                         }
1265                 }
1266         }
1267         
1268         /*
1269                 Step active objects and update lighting of them
1270         */
1271         
1272         for(core::map<u16, ClientActiveObject*>::Iterator
1273                         i = m_active_objects.getIterator();
1274                         i.atEnd()==false; i++)
1275         {
1276                 ClientActiveObject* obj = i.getNode()->getValue();
1277                 // Step object
1278                 obj->step(dtime, this);
1279                 // Update lighting
1280                 //u8 light = LIGHT_MAX;
1281                 u8 light = 0;
1282                 try{
1283                         // Get node at head
1284                         v3s16 p = obj->getLightPosition();
1285                         MapNode n = m_map->getNode(p);
1286                         light = n.getLightBlend(m_daynight_ratio);
1287                 }
1288                 catch(InvalidPositionException &e) {}
1289                 obj->updateLight(light);
1290         }
1291 }
1292
1293 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1294 {
1295         m_map->updateMeshes(blockpos, m_daynight_ratio);
1296 }
1297
1298 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1299 {
1300         m_map->expireMeshes(only_daynight_diffed);
1301 }
1302
1303 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1304 {
1305         core::map<u16, ClientActiveObject*>::Node *n;
1306         n = m_active_objects.find(id);
1307         if(n == NULL)
1308                 return NULL;
1309         return n->getValue();
1310 }
1311
1312 bool isFreeClientActiveObjectId(u16 id,
1313                 core::map<u16, ClientActiveObject*> &objects)
1314 {
1315         if(id == 0)
1316                 return false;
1317         
1318         for(core::map<u16, ClientActiveObject*>::Iterator
1319                         i = objects.getIterator();
1320                         i.atEnd()==false; i++)
1321         {
1322                 if(i.getNode()->getKey() == id)
1323                         return false;
1324         }
1325         return true;
1326 }
1327
1328 u16 getFreeClientActiveObjectId(
1329                 core::map<u16, ClientActiveObject*> &objects)
1330 {
1331         u16 new_id = 1;
1332         for(;;)
1333         {
1334                 if(isFreeClientActiveObjectId(new_id, objects))
1335                         return new_id;
1336                 
1337                 if(new_id == 65535)
1338                         return 0;
1339
1340                 new_id++;
1341         }
1342 }
1343
1344 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1345 {
1346         assert(object);
1347         if(object->getId() == 0)
1348         {
1349                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1350                 if(new_id == 0)
1351                 {
1352                         dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1353                                         <<"no free ids available"<<std::endl;
1354                         delete object;
1355                         return 0;
1356                 }
1357                 object->setId(new_id);
1358         }
1359         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1360         {
1361                 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1362                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1363                 delete object;
1364                 return 0;
1365         }
1366         dstream<<"INGO: ClientEnvironment::addActiveObject(): "
1367                         <<"added (id="<<object->getId()<<")"<<std::endl;
1368         m_active_objects.insert(object->getId(), object);
1369         object->addToScene(m_smgr);
1370         return object->getId();
1371 }
1372
1373 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1374                 const std::string &init_data)
1375 {
1376         ClientActiveObject* obj = ClientActiveObject::create(type);
1377         if(obj == NULL)
1378         {
1379                 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1380                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1381                                 <<std::endl;
1382                 return;
1383         }
1384         
1385         obj->setId(id);
1386
1387         addActiveObject(obj);
1388
1389         obj->initialize(init_data);
1390 }
1391
1392 void ClientEnvironment::removeActiveObject(u16 id)
1393 {
1394         dstream<<"ClientEnvironment::removeActiveObject(): "
1395                         <<"id="<<id<<std::endl;
1396         ClientActiveObject* obj = getActiveObject(id);
1397         if(obj == NULL)
1398         {
1399                 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1400                                 <<"id="<<id<<" not found"<<std::endl;
1401                 return;
1402         }
1403         obj->removeFromScene();
1404         delete obj;
1405         m_active_objects.remove(id);
1406 }
1407
1408 void ClientEnvironment::processActiveObjectMessage(u16 id,
1409                 const std::string &data)
1410 {
1411         ClientActiveObject* obj = getActiveObject(id);
1412         if(obj == NULL)
1413         {
1414                 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1415                                 <<" got message for id="<<id<<", which doesn't exist."
1416                                 <<std::endl;
1417                 return;
1418         }
1419         obj->processMessage(data);
1420 }
1421
1422 /*
1423         Callbacks for activeobjects
1424 */
1425
1426 void ClientEnvironment::damageLocalPlayer(u8 damage)
1427 {
1428         LocalPlayer *lplayer = getLocalPlayer();
1429         assert(lplayer);
1430
1431         if(lplayer->hp > damage)
1432                 lplayer->hp -= damage;
1433         else
1434                 lplayer->hp = 0;
1435
1436         ClientEnvEvent event;
1437         event.type = CEE_PLAYER_DAMAGE;
1438         event.player_damage.amount = damage;
1439         m_client_event_queue.push_back(event);
1440 }
1441
1442 /*
1443         Client likes to call these
1444 */
1445         
1446 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1447                 core::array<DistanceSortedActiveObject> &dest)
1448 {
1449         for(core::map<u16, ClientActiveObject*>::Iterator
1450                         i = m_active_objects.getIterator();
1451                         i.atEnd()==false; i++)
1452         {
1453                 ClientActiveObject* obj = i.getNode()->getValue();
1454
1455                 f32 d = (obj->getPosition() - origin).getLength();
1456
1457                 if(d > max_d)
1458                         continue;
1459
1460                 DistanceSortedActiveObject dso(obj, d);
1461
1462                 dest.push_back(dso);
1463         }
1464 }
1465
1466 ClientEnvEvent ClientEnvironment::getClientEvent()
1467 {
1468         if(m_client_event_queue.size() == 0)
1469         {
1470                 ClientEnvEvent event;
1471                 event.type = CEE_NONE;
1472                 return event;
1473         }
1474         return m_client_event_queue.pop_front();
1475 }
1476
1477 #endif // #ifndef SERVER
1478
1479