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