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