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