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