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