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