]> git.lizzy.rs Git - minetest.git/blob - src/environment.cpp
1951996a23135f0330aa6c40f1dc4e313e03e56d
[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                 // Find out how many objects the block contains
622                 u32 active_object_count = block->m_static_objects.m_active.size();
623                 // Find out how many objects this and all the neighbors contain
624                 u32 active_object_count_wider = 0;
625                 for(s16 x=-1; x<=1; x++)
626                 for(s16 y=-1; y<=1; y++)
627                 for(s16 z=-1; z<=1; z++)
628                 {
629                         MapBlock *block2 = map->getBlockNoCreateNoEx(
630                                         block->getPos() + v3s16(x,y,z));
631                         if(block2==NULL)
632                                 continue;
633                         active_object_count_wider +=
634                                         block2->m_static_objects.m_active.size()
635                                         + block2->m_static_objects.m_stored.size();
636                 }
637
638                 v3s16 p0;
639                 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
640                 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
641                 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
642                 {
643                         MapNode n = block->getNodeNoEx(p0);
644                         content_t c = n.getContent();
645                         v3s16 p = p0 + block->getPosRelative();
646
647                         std::map<content_t, std::list<ActiveABM> >::iterator j;
648                         j = m_aabms.find(c);
649                         if(j == m_aabms.end())
650                                 continue;
651
652                         for(std::list<ActiveABM>::iterator
653                                         i = j->second.begin(); i != j->second.end(); i++){
654                                 if(myrand() % i->chance != 0)
655                                         continue;
656                                 // Call all the trigger variations
657                                 i->abm->trigger(m_env, p, n);
658                                 i->abm->trigger(m_env, p, n,
659                                                 active_object_count, active_object_count_wider);
660                         }
661                 }
662         }
663 };
664
665 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
666 {
667         // Get time difference
668         u32 dtime_s = 0;
669         u32 stamp = block->getTimestamp();
670         if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
671                 dtime_s = m_game_time - block->getTimestamp();
672         dtime_s += additional_dtime;
673
674         /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
675                         <<stamp<<", game time: "<<m_game_time<<std::endl;*/
676
677         // Set current time as timestamp
678         block->setTimestampNoChangedFlag(m_game_time);
679
680         /*infostream<<"ServerEnvironment::activateBlock(): block is "
681                         <<dtime_s<<" seconds old."<<std::endl;*/
682         
683         // Activate stored objects
684         activateObjects(block);
685
686         // Run node metadata
687         bool changed = block->m_node_metadata->step((float)dtime_s);
688         if(changed)
689         {
690                 MapEditEvent event;
691                 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
692                 event.p = block->getPos();
693                 m_map->dispatchEvent(&event);
694
695                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
696                                 "node metadata modified in activateBlock");
697         }
698
699         /* Handle ActiveBlockModifiers */
700         ABMHandler abmhandler(m_abms, dtime_s, this, false);
701         abmhandler.apply(block);
702 }
703
704 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
705 {
706         m_abms.push_back(ABMWithState(abm));
707 }
708
709 void ServerEnvironment::clearAllObjects()
710 {
711         infostream<<"ServerEnvironment::clearAllObjects(): "
712                         <<"Removing all active objects"<<std::endl;
713         core::list<u16> objects_to_remove;
714         for(core::map<u16, ServerActiveObject*>::Iterator
715                         i = m_active_objects.getIterator();
716                         i.atEnd()==false; i++)
717         {
718                 ServerActiveObject* obj = i.getNode()->getValue();
719                 u16 id = i.getNode()->getKey();         
720                 v3f objectpos = obj->getBasePosition(); 
721                 // Delete static object if block is loaded
722                 if(obj->m_static_exists){
723                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
724                         if(block){
725                                 block->m_static_objects.remove(id);
726                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
727                                                 "clearAllObjects");
728                                 obj->m_static_exists = false;
729                         }
730                 }
731                 // If known by some client, don't delete immediately
732                 if(obj->m_known_by_count > 0){
733                         obj->m_pending_deactivation = true;
734                         obj->m_removed = true;
735                         continue;
736                 }
737                 // Deregister in scripting api
738                 scriptapi_rm_object_reference(m_lua, obj);
739                 // Delete active object
740                 delete obj;
741                 // Id to be removed from m_active_objects
742                 objects_to_remove.push_back(id);
743         }
744         // Remove references from m_active_objects
745         for(core::list<u16>::Iterator i = objects_to_remove.begin();
746                         i != objects_to_remove.end(); i++)
747         {
748                 m_active_objects.remove(*i);
749         }
750
751         core::list<v3s16> loadable_blocks;
752         infostream<<"ServerEnvironment::clearAllObjects(): "
753                         <<"Listing all loadable blocks"<<std::endl;
754         m_map->listAllLoadableBlocks(loadable_blocks);
755         infostream<<"ServerEnvironment::clearAllObjects(): "
756                         <<"Done listing all loadable blocks: "
757                         <<loadable_blocks.size()
758                         <<", now clearing"<<std::endl;
759         u32 report_interval = loadable_blocks.size() / 10;
760         u32 num_blocks_checked = 0;
761         u32 num_blocks_cleared = 0;
762         u32 num_objs_cleared = 0;
763         for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
764                         i != loadable_blocks.end(); i++)
765         {
766                 v3s16 p = *i;
767                 MapBlock *block = m_map->emergeBlock(p, false);
768                 if(!block){
769                         errorstream<<"ServerEnvironment::clearAllObjects(): "
770                                         <<"Failed to emerge block "<<PP(p)<<std::endl;
771                         continue;
772                 }
773                 u32 num_stored = block->m_static_objects.m_stored.size();
774                 u32 num_active = block->m_static_objects.m_active.size();
775                 if(num_stored != 0 || num_active != 0){
776                         block->m_static_objects.m_stored.clear();
777                         block->m_static_objects.m_active.clear();
778                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
779                                         "clearAllObjects");
780                         num_objs_cleared += num_stored + num_active;
781                         num_blocks_cleared++;
782                 }
783                 num_blocks_checked++;
784
785                 if(num_blocks_checked % report_interval == 0){
786                         float percent = 100.0 * (float)num_blocks_checked /
787                                         loadable_blocks.size();
788                         infostream<<"ServerEnvironment::clearAllObjects(): "
789                                         <<"Cleared "<<num_objs_cleared<<" objects"
790                                         <<" in "<<num_blocks_cleared<<" blocks ("
791                                         <<percent<<"%)"<<std::endl;
792                 }
793         }
794         infostream<<"ServerEnvironment::clearAllObjects(): "
795                         <<"Finished: Cleared "<<num_objs_cleared<<" objects"
796                         <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
797 }
798
799 void ServerEnvironment::step(float dtime)
800 {
801         DSTACK(__FUNCTION_NAME);
802         
803         //TimeTaker timer("ServerEnv step");
804
805         // Get some settings
806         bool footprints = g_settings->getBool("footprints");
807
808         /*
809                 Increment game time
810         */
811         {
812                 m_game_time_fraction_counter += dtime;
813                 u32 inc_i = (u32)m_game_time_fraction_counter;
814                 m_game_time += inc_i;
815                 m_game_time_fraction_counter -= (float)inc_i;
816         }
817         
818         /*
819                 Handle players
820         */
821         {
822                 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
823                 for(core::list<Player*>::Iterator i = m_players.begin();
824                                 i != m_players.end(); i++)
825                 {
826                         Player *player = *i;
827                         
828                         // Ignore disconnected players
829                         if(player->peer_id == 0)
830                                 continue;
831
832                         v3f playerpos = player->getPosition();
833                         
834                         // Move
835                         player->move(dtime, *m_map, 100*BS);
836                         
837                         /*
838                                 Add footsteps to grass
839                         */
840                         if(footprints)
841                         {
842                                 // Get node that is at BS/4 under player
843                                 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
844                                 try{
845                                         MapNode n = m_map->getNode(bottompos);
846                                         if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
847                                         {
848                                                 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
849                                                 m_map->setNode(bottompos, n);
850                                         }
851                                 }
852                                 catch(InvalidPositionException &e)
853                                 {
854                                 }
855                         }
856                 }
857         }
858
859         /*
860                 Manage active block list
861         */
862         if(m_active_blocks_management_interval.step(dtime, 2.0))
863         {
864                 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
865                 /*
866                         Get player block positions
867                 */
868                 core::list<v3s16> players_blockpos;
869                 for(core::list<Player*>::Iterator
870                                 i = m_players.begin();
871                                 i != m_players.end(); i++)
872                 {
873                         Player *player = *i;
874                         // Ignore disconnected players
875                         if(player->peer_id == 0)
876                                 continue;
877                         v3s16 blockpos = getNodeBlockPos(
878                                         floatToInt(player->getPosition(), BS));
879                         players_blockpos.push_back(blockpos);
880                 }
881                 
882                 /*
883                         Update list of active blocks, collecting changes
884                 */
885                 const s16 active_block_range = g_settings->getS16("active_block_range");
886                 core::map<v3s16, bool> blocks_removed;
887                 core::map<v3s16, bool> blocks_added;
888                 m_active_blocks.update(players_blockpos, active_block_range,
889                                 blocks_removed, blocks_added);
890
891                 /*
892                         Handle removed blocks
893                 */
894
895                 // Convert active objects that are no more in active blocks to static
896                 deactivateFarObjects(false);
897                 
898                 for(core::map<v3s16, bool>::Iterator
899                                 i = blocks_removed.getIterator();
900                                 i.atEnd()==false; i++)
901                 {
902                         v3s16 p = i.getNode()->getKey();
903
904                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
905                                         <<") became inactive"<<std::endl;*/
906                         
907                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
908                         if(block==NULL)
909                                 continue;
910                         
911                         // Set current time as timestamp (and let it set ChangedFlag)
912                         block->setTimestamp(m_game_time);
913                 }
914
915                 /*
916                         Handle added blocks
917                 */
918
919                 for(core::map<v3s16, bool>::Iterator
920                                 i = blocks_added.getIterator();
921                                 i.atEnd()==false; i++)
922                 {
923                         v3s16 p = i.getNode()->getKey();
924                         
925                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
926                                         <<") became active"<<std::endl;*/
927
928                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
929                         if(block==NULL){
930                                 // Block needs to be fetched first
931                                 m_emerger->queueBlockEmerge(p, false);
932                                 m_active_blocks.m_list.remove(p);
933                                 continue;
934                         }
935
936                         activateBlock(block);
937                 }
938         }
939
940         /*
941                 Mess around in active blocks
942         */
943         if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
944         {
945                 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
946                 
947                 float dtime = 1.0;
948
949                 for(core::map<v3s16, bool>::Iterator
950                                 i = m_active_blocks.m_list.getIterator();
951                                 i.atEnd()==false; i++)
952                 {
953                         v3s16 p = i.getNode()->getKey();
954                         
955                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
956                                         <<") being handled"<<std::endl;*/
957
958                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
959                         if(block==NULL)
960                                 continue;
961
962                         // Reset block usage timer
963                         block->resetUsageTimer();
964                         
965                         // Set current time as timestamp
966                         block->setTimestampNoChangedFlag(m_game_time);
967                         // If time has changed much from the one on disk,
968                         // set block to be saved when it is unloaded
969                         if(block->getTimestamp() > block->getDiskTimestamp() + 60)
970                                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
971                                                 "Timestamp older than 60s (step)");
972
973                         // Run node metadata
974                         bool changed = block->m_node_metadata->step(dtime);
975                         if(changed)
976                         {
977                                 MapEditEvent event;
978                                 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
979                                 event.p = p;
980                                 m_map->dispatchEvent(&event);
981
982                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
983                                                 "node metadata modified in step");
984                         }
985                 }
986         }
987         
988         const float abm_interval = 1.0;
989         if(m_active_block_modifier_interval.step(dtime, abm_interval))
990         {
991                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
992                 TimeTaker timer("modify in active blocks");
993                 
994                 // Initialize handling of ActiveBlockModifiers
995                 ABMHandler abmhandler(m_abms, abm_interval, this, true);
996
997                 for(core::map<v3s16, bool>::Iterator
998                                 i = m_active_blocks.m_list.getIterator();
999                                 i.atEnd()==false; i++)
1000                 {
1001                         v3s16 p = i.getNode()->getKey();
1002                         
1003                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1004                                         <<") being handled"<<std::endl;*/
1005
1006                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1007                         if(block==NULL)
1008                                 continue;
1009                         
1010                         // Set current time as timestamp
1011                         block->setTimestampNoChangedFlag(m_game_time);
1012
1013                         /* Handle ActiveBlockModifiers */
1014                         abmhandler.apply(block);
1015                 }
1016
1017                 u32 time_ms = timer.stop(true);
1018                 u32 max_time_ms = 200;
1019                 if(time_ms > max_time_ms){
1020                         infostream<<"WARNING: active block modifiers took "
1021                                         <<time_ms<<"ms (longer than "
1022                                         <<max_time_ms<<"ms)"<<std::endl;
1023                 }
1024         }
1025         
1026         /*
1027                 Step script environment (run global on_step())
1028         */
1029         scriptapi_environment_step(m_lua, dtime);
1030
1031         /*
1032                 Step active objects
1033         */
1034         {
1035                 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1036                 //TimeTaker timer("Step active objects");
1037
1038                 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1039                 
1040                 // This helps the objects to send data at the same time
1041                 bool send_recommended = false;
1042                 m_send_recommended_timer += dtime;
1043                 if(m_send_recommended_timer > getSendRecommendedInterval())
1044                 {
1045                         m_send_recommended_timer -= getSendRecommendedInterval();
1046                         send_recommended = true;
1047                 }
1048
1049                 for(core::map<u16, ServerActiveObject*>::Iterator
1050                                 i = m_active_objects.getIterator();
1051                                 i.atEnd()==false; i++)
1052                 {
1053                         ServerActiveObject* obj = i.getNode()->getValue();
1054                         // Remove non-peaceful mobs on peaceful mode
1055                         if(g_settings->getBool("only_peaceful_mobs")){
1056                                 if(!obj->isPeaceful())
1057                                         obj->m_removed = true;
1058                         }
1059                         // Don't step if is to be removed or stored statically
1060                         if(obj->m_removed || obj->m_pending_deactivation)
1061                                 continue;
1062                         // Step object
1063                         obj->step(dtime, send_recommended);
1064                         // Read messages from object
1065                         while(obj->m_messages_out.size() > 0)
1066                         {
1067                                 m_active_object_messages.push_back(
1068                                                 obj->m_messages_out.pop_front());
1069                         }
1070                 }
1071         }
1072         
1073         /*
1074                 Manage active objects
1075         */
1076         if(m_object_management_interval.step(dtime, 0.5))
1077         {
1078                 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1079                 /*
1080                         Remove objects that satisfy (m_removed && m_known_by_count==0)
1081                 */
1082                 removeRemovedObjects();
1083         }
1084 }
1085
1086 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1087 {
1088         core::map<u16, ServerActiveObject*>::Node *n;
1089         n = m_active_objects.find(id);
1090         if(n == NULL)
1091                 return NULL;
1092         return n->getValue();
1093 }
1094
1095 bool isFreeServerActiveObjectId(u16 id,
1096                 core::map<u16, ServerActiveObject*> &objects)
1097 {
1098         if(id == 0)
1099                 return false;
1100         
1101         for(core::map<u16, ServerActiveObject*>::Iterator
1102                         i = objects.getIterator();
1103                         i.atEnd()==false; i++)
1104         {
1105                 if(i.getNode()->getKey() == id)
1106                         return false;
1107         }
1108         return true;
1109 }
1110
1111 u16 getFreeServerActiveObjectId(
1112                 core::map<u16, ServerActiveObject*> &objects)
1113 {
1114         u16 new_id = 1;
1115         for(;;)
1116         {
1117                 if(isFreeServerActiveObjectId(new_id, objects))
1118                         return new_id;
1119                 
1120                 if(new_id == 65535)
1121                         return 0;
1122
1123                 new_id++;
1124         }
1125 }
1126
1127 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1128 {
1129         assert(object);
1130         u16 id = addActiveObjectRaw(object, true);
1131         return id;
1132 }
1133
1134 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1135 {
1136         assert(obj);
1137
1138         v3f objectpos = obj->getBasePosition(); 
1139
1140         // The block in which the object resides in
1141         v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1142
1143         /*
1144                 Update the static data
1145         */
1146
1147         // Create new static object
1148         std::string staticdata = obj->getStaticData();
1149         StaticObject s_obj(obj->getType(), objectpos, staticdata);
1150         // Add to the block where the object is located in
1151         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1152         // Get or generate the block
1153         MapBlock *block = m_map->emergeBlock(blockpos);
1154
1155         bool succeeded = false;
1156
1157         if(block)
1158         {
1159                 block->m_static_objects.insert(0, s_obj);
1160                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1161                                 "addActiveObjectAsStatic");
1162                 succeeded = true;
1163         }
1164         else{
1165                 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1166                                 <<"Could not find or generate "
1167                                 <<"a block for storing static object"<<std::endl;
1168                 succeeded = false;
1169         }
1170
1171         delete obj;
1172
1173         return succeeded;
1174 }
1175
1176 /*
1177         Finds out what new objects have been added to
1178         inside a radius around a position
1179 */
1180 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1181                 core::map<u16, bool> &current_objects,
1182                 core::map<u16, bool> &added_objects)
1183 {
1184         v3f pos_f = intToFloat(pos, BS);
1185         f32 radius_f = radius * BS;
1186         /*
1187                 Go through the object list,
1188                 - discard m_removed objects,
1189                 - discard objects that are too far away,
1190                 - discard objects that are found in current_objects.
1191                 - add remaining objects to added_objects
1192         */
1193         for(core::map<u16, ServerActiveObject*>::Iterator
1194                         i = m_active_objects.getIterator();
1195                         i.atEnd()==false; i++)
1196         {
1197                 u16 id = i.getNode()->getKey();
1198                 // Get object
1199                 ServerActiveObject *object = i.getNode()->getValue();
1200                 if(object == NULL)
1201                         continue;
1202                 // Discard if removed
1203                 if(object->m_removed)
1204                         continue;
1205                 // Discard if too far
1206                 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1207                 if(distance_f > radius_f)
1208                         continue;
1209                 // Discard if already on current_objects
1210                 core::map<u16, bool>::Node *n;
1211                 n = current_objects.find(id);
1212                 if(n != NULL)
1213                         continue;
1214                 // Add to added_objects
1215                 added_objects.insert(id, false);
1216         }
1217 }
1218
1219 /*
1220         Finds out what objects have been removed from
1221         inside a radius around a position
1222 */
1223 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1224                 core::map<u16, bool> &current_objects,
1225                 core::map<u16, bool> &removed_objects)
1226 {
1227         v3f pos_f = intToFloat(pos, BS);
1228         f32 radius_f = radius * BS;
1229         /*
1230                 Go through current_objects; object is removed if:
1231                 - object is not found in m_active_objects (this is actually an
1232                   error condition; objects should be set m_removed=true and removed
1233                   only after all clients have been informed about removal), or
1234                 - object has m_removed=true, or
1235                 - object is too far away
1236         */
1237         for(core::map<u16, bool>::Iterator
1238                         i = current_objects.getIterator();
1239                         i.atEnd()==false; i++)
1240         {
1241                 u16 id = i.getNode()->getKey();
1242                 ServerActiveObject *object = getActiveObject(id);
1243                 if(object == NULL)
1244                 {
1245                         infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1246                                         <<" object in current_objects is NULL"<<std::endl;
1247                 }
1248                 else if(object->m_removed == false)
1249                 {
1250                         f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1251                         /*infostream<<"removed == false"
1252                                         <<"distance_f = "<<distance_f
1253                                         <<", radius_f = "<<radius_f<<std::endl;*/
1254                         if(distance_f < radius_f)
1255                         {
1256                                 // Not removed
1257                                 continue;
1258                         }
1259                 }
1260                 removed_objects.insert(id, false);
1261         }
1262 }
1263
1264 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1265 {
1266         if(m_active_object_messages.size() == 0)
1267                 return ActiveObjectMessage(0);
1268         
1269         return m_active_object_messages.pop_front();
1270 }
1271
1272 /*
1273         ************ Private methods *************
1274 */
1275
1276 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1277                 bool set_changed)
1278 {
1279         assert(object);
1280         if(object->getId() == 0){
1281                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1282                 if(new_id == 0)
1283                 {
1284                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1285                                         <<"no free ids available"<<std::endl;
1286                         delete object;
1287                         return 0;
1288                 }
1289                 object->setId(new_id);
1290         }
1291         else{
1292                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1293                                 <<"supplied with id "<<object->getId()<<std::endl;
1294         }
1295         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1296         {
1297                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1298                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1299                 delete object;
1300                 return 0;
1301         }
1302         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1303                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1304                         
1305         m_active_objects.insert(object->getId(), object);
1306   
1307         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1308                         <<"Added id="<<object->getId()<<"; there are now "
1309                         <<m_active_objects.size()<<" active objects."
1310                         <<std::endl;
1311         
1312         // Add static object to active static list of the block
1313         v3f objectpos = object->getBasePosition();
1314         std::string staticdata = object->getStaticData();
1315         StaticObject s_obj(object->getType(), objectpos, staticdata);
1316         // Add to the block where the object is located in
1317         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1318         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1319         if(block)
1320         {
1321                 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1322                 object->m_static_exists = true;
1323                 object->m_static_block = blockpos;
1324
1325                 if(set_changed)
1326                         block->raiseModified(MOD_STATE_WRITE_NEEDED, 
1327                                         "addActiveObjectRaw");
1328         }
1329         else{
1330                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1331                                 <<"could not find block for storing id="<<object->getId()
1332                                 <<" statically"<<std::endl;
1333         }
1334         
1335         // Register reference in scripting api (must be done before post-init)
1336         scriptapi_add_object_reference(m_lua, object);
1337         // Post-initialize object
1338         object->addedToEnvironment();
1339
1340         return object->getId();
1341 }
1342
1343 /*
1344         Remove objects that satisfy (m_removed && m_known_by_count==0)
1345 */
1346 void ServerEnvironment::removeRemovedObjects()
1347 {
1348         core::list<u16> objects_to_remove;
1349         for(core::map<u16, ServerActiveObject*>::Iterator
1350                         i = m_active_objects.getIterator();
1351                         i.atEnd()==false; i++)
1352         {
1353                 u16 id = i.getNode()->getKey();
1354                 ServerActiveObject* obj = i.getNode()->getValue();
1355                 // This shouldn't happen but check it
1356                 if(obj == NULL)
1357                 {
1358                         infostream<<"NULL object found in ServerEnvironment"
1359                                         <<" while finding removed objects. id="<<id<<std::endl;
1360                         // Id to be removed from m_active_objects
1361                         objects_to_remove.push_back(id);
1362                         continue;
1363                 }
1364
1365                 /*
1366                         We will delete objects that are marked as removed or thatare
1367                         waiting for deletion after deactivation
1368                 */
1369                 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1370                         continue;
1371
1372                 /*
1373                         Delete static data from block if is marked as removed
1374                 */
1375                 if(obj->m_static_exists && obj->m_removed)
1376                 {
1377                         MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1378                         if(block)
1379                         {
1380                                 block->m_static_objects.remove(id);
1381                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1382                                                 "removeRemovedObjects");
1383                                 obj->m_static_exists = false;
1384                         }
1385                 }
1386
1387                 // If m_known_by_count > 0, don't actually remove.
1388                 if(obj->m_known_by_count > 0)
1389                         continue;
1390                 
1391                 // Deregister in scripting api
1392                 scriptapi_rm_object_reference(m_lua, obj);
1393
1394                 // Delete
1395                 delete obj;
1396                 // Id to be removed from m_active_objects
1397                 objects_to_remove.push_back(id);
1398         }
1399         // Remove references from m_active_objects
1400         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1401                         i != objects_to_remove.end(); i++)
1402         {
1403                 m_active_objects.remove(*i);
1404         }
1405 }
1406
1407 static void print_hexdump(std::ostream &o, const std::string &data)
1408 {
1409         const int linelength = 16;
1410         for(int l=0; ; l++){
1411                 int i0 = linelength * l;
1412                 bool at_end = false;
1413                 int thislinelength = linelength;
1414                 if(i0 + thislinelength > (int)data.size()){
1415                         thislinelength = data.size() - i0;
1416                         at_end = true;
1417                 }
1418                 for(int di=0; di<linelength; di++){
1419                         int i = i0 + di;
1420                         char buf[4];
1421                         if(di<thislinelength)
1422                                 snprintf(buf, 4, "%.2x ", data[i]);
1423                         else
1424                                 snprintf(buf, 4, "   ");
1425                         o<<buf;
1426                 }
1427                 o<<" ";
1428                 for(int di=0; di<thislinelength; di++){
1429                         int i = i0 + di;
1430                         if(data[i] >= 32)
1431                                 o<<data[i];
1432                         else
1433                                 o<<".";
1434                 }
1435                 o<<std::endl;
1436                 if(at_end)
1437                         break;
1438         }
1439 }
1440
1441 /*
1442         Convert stored objects from blocks near the players to active.
1443 */
1444 void ServerEnvironment::activateObjects(MapBlock *block)
1445 {
1446         if(block==NULL)
1447                 return;
1448         // Ignore if no stored objects (to not set changed flag)
1449         if(block->m_static_objects.m_stored.size() == 0)
1450                 return;
1451         verbosestream<<"ServerEnvironment::activateObjects(): "
1452                         <<"activating objects of block "<<PP(block->getPos())
1453                         <<" ("<<block->m_static_objects.m_stored.size()
1454                         <<" objects)"<<std::endl;
1455         bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1456         if(large_amount){
1457                 errorstream<<"suspiciously large amount of objects detected: "
1458                                 <<block->m_static_objects.m_stored.size()<<" in "
1459                                 <<PP(block->getPos())
1460                                 <<"; removing all of them."<<std::endl;
1461                 // Clear stored list
1462                 block->m_static_objects.m_stored.clear();
1463                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1464                                 "stored list cleared in activateObjects due to "
1465                                 "large amount of objects");
1466                 return;
1467         }
1468         // A list for objects that couldn't be converted to static for some
1469         // reason. They will be stored back.
1470         core::list<StaticObject> new_stored;
1471         // Loop through stored static objects
1472         for(core::list<StaticObject>::Iterator
1473                         i = block->m_static_objects.m_stored.begin();
1474                         i != block->m_static_objects.m_stored.end(); i++)
1475         {
1476                 /*infostream<<"Server: Creating an active object from "
1477                                 <<"static data"<<std::endl;*/
1478                 StaticObject &s_obj = *i;
1479                 // Create an active object from the data
1480                 ServerActiveObject *obj = ServerActiveObject::create
1481                                 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1482                 // If couldn't create object, store static data back.
1483                 if(obj==NULL)
1484                 {
1485                         errorstream<<"ServerEnvironment::activateObjects(): "
1486                                         <<"failed to create active object from static object "
1487                                         <<"in block "<<PP(s_obj.pos/BS)
1488                                         <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1489                         print_hexdump(verbosestream, s_obj.data);
1490                         
1491                         new_stored.push_back(s_obj);
1492                         continue;
1493                 }
1494                 verbosestream<<"ServerEnvironment::activateObjects(): "
1495                                 <<"activated static object pos="<<PP(s_obj.pos/BS)
1496                                 <<" type="<<(int)s_obj.type<<std::endl;
1497                 // This will also add the object to the active static list
1498                 addActiveObjectRaw(obj, false);
1499         }
1500         // Clear stored list
1501         block->m_static_objects.m_stored.clear();
1502         // Add leftover failed stuff to stored list
1503         for(core::list<StaticObject>::Iterator
1504                         i = new_stored.begin();
1505                         i != new_stored.end(); i++)
1506         {
1507                 StaticObject &s_obj = *i;
1508                 block->m_static_objects.m_stored.push_back(s_obj);
1509         }
1510         /*
1511                 Note: Block hasn't really been modified here.
1512                 The objects have just been activated and moved from the stored
1513                 static list to the active static list.
1514                 As such, the block is essentially the same.
1515                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1516                 Otherwise there would be a huge amount of unnecessary I/O.
1517         */
1518 }
1519
1520 /*
1521         Convert objects that are not standing inside active blocks to static.
1522
1523         If m_known_by_count != 0, active object is not deleted, but static
1524         data is still updated.
1525
1526         If force_delete is set, active object is deleted nevertheless. It
1527         shall only be set so in the destructor of the environment.
1528 */
1529 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1530 {
1531         core::list<u16> objects_to_remove;
1532         for(core::map<u16, ServerActiveObject*>::Iterator
1533                         i = m_active_objects.getIterator();
1534                         i.atEnd()==false; i++)
1535         {
1536                 ServerActiveObject* obj = i.getNode()->getValue();
1537
1538                 // This shouldn't happen but check it
1539                 if(obj == NULL)
1540                 {
1541                         errorstream<<"NULL object found in ServerEnvironment"
1542                                         <<std::endl;
1543                         assert(0);
1544                         continue;
1545                 }
1546
1547                 // If pending deactivation, let removeRemovedObjects() do it
1548                 if(obj->m_pending_deactivation)
1549                         continue;
1550
1551                 u16 id = i.getNode()->getKey();         
1552                 v3f objectpos = obj->getBasePosition(); 
1553
1554                 // The block in which the object resides in
1555                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1556
1557                 // If block is active, don't remove
1558                 if(m_active_blocks.contains(blockpos_o))
1559                         continue;
1560
1561                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1562                                 <<"deactivating object id="<<id<<" on inactive block "
1563                                 <<PP(blockpos_o)<<std::endl;
1564
1565                 // If known by some client, don't immediately delete.
1566                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1567
1568                 /*
1569                         Update the static data
1570                 */
1571
1572                 // Create new static object
1573                 std::string staticdata_new = obj->getStaticData();
1574                 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1575                 
1576                 bool stays_in_same_block = false;
1577                 bool data_changed = true;
1578
1579                 if(obj->m_static_exists){
1580                         if(obj->m_static_block == blockpos_o)
1581                                 stays_in_same_block = true;
1582
1583                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1584                         
1585                         core::map<u16, StaticObject>::Node *n =
1586                                         block->m_static_objects.m_active.find(id);
1587                         if(n){
1588                                 StaticObject static_old = n->getValue();
1589
1590                                 float save_movem = obj->getMinimumSavedMovement();
1591
1592                                 if(static_old.data == staticdata_new &&
1593                                                 (static_old.pos - objectpos).getLength() < save_movem)
1594                                         data_changed = false;
1595                         } else {
1596                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1597                                                 <<"id="<<id<<" m_static_exists=true but "
1598                                                 <<"static data doesn't actually exist in "
1599                                                 <<PP(obj->m_static_block)<<std::endl;
1600                         }
1601                 }
1602
1603                 bool shall_be_written = (!stays_in_same_block || data_changed);
1604                 
1605                 // Delete old static object
1606                 if(obj->m_static_exists)
1607                 {
1608                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1609                         if(block)
1610                         {
1611                                 block->m_static_objects.remove(id);
1612                                 obj->m_static_exists = false;
1613                                 // Only mark block as modified if data changed considerably
1614                                 if(shall_be_written)
1615                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1616                                                         "deactivateFarObjects: Static data "
1617                                                         "changed considerably");
1618                         }
1619                 }
1620
1621                 // Add to the block where the object is located in
1622                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1623                 // Get or generate the block
1624                 MapBlock *block = m_map->emergeBlock(blockpos);
1625
1626                 if(block)
1627                 {
1628                         if(block->m_static_objects.m_stored.size() >= 49){
1629                                 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1630                                                 <<" statically but block "<<PP(blockpos)
1631                                                 <<" already contains "
1632                                                 <<block->m_static_objects.m_stored.size()
1633                                                 <<" (over 49) objects."
1634                                                 <<" Forcing delete."<<std::endl;
1635                                 force_delete = true;
1636                         } else {
1637                                 u16 new_id = pending_delete ? id : 0;
1638                                 block->m_static_objects.insert(new_id, s_obj);
1639                                 
1640                                 // Only mark block as modified if data changed considerably
1641                                 if(shall_be_written)
1642                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1643                                                         "deactivateFarObjects: Static data "
1644                                                         "changed considerably");
1645                                 
1646                                 obj->m_static_exists = true;
1647                                 obj->m_static_block = block->getPos();
1648                         }
1649                 }
1650                 else{
1651                         errorstream<<"ServerEnv: Could not find or generate "
1652                                         <<"a block for storing id="<<obj->getId()
1653                                         <<" statically"<<std::endl;
1654                         continue;
1655                 }
1656
1657                 /*
1658                         If known by some client, set pending deactivation.
1659                         Otherwise delete it immediately.
1660                 */
1661
1662                 if(pending_delete)
1663                 {
1664                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1665                                         <<"object id="<<id<<" is known by clients"
1666                                         <<"; not deleting yet"<<std::endl;
1667
1668                         obj->m_pending_deactivation = true;
1669                         continue;
1670                 }
1671                 
1672                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1673                                 <<"object id="<<id<<" is not known by clients"
1674                                 <<"; deleting"<<std::endl;
1675
1676                 // Deregister in scripting api
1677                 scriptapi_rm_object_reference(m_lua, obj);
1678
1679                 // Delete active object
1680                 delete obj;
1681                 // Id to be removed from m_active_objects
1682                 objects_to_remove.push_back(id);
1683         }
1684
1685         // Remove references from m_active_objects
1686         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1687                         i != objects_to_remove.end(); i++)
1688         {
1689                 m_active_objects.remove(*i);
1690         }
1691 }
1692
1693
1694 #ifndef SERVER
1695
1696 /*
1697         ClientEnvironment
1698 */
1699
1700 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1701                 ITextureSource *texturesource, IGameDef *gamedef):
1702         m_map(map),
1703         m_smgr(smgr),
1704         m_texturesource(texturesource),
1705         m_gamedef(gamedef)
1706 {
1707         assert(m_map);
1708         assert(m_smgr);
1709 }
1710
1711 ClientEnvironment::~ClientEnvironment()
1712 {
1713         // delete active objects
1714         for(core::map<u16, ClientActiveObject*>::Iterator
1715                         i = m_active_objects.getIterator();
1716                         i.atEnd()==false; i++)
1717         {
1718                 delete i.getNode()->getValue();
1719         }
1720
1721         // Drop/delete map
1722         m_map->drop();
1723 }
1724
1725 void ClientEnvironment::addPlayer(Player *player)
1726 {
1727         DSTACK(__FUNCTION_NAME);
1728         /*
1729                 It is a failure if player is local and there already is a local
1730                 player
1731         */
1732         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1733
1734         Environment::addPlayer(player);
1735 }
1736
1737 LocalPlayer * ClientEnvironment::getLocalPlayer()
1738 {
1739         for(core::list<Player*>::Iterator i = m_players.begin();
1740                         i != m_players.end(); i++)
1741         {
1742                 Player *player = *i;
1743                 if(player->isLocal())
1744                         return (LocalPlayer*)player;
1745         }
1746         return NULL;
1747 }
1748
1749 void ClientEnvironment::step(float dtime)
1750 {
1751         DSTACK(__FUNCTION_NAME);
1752
1753         // Get some settings
1754         bool free_move = g_settings->getBool("free_move");
1755         bool footprints = g_settings->getBool("footprints");
1756
1757         // Get local player
1758         LocalPlayer *lplayer = getLocalPlayer();
1759         assert(lplayer);
1760         // collision info queue
1761         core::list<CollisionInfo> player_collisions;
1762         
1763         /*
1764                 Get the speed the player is going
1765         */
1766         bool is_climbing = lplayer->is_climbing;
1767         
1768         f32 player_speed = lplayer->getSpeed().getLength();
1769         
1770         /*
1771                 Maximum position increment
1772         */
1773         //f32 position_max_increment = 0.05*BS;
1774         f32 position_max_increment = 0.1*BS;
1775
1776         // Maximum time increment (for collision detection etc)
1777         // time = distance / speed
1778         f32 dtime_max_increment = 1;
1779         if(player_speed > 0.001)
1780                 dtime_max_increment = position_max_increment / player_speed;
1781         
1782         // Maximum time increment is 10ms or lower
1783         if(dtime_max_increment > 0.01)
1784                 dtime_max_increment = 0.01;
1785         
1786         // Don't allow overly huge dtime
1787         if(dtime > 0.5)
1788                 dtime = 0.5;
1789         
1790         f32 dtime_downcount = dtime;
1791
1792         /*
1793                 Stuff that has a maximum time increment
1794         */
1795
1796         u32 loopcount = 0;
1797         do
1798         {
1799                 loopcount++;
1800
1801                 f32 dtime_part;
1802                 if(dtime_downcount > dtime_max_increment)
1803                 {
1804                         dtime_part = dtime_max_increment;
1805                         dtime_downcount -= dtime_part;
1806                 }
1807                 else
1808                 {
1809                         dtime_part = dtime_downcount;
1810                         /*
1811                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
1812                                 when dtime_part is so small that dtime_downcount -= dtime_part
1813                                 does nothing
1814                         */
1815                         dtime_downcount = 0;
1816                 }
1817                 
1818                 /*
1819                         Handle local player
1820                 */
1821                 
1822                 {
1823                         v3f lplayerpos = lplayer->getPosition();
1824                         
1825                         // Apply physics
1826                         if(free_move == false && is_climbing == false)
1827                         {
1828                                 // Gravity
1829                                 v3f speed = lplayer->getSpeed();
1830                                 if(lplayer->swimming_up == false)
1831                                         speed.Y -= 9.81 * BS * dtime_part * 2;
1832
1833                                 // Water resistance
1834                                 if(lplayer->in_water_stable || lplayer->in_water)
1835                                 {
1836                                         f32 max_down = 2.0*BS;
1837                                         if(speed.Y < -max_down) speed.Y = -max_down;
1838
1839                                         f32 max = 2.5*BS;
1840                                         if(speed.getLength() > max)
1841                                         {
1842                                                 speed = speed / speed.getLength() * max;
1843                                         }
1844                                 }
1845
1846                                 lplayer->setSpeed(speed);
1847                         }
1848
1849                         /*
1850                                 Move the lplayer.
1851                                 This also does collision detection.
1852                         */
1853                         lplayer->move(dtime_part, *m_map, position_max_increment,
1854                                         &player_collisions);
1855                 }
1856         }
1857         while(dtime_downcount > 0.001);
1858                 
1859         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1860         
1861         for(core::list<CollisionInfo>::Iterator
1862                         i = player_collisions.begin();
1863                         i != player_collisions.end(); i++)
1864         {
1865                 CollisionInfo &info = *i;
1866                 if(info.t == COLLISION_FALL)
1867                 {
1868                         //f32 tolerance = BS*10; // 2 without damage
1869                         f32 tolerance = BS*12; // 3 without damage
1870                         f32 factor = 1;
1871                         if(info.speed > tolerance)
1872                         {
1873                                 f32 damage_f = (info.speed - tolerance)/BS*factor;
1874                                 u16 damage = (u16)(damage_f+0.5);
1875                                 if(lplayer->hp > damage)
1876                                         lplayer->hp -= damage;
1877                                 else
1878                                         lplayer->hp = 0;
1879
1880                                 ClientEnvEvent event;
1881                                 event.type = CEE_PLAYER_DAMAGE;
1882                                 event.player_damage.amount = damage;
1883                                 m_client_event_queue.push_back(event);
1884                         }
1885                 }
1886         }
1887         
1888         /*
1889                 A quick draft of lava damage
1890         */
1891         if(m_lava_hurt_interval.step(dtime, 1.0))
1892         {
1893                 v3f pf = lplayer->getPosition();
1894                 
1895                 // Feet, middle and head
1896                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1897                 MapNode n1 = m_map->getNodeNoEx(p1);
1898                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1899                 MapNode n2 = m_map->getNodeNoEx(p2);
1900                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1901                 MapNode n3 = m_map->getNodeNoEx(p2);
1902
1903                 u32 damage_per_second = 0;
1904                 damage_per_second = MYMAX(damage_per_second,
1905                                 m_gamedef->ndef()->get(n1).damage_per_second);
1906                 damage_per_second = MYMAX(damage_per_second,
1907                                 m_gamedef->ndef()->get(n2).damage_per_second);
1908                 damage_per_second = MYMAX(damage_per_second,
1909                                 m_gamedef->ndef()->get(n3).damage_per_second);
1910                 
1911                 if(damage_per_second != 0)
1912                 {
1913                         ClientEnvEvent event;
1914                         event.type = CEE_PLAYER_DAMAGE;
1915                         event.player_damage.amount = damage_per_second;
1916                         m_client_event_queue.push_back(event);
1917                 }
1918         }
1919         
1920         /*
1921                 Stuff that can be done in an arbitarily large dtime
1922         */
1923         for(core::list<Player*>::Iterator i = m_players.begin();
1924                         i != m_players.end(); i++)
1925         {
1926                 Player *player = *i;
1927                 v3f playerpos = player->getPosition();
1928                 
1929                 /*
1930                         Handle non-local players
1931                 */
1932                 if(player->isLocal() == false)
1933                 {
1934                         // Move
1935                         player->move(dtime, *m_map, 100*BS);
1936
1937                 }
1938                 
1939                 // Update lighting on all players on client
1940                 u8 light = LIGHT_MAX;
1941                 try{
1942                         // Get node at head
1943                         v3s16 p = player->getLightPosition();
1944                         MapNode n = m_map->getNode(p);
1945                         light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
1946                 }
1947                 catch(InvalidPositionException &e) {}
1948                 player->updateLight(light);
1949
1950                 /*
1951                         Add footsteps to grass
1952                 */
1953                 if(footprints)
1954                 {
1955                         // Get node that is at BS/4 under player
1956                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1957                         try{
1958                                 MapNode n = m_map->getNode(bottompos);
1959                                 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
1960                                 {
1961                                         n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
1962                                         m_map->setNode(bottompos, n);
1963                                         // Update mesh on client
1964                                         if(m_map->mapType() == MAPTYPE_CLIENT)
1965                                         {
1966                                                 v3s16 p_blocks = getNodeBlockPos(bottompos);
1967                                                 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1968                                                 //b->updateMesh(getDayNightRatio());
1969                                                 b->setMeshExpired(true);
1970                                         }
1971                                 }
1972                         }
1973                         catch(InvalidPositionException &e)
1974                         {
1975                         }
1976                 }
1977         }
1978         
1979         /*
1980                 Step active objects and update lighting of them
1981         */
1982         
1983         for(core::map<u16, ClientActiveObject*>::Iterator
1984                         i = m_active_objects.getIterator();
1985                         i.atEnd()==false; i++)
1986         {
1987                 ClientActiveObject* obj = i.getNode()->getValue();
1988                 // Step object
1989                 obj->step(dtime, this);
1990
1991                 if(m_active_object_light_update_interval.step(dtime, 0.21))
1992                 {
1993                         // Update lighting
1994                         //u8 light = LIGHT_MAX;
1995                         u8 light = 0;
1996                         try{
1997                                 // Get node at head
1998                                 v3s16 p = obj->getLightPosition();
1999                                 MapNode n = m_map->getNode(p);
2000                                 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2001                         }
2002                         catch(InvalidPositionException &e) {}
2003                         obj->updateLight(light);
2004                 }
2005         }
2006 }
2007
2008 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2009 {
2010         m_map->updateMeshes(blockpos, getDayNightRatio());
2011 }
2012
2013 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2014 {
2015         m_map->expireMeshes(only_daynight_diffed);
2016 }
2017
2018 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2019 {
2020         core::map<u16, ClientActiveObject*>::Node *n;
2021         n = m_active_objects.find(id);
2022         if(n == NULL)
2023                 return NULL;
2024         return n->getValue();
2025 }
2026
2027 bool isFreeClientActiveObjectId(u16 id,
2028                 core::map<u16, ClientActiveObject*> &objects)
2029 {
2030         if(id == 0)
2031                 return false;
2032         
2033         for(core::map<u16, ClientActiveObject*>::Iterator
2034                         i = objects.getIterator();
2035                         i.atEnd()==false; i++)
2036         {
2037                 if(i.getNode()->getKey() == id)
2038                         return false;
2039         }
2040         return true;
2041 }
2042
2043 u16 getFreeClientActiveObjectId(
2044                 core::map<u16, ClientActiveObject*> &objects)
2045 {
2046         u16 new_id = 1;
2047         for(;;)
2048         {
2049                 if(isFreeClientActiveObjectId(new_id, objects))
2050                         return new_id;
2051                 
2052                 if(new_id == 65535)
2053                         return 0;
2054
2055                 new_id++;
2056         }
2057 }
2058
2059 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2060 {
2061         assert(object);
2062         if(object->getId() == 0)
2063         {
2064                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2065                 if(new_id == 0)
2066                 {
2067                         infostream<<"ClientEnvironment::addActiveObject(): "
2068                                         <<"no free ids available"<<std::endl;
2069                         delete object;
2070                         return 0;
2071                 }
2072                 object->setId(new_id);
2073         }
2074         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2075         {
2076                 infostream<<"ClientEnvironment::addActiveObject(): "
2077                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2078                 delete object;
2079                 return 0;
2080         }
2081         infostream<<"ClientEnvironment::addActiveObject(): "
2082                         <<"added (id="<<object->getId()<<")"<<std::endl;
2083         m_active_objects.insert(object->getId(), object);
2084         object->addToScene(m_smgr, m_texturesource);
2085         { // Update lighting immediately
2086                 u8 light = 0;
2087                 try{
2088                         // Get node at head
2089                         v3s16 p = object->getLightPosition();
2090                         MapNode n = m_map->getNode(p);
2091                         light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2092                 }
2093                 catch(InvalidPositionException &e) {}
2094                 object->updateLight(light);
2095         }
2096         return object->getId();
2097 }
2098
2099 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2100                 const std::string &init_data)
2101 {
2102         ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2103         if(obj == NULL)
2104         {
2105                 infostream<<"ClientEnvironment::addActiveObject(): "
2106                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2107                                 <<std::endl;
2108                 return;
2109         }
2110         
2111         obj->setId(id);
2112
2113         obj->initialize(init_data);
2114         
2115         addActiveObject(obj);
2116 }
2117
2118 void ClientEnvironment::removeActiveObject(u16 id)
2119 {
2120         infostream<<"ClientEnvironment::removeActiveObject(): "
2121                         <<"id="<<id<<std::endl;
2122         ClientActiveObject* obj = getActiveObject(id);
2123         if(obj == NULL)
2124         {
2125                 infostream<<"ClientEnvironment::removeActiveObject(): "
2126                                 <<"id="<<id<<" not found"<<std::endl;
2127                 return;
2128         }
2129         obj->removeFromScene();
2130         delete obj;
2131         m_active_objects.remove(id);
2132 }
2133
2134 void ClientEnvironment::processActiveObjectMessage(u16 id,
2135                 const std::string &data)
2136 {
2137         ClientActiveObject* obj = getActiveObject(id);
2138         if(obj == NULL)
2139         {
2140                 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2141                                 <<" got message for id="<<id<<", which doesn't exist."
2142                                 <<std::endl;
2143                 return;
2144         }
2145         obj->processMessage(data);
2146 }
2147
2148 /*
2149         Callbacks for activeobjects
2150 */
2151
2152 void ClientEnvironment::damageLocalPlayer(u8 damage)
2153 {
2154         LocalPlayer *lplayer = getLocalPlayer();
2155         assert(lplayer);
2156
2157         if(lplayer->hp > damage)
2158                 lplayer->hp -= damage;
2159         else
2160                 lplayer->hp = 0;
2161
2162         ClientEnvEvent event;
2163         event.type = CEE_PLAYER_DAMAGE;
2164         event.player_damage.amount = damage;
2165         m_client_event_queue.push_back(event);
2166 }
2167
2168 /*
2169         Client likes to call these
2170 */
2171         
2172 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2173                 core::array<DistanceSortedActiveObject> &dest)
2174 {
2175         for(core::map<u16, ClientActiveObject*>::Iterator
2176                         i = m_active_objects.getIterator();
2177                         i.atEnd()==false; i++)
2178         {
2179                 ClientActiveObject* obj = i.getNode()->getValue();
2180
2181                 f32 d = (obj->getPosition() - origin).getLength();
2182
2183                 if(d > max_d)
2184                         continue;
2185
2186                 DistanceSortedActiveObject dso(obj, d);
2187
2188                 dest.push_back(dso);
2189         }
2190 }
2191
2192 ClientEnvEvent ClientEnvironment::getClientEvent()
2193 {
2194         if(m_client_event_queue.size() == 0)
2195         {
2196                 ClientEnvEvent event;
2197                 event.type = CEE_NONE;
2198                 return event;
2199         }
2200         return m_client_event_queue.pop_front();
2201 }
2202
2203 #endif // #ifndef SERVER
2204
2205