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