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