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