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