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