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