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