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