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