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