mainly work on object scripting api
[oweals/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 #include "porting.h"
23
24 Environment::Environment()
25 {
26         m_daynight_ratio = 0.5;
27 }
28
29 Environment::~Environment()
30 {
31         // Deallocate players
32         for(core::list<Player*>::Iterator i = m_players.begin();
33                         i != m_players.end(); i++)
34         {
35                 delete (*i);
36         }
37 }
38
39 void Environment::addPlayer(Player *player)
40 {
41         DSTACK(__FUNCTION_NAME);
42         /*
43                 Check that peer_ids are unique.
44                 Also check that names are unique.
45                 Exception: there can be multiple players with peer_id=0
46         */
47         // If peer id is non-zero, it has to be unique.
48         if(player->peer_id != 0)
49                 assert(getPlayer(player->peer_id) == NULL);
50         // Name has to be unique.
51         assert(getPlayer(player->getName()) == NULL);
52         // Add.
53         m_players.push_back(player);
54 }
55
56 void Environment::removePlayer(u16 peer_id)
57 {
58         DSTACK(__FUNCTION_NAME);
59 re_search:
60         for(core::list<Player*>::Iterator i = m_players.begin();
61                         i != m_players.end(); i++)
62         {
63                 Player *player = *i;
64                 if(player->peer_id != peer_id)
65                         continue;
66                 
67                 delete player;
68                 m_players.erase(i);
69                 // See if there is an another one
70                 // (shouldn't be, but just to be sure)
71                 goto re_search;
72         }
73 }
74
75 Player * Environment::getPlayer(u16 peer_id)
76 {
77         for(core::list<Player*>::Iterator i = m_players.begin();
78                         i != m_players.end(); i++)
79         {
80                 Player *player = *i;
81                 if(player->peer_id == peer_id)
82                         return player;
83         }
84         return NULL;
85 }
86
87 Player * Environment::getPlayer(const char *name)
88 {
89         for(core::list<Player*>::Iterator i = m_players.begin();
90                         i != m_players.end(); i++)
91         {
92                 Player *player = *i;
93                 if(strcmp(player->getName(), name) == 0)
94                         return player;
95         }
96         return NULL;
97 }
98
99 Player * Environment::getRandomConnectedPlayer()
100 {
101         core::list<Player*> connected_players = getPlayers(true);
102         u32 chosen_one = myrand() % connected_players.size();
103         u32 j = 0;
104         for(core::list<Player*>::Iterator
105                         i = connected_players.begin();
106                         i != connected_players.end(); i++)
107         {
108                 if(j == chosen_one)
109                 {
110                         Player *player = *i;
111                         return player;
112                 }
113                 j++;
114         }
115         return NULL;
116 }
117
118 core::list<Player*> Environment::getPlayers()
119 {
120         return m_players;
121 }
122
123 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
124 {
125         core::list<Player*> newlist;
126         for(core::list<Player*>::Iterator
127                         i = m_players.begin();
128                         i != m_players.end(); i++)
129         {
130                 Player *player = *i;
131                 
132                 if(ignore_disconnected)
133                 {
134                         // Ignore disconnected players
135                         if(player->peer_id == 0)
136                                 continue;
137                 }
138
139                 newlist.push_back(player);
140         }
141         return newlist;
142 }
143
144 void Environment::printPlayers(std::ostream &o)
145 {
146         o<<"Players in environment:"<<std::endl;
147         for(core::list<Player*>::Iterator i = m_players.begin();
148                         i != m_players.end(); i++)
149         {
150                 Player *player = *i;
151                 o<<"Player peer_id="<<player->peer_id<<std::endl;
152         }
153 }
154
155 void Environment::setDayNightRatio(u32 r)
156 {
157         m_daynight_ratio = r;
158 }
159
160 u32 Environment::getDayNightRatio()
161 {
162         return m_daynight_ratio;
163 }
164
165 /*
166         ServerEnvironment
167 */
168
169 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
170         m_map(map),
171         m_server(server),
172         m_random_spawn_timer(0)
173 {
174 }
175
176 ServerEnvironment::~ServerEnvironment()
177 {
178         // Drop/delete map
179         m_map->drop();
180 }
181
182 void ServerEnvironment::serializePlayers(const std::string &savedir)
183 {
184         std::string players_path = savedir + "/players";
185         fs::CreateDir(players_path);
186
187         core::map<Player*, bool> saved_players;
188
189         std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
190         for(u32 i=0; i<player_files.size(); i++)
191         {
192                 if(player_files[i].dir)
193                         continue;
194                 
195                 // Full path to this file
196                 std::string path = players_path + "/" + player_files[i].name;
197
198                 //dstream<<"Checking player file "<<path<<std::endl;
199
200                 // Load player to see what is its name
201                 ServerRemotePlayer testplayer;
202                 {
203                         // Open file and deserialize
204                         std::ifstream is(path.c_str(), std::ios_base::binary);
205                         if(is.good() == false)
206                         {
207                                 dstream<<"Failed to read "<<path<<std::endl;
208                                 continue;
209                         }
210                         testplayer.deSerialize(is);
211                 }
212
213                 //dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
214                 
215                 // Search for the player
216                 std::string playername = testplayer.getName();
217                 Player *player = getPlayer(playername.c_str());
218                 if(player == NULL)
219                 {
220                         dstream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
221                         continue;
222                 }
223
224                 //dstream<<"Found matching player, overwriting."<<std::endl;
225
226                 // OK, found. Save player there.
227                 {
228                         // Open file and serialize
229                         std::ofstream os(path.c_str(), std::ios_base::binary);
230                         if(os.good() == false)
231                         {
232                                 dstream<<"Failed to overwrite "<<path<<std::endl;
233                                 continue;
234                         }
235                         player->serialize(os);
236                         saved_players.insert(player, true);
237                 }
238         }
239
240         for(core::list<Player*>::Iterator i = m_players.begin();
241                         i != m_players.end(); i++)
242         {
243                 Player *player = *i;
244                 if(saved_players.find(player) != NULL)
245                 {
246                         /*dstream<<"Player "<<player->getName()
247                                         <<" was already saved."<<std::endl;*/
248                         continue;
249                 }
250                 std::string playername = player->getName();
251                 // Don't save unnamed player
252                 if(playername == "")
253                 {
254                         //dstream<<"Not saving unnamed player."<<std::endl;
255                         continue;
256                 }
257                 /*
258                         Find a sane filename
259                 */
260                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
261                         playername = "player";
262                 std::string path = players_path + "/" + playername;
263                 bool found = false;
264                 for(u32 i=0; i<1000; i++)
265                 {
266                         if(fs::PathExists(path) == false)
267                         {
268                                 found = true;
269                                 break;
270                         }
271                         path = players_path + "/" + playername + itos(i);
272                 }
273                 if(found == false)
274                 {
275                         dstream<<"WARNING: Didn't find free file for player"<<std::endl;
276                         continue;
277                 }
278
279                 {
280                         /*dstream<<"Saving player "<<player->getName()<<" to "
281                                         <<path<<std::endl;*/
282                         // Open file and serialize
283                         std::ofstream os(path.c_str(), std::ios_base::binary);
284                         if(os.good() == false)
285                         {
286                                 dstream<<"WARNING: Failed to overwrite "<<path<<std::endl;
287                                 continue;
288                         }
289                         player->serialize(os);
290                         saved_players.insert(player, true);
291                 }
292         }
293
294         //dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
295 }
296
297 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
298 {
299         std::string players_path = savedir + "/players";
300
301         core::map<Player*, bool> saved_players;
302
303         std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
304         for(u32 i=0; i<player_files.size(); i++)
305         {
306                 if(player_files[i].dir)
307                         continue;
308                 
309                 // Full path to this file
310                 std::string path = players_path + "/" + player_files[i].name;
311
312                 dstream<<"Checking player file "<<path<<std::endl;
313
314                 // Load player to see what is its name
315                 ServerRemotePlayer testplayer;
316                 {
317                         // Open file and deserialize
318                         std::ifstream is(path.c_str(), std::ios_base::binary);
319                         if(is.good() == false)
320                         {
321                                 dstream<<"Failed to read "<<path<<std::endl;
322                                 continue;
323                         }
324                         testplayer.deSerialize(is);
325                 }
326
327                 dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
328                 
329                 // Search for the player
330                 std::string playername = testplayer.getName();
331                 Player *player = getPlayer(playername.c_str());
332                 bool newplayer = false;
333                 if(player == NULL)
334                 {
335                         dstream<<"Is a new player"<<std::endl;
336                         player = new ServerRemotePlayer();
337                         newplayer = true;
338                 }
339
340                 // Load player
341                 {
342                         dstream<<"Reading player "<<testplayer.getName()<<" from "
343                                         <<path<<std::endl;
344                         // Open file and deserialize
345                         std::ifstream is(path.c_str(), std::ios_base::binary);
346                         if(is.good() == false)
347                         {
348                                 dstream<<"Failed to read "<<path<<std::endl;
349                                 continue;
350                         }
351                         player->deSerialize(is);
352                 }
353
354                 if(newplayer)
355                         addPlayer(player);
356         }
357 }
358
359 void ServerEnvironment::step(float dtime)
360 {
361         DSTACK(__FUNCTION_NAME);
362
363         // Get some settings
364         //bool free_move = g_settings.getBool("free_move");
365         bool footprints = g_settings.getBool("footprints");
366
367         {
368                 //TimeTaker timer("Server m_map->timerUpdate()", g_device);
369                 m_map->timerUpdate(dtime);
370         }
371
372         /*
373                 Handle players
374         */
375         for(core::list<Player*>::Iterator i = m_players.begin();
376                         i != m_players.end(); i++)
377         {
378                 Player *player = *i;
379                 v3f playerpos = player->getPosition();
380                 
381                 // Move
382                 player->move(dtime, *m_map, 100*BS);
383                 
384                 /*
385                         Add footsteps to grass
386                 */
387                 if(footprints)
388                 {
389                         // Get node that is at BS/4 under player
390                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
391                         try{
392                                 MapNode n = m_map->getNode(bottompos);
393                                 if(n.d == CONTENT_GRASS)
394                                 {
395                                         n.d = CONTENT_GRASS_FOOTSTEPS;
396                                         m_map->setNode(bottompos, n);
397                                 }
398                         }
399                         catch(InvalidPositionException &e)
400                         {
401                         }
402                 }
403         }
404         
405         if(g_settings.getBool("enable_experimental"))
406         {
407
408         /*
409                 Step active objects
410         */
411         for(core::map<u16, ServerActiveObject*>::Iterator
412                         i = m_active_objects.getIterator();
413                         i.atEnd()==false; i++)
414         {
415                 ServerActiveObject* obj = i.getNode()->getValue();
416                 // Step object, putting messages directly to the queue
417                 obj->step(dtime, m_active_object_messages);
418         }
419
420         /*
421                 Remove (m_removed && m_known_by_count==0) objects
422         */
423         {
424                 core::list<u16> objects_to_remove;
425                 for(core::map<u16, ServerActiveObject*>::Iterator
426                                 i = m_active_objects.getIterator();
427                                 i.atEnd()==false; i++)
428                 {
429                         u16 id = i.getNode()->getKey();
430                         ServerActiveObject* obj = i.getNode()->getValue();
431                         // This shouldn't happen but check it
432                         if(obj == NULL)
433                         {
434                                 dstream<<"WARNING: NULL object found in ServerEnvironment"
435                                                 <<" while finding removed objects. id="<<id<<std::endl;
436                                 // Id to be removed from m_active_objects
437                                 objects_to_remove.push_back(id);
438                                 continue;
439                         }
440                         else
441                         {
442                                 // If not m_removed, don't remove.
443                                 if(obj->m_removed == false)
444                                         continue;
445                                 // Delete
446                                 delete obj;
447                                 // Id to be removed from m_active_objects
448                                 objects_to_remove.push_back(id);
449                         }
450                 }
451                 // Remove references from m_active_objects
452                 for(core::list<u16>::Iterator i = objects_to_remove.begin();
453                                 i != objects_to_remove.end(); i++)
454                 {
455                         m_active_objects.remove(*i);
456                 }
457         }
458
459         /*
460                 TEST CODE
461         */
462         m_random_spawn_timer -= dtime;
463         if(m_random_spawn_timer < 0)
464         {
465                 m_random_spawn_timer += myrand_range(2.0, 20.0);
466
467                 /*TestSAO *obj = new TestSAO(0,
468                                 v3f(myrand_range(-2*BS,2*BS), BS*5, myrand_range(-2*BS,2*BS)));*/
469
470                 /*
471                         Find some position
472                 */
473
474                 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
475                 s16 y = 1 + getServerMap().findGroundLevel(p2d);
476                 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
477                 
478                 Player *player = getRandomConnectedPlayer();
479                 v3f pos(0,0,0);
480                 if(player)
481                         pos = player->getPosition();
482                 pos += v3f(
483                         myrand_range(-5,5)*BS,
484                         0,
485                         myrand_range(-5,5)*BS
486                 );
487
488                 /*
489                         Create a LuaSAO (ServerActiveObject)
490                 */
491
492                 LuaSAO *obj = new LuaSAO(this, 0, pos);
493                 
494                 /*
495                         Select a random type for it
496                 */
497                 std::string objectdir = porting::getDataPath("luaobjects");
498                 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(objectdir);
499                 if(dirlist.size() > 0)
500                 {
501                         u32 selected_i = myrand_range(0, dirlist.size()-1);
502                         std::string selected_name = "";
503                         selected_name = dirlist[selected_i].name;
504                         /*for(u32 i=0; i<dirlist.size(); i++)*/
505                         
506                         dstream<<"ServerEnvironment: Selected script name \""<<selected_name
507                                         <<"\" for new lua object"<<std::endl;
508
509                         /*
510                                 Load the scripts for the type
511                         */
512                         obj->initializeFromNothing(selected_name.c_str());
513
514                         // Add the object to the environment
515                         addActiveObject(obj);
516                 }
517         }
518
519         } // enable_experimental
520 }
521
522 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
523 {
524         core::map<u16, ServerActiveObject*>::Node *n;
525         n = m_active_objects.find(id);
526         if(n == NULL)
527                 return NULL;
528         return n->getValue();
529 }
530
531 bool isFreeServerActiveObjectId(u16 id,
532                 core::map<u16, ServerActiveObject*> &objects)
533 {
534         if(id == 0)
535                 return false;
536         
537         for(core::map<u16, ServerActiveObject*>::Iterator
538                         i = objects.getIterator();
539                         i.atEnd()==false; i++)
540         {
541                 if(i.getNode()->getKey() == id)
542                         return false;
543         }
544         return true;
545 }
546
547 u16 getFreeServerActiveObjectId(
548                 core::map<u16, ServerActiveObject*> &objects)
549 {
550         u16 new_id = 1;
551         for(;;)
552         {
553                 if(isFreeServerActiveObjectId(new_id, objects))
554                         return new_id;
555                 
556                 if(new_id == 65535)
557                         return 0;
558
559                 new_id++;
560         }
561 }
562
563 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
564 {
565         assert(object);
566         if(object->getId() == 0)
567         {
568                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
569                 if(new_id == 0)
570                 {
571                         dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
572                                         <<"no free ids available"<<std::endl;
573                         delete object;
574                         return 0;
575                 }
576                 object->setId(new_id);
577         }
578         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
579         {
580                 dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
581                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
582                 delete object;
583                 return 0;
584         }
585         dstream<<"INGO: ServerEnvironment::addActiveObject(): "
586                         <<"added (id="<<object->getId()<<")"<<std::endl;
587         m_active_objects.insert(object->getId(), object);
588         return object->getId();
589 }
590
591 /*
592         Finds out what new objects have been added to
593         inside a radius around a position
594 */
595 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
596                 core::map<u16, bool> &current_objects,
597                 core::map<u16, bool> &added_objects)
598 {
599         v3f pos_f = intToFloat(pos, BS);
600         f32 radius_f = radius * BS;
601         /*
602                 Go through the object list,
603                 - discard m_removed objects,
604                 - discard objects that are too far away,
605                 - discard objects that are found in current_objects.
606                 - add remaining objects to added_objects
607         */
608         for(core::map<u16, ServerActiveObject*>::Iterator
609                         i = m_active_objects.getIterator();
610                         i.atEnd()==false; i++)
611         {
612                 u16 id = i.getNode()->getKey();
613                 // Get object
614                 ServerActiveObject *object = i.getNode()->getValue();
615                 if(object == NULL)
616                         continue;
617                 // Discard if removed
618                 if(object->m_removed)
619                         continue;
620                 // Discard if too far
621                 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
622                 if(distance_f > radius_f)
623                         continue;
624                 // Discard if already on current_objects
625                 core::map<u16, bool>::Node *n;
626                 n = current_objects.find(id);
627                 if(n != NULL)
628                         continue;
629                 // Add to added_objects
630                 added_objects.insert(id, false);
631         }
632 }
633
634 /*
635         Finds out what objects have been removed from
636         inside a radius around a position
637 */
638 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
639                 core::map<u16, bool> &current_objects,
640                 core::map<u16, bool> &removed_objects)
641 {
642         v3f pos_f = intToFloat(pos, BS);
643         f32 radius_f = radius * BS;
644         /*
645                 Go through current_objects; object is removed if:
646                 - object is not found in m_active_objects (this is actually an
647                   error condition; objects should be set m_removed=true and removed
648                   only after all clients have been informed about removal), or
649                 - object has m_removed=true, or
650                 - object is too far away
651         */
652         for(core::map<u16, bool>::Iterator
653                         i = current_objects.getIterator();
654                         i.atEnd()==false; i++)
655         {
656                 u16 id = i.getNode()->getKey();
657                 ServerActiveObject *object = getActiveObject(id);
658                 if(object == NULL)
659                 {
660                         dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
661                                         <<" object in current_objects is NULL"<<std::endl;
662                 }
663                 else if(object->m_removed == false)
664                 {
665                         f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
666                         /*dstream<<"removed == false"
667                                         <<"distance_f = "<<distance_f
668                                         <<", radius_f = "<<radius_f<<std::endl;*/
669                         if(distance_f < radius_f)
670                         {
671                                 // Not removed
672                                 continue;
673                         }
674                 }
675                 removed_objects.insert(id, false);
676         }
677 }
678
679 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
680 {
681         if(m_active_object_messages.size() == 0)
682                 return ActiveObjectMessage(0);
683         
684         return m_active_object_messages.pop_front();
685 }
686
687 #ifndef SERVER
688
689 /*
690         ClientEnvironment
691 */
692
693 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
694         m_map(map),
695         m_smgr(smgr)
696 {
697         assert(m_map);
698         assert(m_smgr);
699 }
700
701 ClientEnvironment::~ClientEnvironment()
702 {
703         // delete active objects
704         for(core::map<u16, ClientActiveObject*>::Iterator
705                         i = m_active_objects.getIterator();
706                         i.atEnd()==false; i++)
707         {
708                 delete i.getNode()->getValue();
709         }
710
711         // Drop/delete map
712         m_map->drop();
713 }
714
715 void ClientEnvironment::addPlayer(Player *player)
716 {
717         DSTACK(__FUNCTION_NAME);
718         /*
719                 It is a failure if player is local and there already is a local
720                 player
721         */
722         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
723
724         Environment::addPlayer(player);
725 }
726
727 LocalPlayer * ClientEnvironment::getLocalPlayer()
728 {
729         for(core::list<Player*>::Iterator i = m_players.begin();
730                         i != m_players.end(); i++)
731         {
732                 Player *player = *i;
733                 if(player->isLocal())
734                         return (LocalPlayer*)player;
735         }
736         return NULL;
737 }
738
739 void ClientEnvironment::step(float dtime)
740 {
741         DSTACK(__FUNCTION_NAME);
742
743         // Get some settings
744         bool free_move = g_settings.getBool("free_move");
745         bool footprints = g_settings.getBool("footprints");
746
747         {
748                 //TimeTaker timer("Client m_map->timerUpdate()", g_device);
749                 m_map->timerUpdate(dtime);
750         }
751
752         /*
753                 Get the speed the player is going
754         */
755         f32 player_speed = 0.001; // just some small value
756         LocalPlayer *lplayer = getLocalPlayer();
757         if(lplayer)
758                 player_speed = lplayer->getSpeed().getLength();
759         
760         /*
761                 Maximum position increment
762         */
763         //f32 position_max_increment = 0.05*BS;
764         f32 position_max_increment = 0.1*BS;
765
766         // Maximum time increment (for collision detection etc)
767         // time = distance / speed
768         f32 dtime_max_increment = position_max_increment / player_speed;
769         
770         // Maximum time increment is 10ms or lower
771         if(dtime_max_increment > 0.01)
772                 dtime_max_increment = 0.01;
773         
774         // Don't allow overly huge dtime
775         if(dtime > 0.5)
776                 dtime = 0.5;
777         
778         f32 dtime_downcount = dtime;
779
780         /*
781                 Stuff that has a maximum time increment
782         */
783
784         u32 loopcount = 0;
785         do
786         {
787                 loopcount++;
788
789                 f32 dtime_part;
790                 if(dtime_downcount > dtime_max_increment)
791                         dtime_part = dtime_max_increment;
792                 else
793                         dtime_part = dtime_downcount;
794                 dtime_downcount -= dtime_part;
795                 
796                 /*
797                         Handle local player
798                 */
799                 
800                 {
801                         Player *player = getLocalPlayer();
802
803                         v3f playerpos = player->getPosition();
804                         
805                         // Apply physics
806                         if(free_move == false)
807                         {
808                                 // Gravity
809                                 v3f speed = player->getSpeed();
810                                 if(player->swimming_up == false)
811                                         speed.Y -= 9.81 * BS * dtime_part * 2;
812
813                                 // Water resistance
814                                 if(player->in_water_stable || player->in_water)
815                                 {
816                                         f32 max_down = 2.0*BS;
817                                         if(speed.Y < -max_down) speed.Y = -max_down;
818
819                                         f32 max = 2.5*BS;
820                                         if(speed.getLength() > max)
821                                         {
822                                                 speed = speed / speed.getLength() * max;
823                                         }
824                                 }
825
826                                 player->setSpeed(speed);
827                         }
828
829                         /*
830                                 Move the player.
831                                 This also does collision detection.
832                         */
833                         player->move(dtime_part, *m_map, position_max_increment);
834                 }
835         }
836         while(dtime_downcount > 0.001);
837                 
838         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
839         
840         /*
841                 Stuff that can be done in an arbitarily large dtime
842         */
843         for(core::list<Player*>::Iterator i = m_players.begin();
844                         i != m_players.end(); i++)
845         {
846                 Player *player = *i;
847                 v3f playerpos = player->getPosition();
848                 
849                 /*
850                         Handle non-local players
851                 */
852                 if(player->isLocal() == false)
853                 {
854                         // Move
855                         player->move(dtime, *m_map, 100*BS);
856
857                         // Update lighting on remote players on client
858                         u8 light = LIGHT_MAX;
859                         try{
860                                 // Get node at head
861                                 v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
862                                 MapNode n = m_map->getNode(p);
863                                 light = n.getLightBlend(m_daynight_ratio);
864                         }
865                         catch(InvalidPositionException &e) {}
866                         player->updateLight(light);
867                 }
868                 
869                 /*
870                         Add footsteps to grass
871                 */
872                 if(footprints)
873                 {
874                         // Get node that is at BS/4 under player
875                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
876                         try{
877                                 MapNode n = m_map->getNode(bottompos);
878                                 if(n.d == CONTENT_GRASS)
879                                 {
880                                         n.d = CONTENT_GRASS_FOOTSTEPS;
881                                         m_map->setNode(bottompos, n);
882                                         // Update mesh on client
883                                         if(m_map->mapType() == MAPTYPE_CLIENT)
884                                         {
885                                                 v3s16 p_blocks = getNodeBlockPos(bottompos);
886                                                 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
887                                                 b->updateMesh(m_daynight_ratio);
888                                         }
889                                 }
890                         }
891                         catch(InvalidPositionException &e)
892                         {
893                         }
894                 }
895         }
896         
897         /*
898                 Step active objects
899         */
900         for(core::map<u16, ClientActiveObject*>::Iterator
901                         i = m_active_objects.getIterator();
902                         i.atEnd()==false; i++)
903         {
904                 ClientActiveObject* obj = i.getNode()->getValue();
905                 // Step object
906                 obj->step(dtime);
907         }
908 }
909
910 void ClientEnvironment::updateMeshes(v3s16 blockpos)
911 {
912         m_map->updateMeshes(blockpos, m_daynight_ratio);
913 }
914
915 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
916 {
917         m_map->expireMeshes(only_daynight_diffed);
918 }
919
920 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
921 {
922         core::map<u16, ClientActiveObject*>::Node *n;
923         n = m_active_objects.find(id);
924         if(n == NULL)
925                 return NULL;
926         return n->getValue();
927 }
928
929 bool isFreeClientActiveObjectId(u16 id,
930                 core::map<u16, ClientActiveObject*> &objects)
931 {
932         if(id == 0)
933                 return false;
934         
935         for(core::map<u16, ClientActiveObject*>::Iterator
936                         i = objects.getIterator();
937                         i.atEnd()==false; i++)
938         {
939                 if(i.getNode()->getKey() == id)
940                         return false;
941         }
942         return true;
943 }
944
945 u16 getFreeClientActiveObjectId(
946                 core::map<u16, ClientActiveObject*> &objects)
947 {
948         u16 new_id = 1;
949         for(;;)
950         {
951                 if(isFreeClientActiveObjectId(new_id, objects))
952                         return new_id;
953                 
954                 if(new_id == 65535)
955                         return 0;
956
957                 new_id++;
958         }
959 }
960
961 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
962 {
963         assert(object);
964         if(object->getId() == 0)
965         {
966                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
967                 if(new_id == 0)
968                 {
969                         dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
970                                         <<"no free ids available"<<std::endl;
971                         delete object;
972                         return 0;
973                 }
974                 object->setId(new_id);
975         }
976         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
977         {
978                 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
979                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
980                 delete object;
981                 return 0;
982         }
983         dstream<<"INGO: ClientEnvironment::addActiveObject(): "
984                         <<"added (id="<<object->getId()<<")"<<std::endl;
985         m_active_objects.insert(object->getId(), object);
986         object->addToScene(m_smgr);
987         return object->getId();
988 }
989
990 void ClientEnvironment::addActiveObject(u16 id, u8 type,
991                 const std::string &init_data)
992 {
993         ClientActiveObject* obj = ClientActiveObject::create(type);
994         if(obj == NULL)
995         {
996                 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
997                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
998                                 <<std::endl;
999                 return;
1000         }
1001         
1002         obj->setId(id);
1003
1004         addActiveObject(obj);
1005
1006         obj->initialize(init_data);
1007 }
1008
1009 void ClientEnvironment::removeActiveObject(u16 id)
1010 {
1011         dstream<<"ClientEnvironment::removeActiveObject(): "
1012                         <<"id="<<id<<std::endl;
1013         ClientActiveObject* obj = getActiveObject(id);
1014         if(obj == NULL)
1015         {
1016                 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1017                                 <<"id="<<id<<" not found"<<std::endl;
1018                 return;
1019         }
1020         obj->removeFromScene();
1021         delete obj;
1022         m_active_objects.remove(id);
1023 }
1024
1025 void ClientEnvironment::processActiveObjectMessage(u16 id,
1026                 const std::string &data)
1027 {
1028         ClientActiveObject* obj = getActiveObject(id);
1029         if(obj == NULL)
1030         {
1031                 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1032                                 <<" got message for id="<<id<<", which doesn't exist."
1033                                 <<std::endl;
1034                 return;
1035         }
1036         obj->processMessage(data);
1037 }
1038
1039 #endif // #ifndef SERVER
1040
1041