]> git.lizzy.rs Git - dragonfireclient.git/blob - src/environment.cpp
5249dd9ec5757de6b1a8afb981feaa4e34ef83d7
[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 = m_forceloaded_list;
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(report_interval != 0 &&
1009                                 num_blocks_checked % report_interval == 0){
1010                         float percent = 100.0 * (float)num_blocks_checked /
1011                                         loadable_blocks.size();
1012                         infostream<<"ServerEnvironment::clearAllObjects(): "
1013                                         <<"Cleared "<<num_objs_cleared<<" objects"
1014                                         <<" in "<<num_blocks_cleared<<" blocks ("
1015                                         <<percent<<"%)"<<std::endl;
1016                 }
1017                 if(num_blocks_checked % unload_interval == 0){
1018                         m_map->unloadUnreferencedBlocks();
1019                 }
1020         }
1021         m_map->unloadUnreferencedBlocks();
1022
1023         // Drop references that were added above
1024         for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1025                         i != loaded_blocks.end(); ++i)
1026         {
1027                 v3s16 p = *i;
1028                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1029                 assert(block);
1030                 block->refDrop();
1031         }
1032
1033         infostream<<"ServerEnvironment::clearAllObjects(): "
1034                         <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1035                         <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1036 }
1037
1038 void ServerEnvironment::step(float dtime)
1039 {
1040         DSTACK(__FUNCTION_NAME);
1041         
1042         //TimeTaker timer("ServerEnv step");
1043
1044         /* Step time of day */
1045         stepTimeOfDay(dtime);
1046
1047         // Update this one
1048         // NOTE: This is kind of funny on a singleplayer game, but doesn't
1049         // really matter that much.
1050         m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1051
1052         /*
1053                 Increment game time
1054         */
1055         {
1056                 m_game_time_fraction_counter += dtime;
1057                 u32 inc_i = (u32)m_game_time_fraction_counter;
1058                 m_game_time += inc_i;
1059                 m_game_time_fraction_counter -= (float)inc_i;
1060         }
1061         
1062         /*
1063                 Handle players
1064         */
1065         {
1066                 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1067                 for(std::list<Player*>::iterator i = m_players.begin();
1068                                 i != m_players.end(); ++i)
1069                 {
1070                         Player *player = *i;
1071                         
1072                         // Ignore disconnected players
1073                         if(player->peer_id == 0)
1074                                 continue;
1075                         
1076                         // Move
1077                         player->move(dtime, *m_map, 100*BS);
1078                 }
1079         }
1080
1081         /*
1082                 Manage active block list
1083         */
1084         if(m_active_blocks_management_interval.step(dtime, 2.0))
1085         {
1086                 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1087                 /*
1088                         Get player block positions
1089                 */
1090                 std::list<v3s16> players_blockpos;
1091                 for(std::list<Player*>::iterator
1092                                 i = m_players.begin();
1093                                 i != m_players.end(); ++i)
1094                 {
1095                         Player *player = *i;
1096                         // Ignore disconnected players
1097                         if(player->peer_id == 0)
1098                                 continue;
1099                         v3s16 blockpos = getNodeBlockPos(
1100                                         floatToInt(player->getPosition(), BS));
1101                         players_blockpos.push_back(blockpos);
1102                 }
1103                 
1104                 /*
1105                         Update list of active blocks, collecting changes
1106                 */
1107                 const s16 active_block_range = g_settings->getS16("active_block_range");
1108                 std::set<v3s16> blocks_removed;
1109                 std::set<v3s16> blocks_added;
1110                 m_active_blocks.update(players_blockpos, active_block_range,
1111                                 blocks_removed, blocks_added);
1112
1113                 /*
1114                         Handle removed blocks
1115                 */
1116
1117                 // Convert active objects that are no more in active blocks to static
1118                 deactivateFarObjects(false);
1119                 
1120                 for(std::set<v3s16>::iterator
1121                                 i = blocks_removed.begin();
1122                                 i != blocks_removed.end(); ++i)
1123                 {
1124                         v3s16 p = *i;
1125
1126                         /* infostream<<"Server: Block " << PP(p)
1127                                 << " became inactive"<<std::endl; */
1128                         
1129                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1130                         if(block==NULL)
1131                                 continue;
1132                         
1133                         // Set current time as timestamp (and let it set ChangedFlag)
1134                         block->setTimestamp(m_game_time);
1135                 }
1136
1137                 /*
1138                         Handle added blocks
1139                 */
1140
1141                 for(std::set<v3s16>::iterator
1142                                 i = blocks_added.begin();
1143                                 i != blocks_added.end(); ++i)
1144                 {
1145                         v3s16 p = *i;
1146
1147                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1148                         if(block==NULL){
1149                                 // Block needs to be fetched first
1150                                 m_emerger->enqueueBlockEmerge(
1151                                                 PEER_ID_INEXISTENT, p, false);
1152                                 m_active_blocks.m_list.erase(p);
1153                                 continue;
1154                         }
1155
1156                         activateBlock(block);
1157                         /* infostream<<"Server: Block " << PP(p)
1158                                 << " became active"<<std::endl; */
1159                 }
1160         }
1161
1162         /*
1163                 Mess around in active blocks
1164         */
1165         if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1166         {
1167                 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1168                 
1169                 float dtime = 1.0;
1170
1171                 for(std::set<v3s16>::iterator
1172                                 i = m_active_blocks.m_list.begin();
1173                                 i != m_active_blocks.m_list.end(); ++i)
1174                 {
1175                         v3s16 p = *i;
1176                         
1177                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1178                                         <<") being handled"<<std::endl;*/
1179
1180                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1181                         if(block==NULL)
1182                                 continue;
1183
1184                         // Reset block usage timer
1185                         block->resetUsageTimer();
1186                         
1187                         // Set current time as timestamp
1188                         block->setTimestampNoChangedFlag(m_game_time);
1189                         // If time has changed much from the one on disk,
1190                         // set block to be saved when it is unloaded
1191                         if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1192                                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1193                                                 "Timestamp older than 60s (step)");
1194
1195                         // Run node timers
1196                         std::map<v3s16, NodeTimer> elapsed_timers =
1197                                 block->m_node_timers.step((float)dtime);
1198                         if(!elapsed_timers.empty()){
1199                                 MapNode n;
1200                                 for(std::map<v3s16, NodeTimer>::iterator
1201                                                 i = elapsed_timers.begin();
1202                                                 i != elapsed_timers.end(); i++){
1203                                         n = block->getNodeNoEx(i->first);
1204                                         p = i->first + block->getPosRelative();
1205                                         if(m_script->node_on_timer(p,n,i->second.elapsed))
1206                                                 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1207                                 }
1208                         }
1209                 }
1210         }
1211         
1212         const float abm_interval = 1.0;
1213         if(m_active_block_modifier_interval.step(dtime, abm_interval))
1214         do{ // breakable
1215                 if(m_active_block_interval_overload_skip > 0){
1216                         ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1217                         m_active_block_interval_overload_skip--;
1218                         break;
1219                 }
1220                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1221                 TimeTaker timer("modify in active blocks");
1222                 
1223                 // Initialize handling of ActiveBlockModifiers
1224                 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1225
1226                 for(std::set<v3s16>::iterator
1227                                 i = m_active_blocks.m_list.begin();
1228                                 i != m_active_blocks.m_list.end(); ++i)
1229                 {
1230                         v3s16 p = *i;
1231                         
1232                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1233                                         <<") being handled"<<std::endl;*/
1234
1235                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1236                         if(block==NULL)
1237                                 continue;
1238                         
1239                         // Set current time as timestamp
1240                         block->setTimestampNoChangedFlag(m_game_time);
1241
1242                         /* Handle ActiveBlockModifiers */
1243                         abmhandler.apply(block);
1244                 }
1245
1246                 u32 time_ms = timer.stop(true);
1247                 u32 max_time_ms = 200;
1248                 if(time_ms > max_time_ms){
1249                         infostream<<"WARNING: active block modifiers took "
1250                                         <<time_ms<<"ms (longer than "
1251                                         <<max_time_ms<<"ms)"<<std::endl;
1252                         m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1253                 }
1254         }while(0);
1255         
1256         /*
1257                 Step script environment (run global on_step())
1258         */
1259         m_script->environment_Step(dtime);
1260
1261         /*
1262                 Step active objects
1263         */
1264         {
1265                 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1266                 //TimeTaker timer("Step active objects");
1267
1268                 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1269                 
1270                 // This helps the objects to send data at the same time
1271                 bool send_recommended = false;
1272                 m_send_recommended_timer += dtime;
1273                 if(m_send_recommended_timer > getSendRecommendedInterval())
1274                 {
1275                         m_send_recommended_timer -= getSendRecommendedInterval();
1276                         send_recommended = true;
1277                 }
1278
1279                 for(std::map<u16, ServerActiveObject*>::iterator
1280                                 i = m_active_objects.begin();
1281                                 i != m_active_objects.end(); ++i)
1282                 {
1283                         ServerActiveObject* obj = i->second;
1284                         // Remove non-peaceful mobs on peaceful mode
1285                         if(g_settings->getBool("only_peaceful_mobs")){
1286                                 if(!obj->isPeaceful())
1287                                         obj->m_removed = true;
1288                         }
1289                         // Don't step if is to be removed or stored statically
1290                         if(obj->m_removed || obj->m_pending_deactivation)
1291                                 continue;
1292                         // Step object
1293                         obj->step(dtime, send_recommended);
1294                         // Read messages from object
1295                         while(!obj->m_messages_out.empty())
1296                         {
1297                                 m_active_object_messages.push_back(
1298                                                 obj->m_messages_out.pop_front());
1299                         }
1300                 }
1301         }
1302         
1303         /*
1304                 Manage active objects
1305         */
1306         if(m_object_management_interval.step(dtime, 0.5))
1307         {
1308                 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1309                 /*
1310                         Remove objects that satisfy (m_removed && m_known_by_count==0)
1311                 */
1312                 removeRemovedObjects();
1313         }
1314 }
1315
1316 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1317 {
1318         std::map<u16, ServerActiveObject*>::iterator n;
1319         n = m_active_objects.find(id);
1320         if(n == m_active_objects.end())
1321                 return NULL;
1322         return n->second;
1323 }
1324
1325 bool isFreeServerActiveObjectId(u16 id,
1326                 std::map<u16, ServerActiveObject*> &objects)
1327 {
1328         if(id == 0)
1329                 return false;
1330
1331         return objects.find(id) == objects.end();
1332 }
1333
1334 u16 getFreeServerActiveObjectId(
1335                 std::map<u16, ServerActiveObject*> &objects)
1336 {
1337         //try to reuse id's as late as possible
1338         static u16 last_used_id = 0;
1339         u16 startid = last_used_id;
1340         for(;;)
1341         {
1342                 last_used_id ++;
1343                 if(isFreeServerActiveObjectId(last_used_id, objects))
1344                         return last_used_id;
1345                 
1346                 if(last_used_id == startid)
1347                         return 0;
1348         }
1349 }
1350
1351 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1352 {
1353         assert(object);
1354         u16 id = addActiveObjectRaw(object, true, 0);
1355         return id;
1356 }
1357
1358 #if 0
1359 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1360 {
1361         assert(obj);
1362
1363         v3f objectpos = obj->getBasePosition(); 
1364
1365         // The block in which the object resides in
1366         v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1367
1368         /*
1369                 Update the static data
1370         */
1371
1372         // Create new static object
1373         std::string staticdata = obj->getStaticData();
1374         StaticObject s_obj(obj->getType(), objectpos, staticdata);
1375         // Add to the block where the object is located in
1376         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1377         // Get or generate the block
1378         MapBlock *block = m_map->emergeBlock(blockpos);
1379
1380         bool succeeded = false;
1381
1382         if(block)
1383         {
1384                 block->m_static_objects.insert(0, s_obj);
1385                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1386                                 "addActiveObjectAsStatic");
1387                 succeeded = true;
1388         }
1389         else{
1390                 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1391                                 <<"Could not find or generate "
1392                                 <<"a block for storing static object"<<std::endl;
1393                 succeeded = false;
1394         }
1395
1396         if(obj->environmentDeletes())
1397                 delete obj;
1398
1399         return succeeded;
1400 }
1401 #endif
1402
1403 /*
1404         Finds out what new objects have been added to
1405         inside a radius around a position
1406 */
1407 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1408                 std::set<u16> &current_objects,
1409                 std::set<u16> &added_objects)
1410 {
1411         v3f pos_f = intToFloat(pos, BS);
1412         f32 radius_f = radius * BS;
1413         /*
1414                 Go through the object list,
1415                 - discard m_removed objects,
1416                 - discard objects that are too far away,
1417                 - discard objects that are found in current_objects.
1418                 - add remaining objects to added_objects
1419         */
1420         for(std::map<u16, ServerActiveObject*>::iterator
1421                         i = m_active_objects.begin();
1422                         i != m_active_objects.end(); ++i)
1423         {
1424                 u16 id = i->first;
1425                 // Get object
1426                 ServerActiveObject *object = i->second;
1427                 if(object == NULL)
1428                         continue;
1429                 // Discard if removed or deactivating
1430                 if(object->m_removed || object->m_pending_deactivation)
1431                         continue;
1432                 if(object->unlimitedTransferDistance() == false){
1433                         // Discard if too far
1434                         f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1435                         if(distance_f > radius_f)
1436                                 continue;
1437                 }
1438                 // Discard if already on current_objects
1439                 std::set<u16>::iterator n;
1440                 n = current_objects.find(id);
1441                 if(n != current_objects.end())
1442                         continue;
1443                 // Add to added_objects
1444                 added_objects.insert(id);
1445         }
1446 }
1447
1448 /*
1449         Finds out what objects have been removed from
1450         inside a radius around a position
1451 */
1452 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1453                 std::set<u16> &current_objects,
1454                 std::set<u16> &removed_objects)
1455 {
1456         v3f pos_f = intToFloat(pos, BS);
1457         f32 radius_f = radius * BS;
1458         /*
1459                 Go through current_objects; object is removed if:
1460                 - object is not found in m_active_objects (this is actually an
1461                   error condition; objects should be set m_removed=true and removed
1462                   only after all clients have been informed about removal), or
1463                 - object has m_removed=true, or
1464                 - object is too far away
1465         */
1466         for(std::set<u16>::iterator
1467                         i = current_objects.begin();
1468                         i != current_objects.end(); ++i)
1469         {
1470                 u16 id = *i;
1471                 ServerActiveObject *object = getActiveObject(id);
1472
1473                 if(object == NULL){
1474                         infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1475                                         <<" object in current_objects is NULL"<<std::endl;
1476                         removed_objects.insert(id);
1477                         continue;
1478                 }
1479
1480                 if(object->m_removed || object->m_pending_deactivation)
1481                 {
1482                         removed_objects.insert(id);
1483                         continue;
1484                 }
1485                 
1486                 // If transfer distance is unlimited, don't remove
1487                 if(object->unlimitedTransferDistance())
1488                         continue;
1489
1490                 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1491
1492                 if(distance_f >= radius_f)
1493                 {
1494                         removed_objects.insert(id);
1495                         continue;
1496                 }
1497                 
1498                 // Not removed
1499         }
1500 }
1501
1502 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1503 {
1504         if(m_active_object_messages.empty())
1505                 return ActiveObjectMessage(0);
1506         
1507         ActiveObjectMessage message = m_active_object_messages.front();
1508         m_active_object_messages.pop_front();
1509         return message;
1510 }
1511
1512 /*
1513         ************ Private methods *************
1514 */
1515
1516 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1517                 bool set_changed, u32 dtime_s)
1518 {
1519         assert(object);
1520         if(object->getId() == 0){
1521                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1522                 if(new_id == 0)
1523                 {
1524                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1525                                         <<"no free ids available"<<std::endl;
1526                         if(object->environmentDeletes())
1527                                 delete object;
1528                         return 0;
1529                 }
1530                 object->setId(new_id);
1531         }
1532         else{
1533                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1534                                 <<"supplied with id "<<object->getId()<<std::endl;
1535         }
1536         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1537         {
1538                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1539                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1540                 if(object->environmentDeletes())
1541                         delete object;
1542                 return 0;
1543         }
1544         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1545                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1546                         
1547         m_active_objects[object->getId()] = object;
1548   
1549         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1550                         <<"Added id="<<object->getId()<<"; there are now "
1551                         <<m_active_objects.size()<<" active objects."
1552                         <<std::endl;
1553         
1554         // Register reference in scripting api (must be done before post-init)
1555         m_script->addObjectReference(object);
1556         // Post-initialize object
1557         object->addedToEnvironment(dtime_s);
1558         
1559         // Add static data to block
1560         if(object->isStaticAllowed())
1561         {
1562                 // Add static object to active static list of the block
1563                 v3f objectpos = object->getBasePosition();
1564                 std::string staticdata = object->getStaticData();
1565                 StaticObject s_obj(object->getType(), objectpos, staticdata);
1566                 // Add to the block where the object is located in
1567                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1568                 MapBlock *block = m_map->emergeBlock(blockpos);
1569                 if(block){
1570                         block->m_static_objects.m_active[object->getId()] = s_obj;
1571                         object->m_static_exists = true;
1572                         object->m_static_block = blockpos;
1573
1574                         if(set_changed)
1575                                 block->raiseModified(MOD_STATE_WRITE_NEEDED, 
1576                                                 "addActiveObjectRaw");
1577                 } else {
1578                         v3s16 p = floatToInt(objectpos, BS);
1579                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1580                                         <<"could not emerge block for storing id="<<object->getId()
1581                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
1582                 }
1583         }
1584         
1585         return object->getId();
1586 }
1587
1588 /*
1589         Remove objects that satisfy (m_removed && m_known_by_count==0)
1590 */
1591 void ServerEnvironment::removeRemovedObjects()
1592 {
1593         std::list<u16> objects_to_remove;
1594         for(std::map<u16, ServerActiveObject*>::iterator
1595                         i = m_active_objects.begin();
1596                         i != m_active_objects.end(); ++i)
1597         {
1598                 u16 id = i->first;
1599                 ServerActiveObject* obj = i->second;
1600                 // This shouldn't happen but check it
1601                 if(obj == NULL)
1602                 {
1603                         infostream<<"NULL object found in ServerEnvironment"
1604                                         <<" while finding removed objects. id="<<id<<std::endl;
1605                         // Id to be removed from m_active_objects
1606                         objects_to_remove.push_back(id);
1607                         continue;
1608                 }
1609
1610                 /*
1611                         We will delete objects that are marked as removed or thatare
1612                         waiting for deletion after deactivation
1613                 */
1614                 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1615                         continue;
1616
1617                 /*
1618                         Delete static data from block if is marked as removed
1619                 */
1620                 if(obj->m_static_exists && obj->m_removed)
1621                 {
1622                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1623                         if (block) {
1624                                 block->m_static_objects.remove(id);
1625                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1626                                                 "removeRemovedObjects/remove");
1627                                 obj->m_static_exists = false;
1628                         } else {
1629                                 infostream<<"Failed to emerge block from which an object to "
1630                                                 <<"be removed was loaded from. id="<<id<<std::endl;
1631                         }
1632                 }
1633
1634                 // If m_known_by_count > 0, don't actually remove. On some future
1635                 // invocation this will be 0, which is when removal will continue.
1636                 if(obj->m_known_by_count > 0)
1637                         continue;
1638
1639                 /*
1640                         Move static data from active to stored if not marked as removed
1641                 */
1642                 if(obj->m_static_exists && !obj->m_removed){
1643                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1644                         if (block) {
1645                                 std::map<u16, StaticObject>::iterator i =
1646                                                 block->m_static_objects.m_active.find(id);
1647                                 if(i != block->m_static_objects.m_active.end()){
1648                                         block->m_static_objects.m_stored.push_back(i->second);
1649                                         block->m_static_objects.m_active.erase(id);
1650                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1651                                                         "removeRemovedObjects/deactivate");
1652                                 }
1653                         } else {
1654                                 infostream<<"Failed to emerge block from which an object to "
1655                                                 <<"be deactivated was loaded from. id="<<id<<std::endl;
1656                         }
1657                 }
1658
1659                 // Tell the object about removal
1660                 obj->removingFromEnvironment();
1661                 // Deregister in scripting api
1662                 m_script->removeObjectReference(obj);
1663
1664                 // Delete
1665                 if(obj->environmentDeletes())
1666                         delete obj;
1667                 // Id to be removed from m_active_objects
1668                 objects_to_remove.push_back(id);
1669         }
1670         // Remove references from m_active_objects
1671         for(std::list<u16>::iterator i = objects_to_remove.begin();
1672                         i != objects_to_remove.end(); ++i)
1673         {
1674                 m_active_objects.erase(*i);
1675         }
1676 }
1677
1678 static void print_hexdump(std::ostream &o, const std::string &data)
1679 {
1680         const int linelength = 16;
1681         for(int l=0; ; l++){
1682                 int i0 = linelength * l;
1683                 bool at_end = false;
1684                 int thislinelength = linelength;
1685                 if(i0 + thislinelength > (int)data.size()){
1686                         thislinelength = data.size() - i0;
1687                         at_end = true;
1688                 }
1689                 for(int di=0; di<linelength; di++){
1690                         int i = i0 + di;
1691                         char buf[4];
1692                         if(di<thislinelength)
1693                                 snprintf(buf, 4, "%.2x ", data[i]);
1694                         else
1695                                 snprintf(buf, 4, "   ");
1696                         o<<buf;
1697                 }
1698                 o<<" ";
1699                 for(int di=0; di<thislinelength; di++){
1700                         int i = i0 + di;
1701                         if(data[i] >= 32)
1702                                 o<<data[i];
1703                         else
1704                                 o<<".";
1705                 }
1706                 o<<std::endl;
1707                 if(at_end)
1708                         break;
1709         }
1710 }
1711
1712 /*
1713         Convert stored objects from blocks near the players to active.
1714 */
1715 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1716 {
1717         if(block==NULL)
1718                 return;
1719         // Ignore if no stored objects (to not set changed flag)
1720         if(block->m_static_objects.m_stored.size() == 0)
1721                 return;
1722         verbosestream<<"ServerEnvironment::activateObjects(): "
1723                         <<"activating objects of block "<<PP(block->getPos())
1724                         <<" ("<<block->m_static_objects.m_stored.size()
1725                         <<" objects)"<<std::endl;
1726         bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1727         if(large_amount){
1728                 errorstream<<"suspiciously large amount of objects detected: "
1729                                 <<block->m_static_objects.m_stored.size()<<" in "
1730                                 <<PP(block->getPos())
1731                                 <<"; removing all of them."<<std::endl;
1732                 // Clear stored list
1733                 block->m_static_objects.m_stored.clear();
1734                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1735                                 "stored list cleared in activateObjects due to "
1736                                 "large amount of objects");
1737                 return;
1738         }
1739
1740         // Activate stored objects
1741         std::list<StaticObject> new_stored;
1742         for(std::list<StaticObject>::iterator
1743                         i = block->m_static_objects.m_stored.begin();
1744                         i != block->m_static_objects.m_stored.end(); ++i)
1745         {
1746                 /*infostream<<"Server: Creating an active object from "
1747                                 <<"static data"<<std::endl;*/
1748                 StaticObject &s_obj = *i;
1749                 // Create an active object from the data
1750                 ServerActiveObject *obj = ServerActiveObject::create
1751                                 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1752                 // If couldn't create object, store static data back.
1753                 if(obj==NULL)
1754                 {
1755                         errorstream<<"ServerEnvironment::activateObjects(): "
1756                                         <<"failed to create active object from static object "
1757                                         <<"in block "<<PP(s_obj.pos/BS)
1758                                         <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1759                         print_hexdump(verbosestream, s_obj.data);
1760                         
1761                         new_stored.push_back(s_obj);
1762                         continue;
1763                 }
1764                 verbosestream<<"ServerEnvironment::activateObjects(): "
1765                                 <<"activated static object pos="<<PP(s_obj.pos/BS)
1766                                 <<" type="<<(int)s_obj.type<<std::endl;
1767                 // This will also add the object to the active static list
1768                 addActiveObjectRaw(obj, false, dtime_s);
1769         }
1770         // Clear stored list
1771         block->m_static_objects.m_stored.clear();
1772         // Add leftover failed stuff to stored list
1773         for(std::list<StaticObject>::iterator
1774                         i = new_stored.begin();
1775                         i != new_stored.end(); ++i)
1776         {
1777                 StaticObject &s_obj = *i;
1778                 block->m_static_objects.m_stored.push_back(s_obj);
1779         }
1780
1781         // Turn the active counterparts of activated objects not pending for
1782         // deactivation
1783         for(std::map<u16, StaticObject>::iterator
1784                         i = block->m_static_objects.m_active.begin();
1785                         i != block->m_static_objects.m_active.end(); ++i)
1786         {
1787                 u16 id = i->first;
1788                 ServerActiveObject *object = getActiveObject(id);
1789                 assert(object);
1790                 object->m_pending_deactivation = false;
1791         }
1792
1793         /*
1794                 Note: Block hasn't really been modified here.
1795                 The objects have just been activated and moved from the stored
1796                 static list to the active static list.
1797                 As such, the block is essentially the same.
1798                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1799                 Otherwise there would be a huge amount of unnecessary I/O.
1800         */
1801 }
1802
1803 /*
1804         Convert objects that are not standing inside active blocks to static.
1805
1806         If m_known_by_count != 0, active object is not deleted, but static
1807         data is still updated.
1808
1809         If force_delete is set, active object is deleted nevertheless. It
1810         shall only be set so in the destructor of the environment.
1811
1812         If block wasn't generated (not in memory or on disk), 
1813 */
1814 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1815 {
1816         std::list<u16> objects_to_remove;
1817         for(std::map<u16, ServerActiveObject*>::iterator
1818                         i = m_active_objects.begin();
1819                         i != m_active_objects.end(); ++i)
1820         {
1821                 ServerActiveObject* obj = i->second;
1822                 assert(obj);
1823                 
1824                 // Do not deactivate if static data creation not allowed
1825                 if(!force_delete && !obj->isStaticAllowed())
1826                         continue;
1827
1828                 // If pending deactivation, let removeRemovedObjects() do it
1829                 if(!force_delete && obj->m_pending_deactivation)
1830                         continue;
1831
1832                 u16 id = i->first;
1833                 v3f objectpos = obj->getBasePosition(); 
1834
1835                 // The block in which the object resides in
1836                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1837
1838                 // If object's static data is stored in a deactivated block and object
1839                 // is actually located in an active block, re-save to the block in
1840                 // which the object is actually located in.
1841                 if(!force_delete &&
1842                                 obj->m_static_exists &&
1843                                 !m_active_blocks.contains(obj->m_static_block) &&
1844                                  m_active_blocks.contains(blockpos_o))
1845                 {
1846                         v3s16 old_static_block = obj->m_static_block;
1847
1848                         // Save to block where object is located
1849                         MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1850                         if(!block){
1851                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1852                                                 <<"Could not save object id="<<id
1853                                                 <<" to it's current block "<<PP(blockpos_o)
1854                                                 <<std::endl;
1855                                 continue;
1856                         }
1857                         std::string staticdata_new = obj->getStaticData();
1858                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1859                         block->m_static_objects.insert(id, s_obj);
1860                         obj->m_static_block = blockpos_o;
1861                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1862                                         "deactivateFarObjects: Static data moved in");
1863
1864                         // Delete from block where object was located
1865                         block = m_map->emergeBlock(old_static_block, false);
1866                         if(!block){
1867                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1868                                                 <<"Could not delete object id="<<id
1869                                                 <<" from it's previous block "<<PP(old_static_block)
1870                                                 <<std::endl;
1871                                 continue;
1872                         }
1873                         block->m_static_objects.remove(id);
1874                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1875                                         "deactivateFarObjects: Static data moved out");
1876                         continue;
1877                 }
1878
1879                 // If block is active, don't remove
1880                 if(!force_delete && m_active_blocks.contains(blockpos_o))
1881                         continue;
1882
1883                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1884                                 <<"deactivating object id="<<id<<" on inactive block "
1885                                 <<PP(blockpos_o)<<std::endl;
1886
1887                 // If known by some client, don't immediately delete.
1888                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1889
1890                 /*
1891                         Update the static data
1892                 */
1893
1894                 if(obj->isStaticAllowed())
1895                 {
1896                         // Create new static object
1897                         std::string staticdata_new = obj->getStaticData();
1898                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1899                         
1900                         bool stays_in_same_block = false;
1901                         bool data_changed = true;
1902
1903                         if(obj->m_static_exists){
1904                                 if(obj->m_static_block == blockpos_o)
1905                                         stays_in_same_block = true;
1906
1907                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1908                                 
1909                                 std::map<u16, StaticObject>::iterator n =
1910                                                 block->m_static_objects.m_active.find(id);
1911                                 if(n != block->m_static_objects.m_active.end()){
1912                                         StaticObject static_old = n->second;
1913
1914                                         float save_movem = obj->getMinimumSavedMovement();
1915
1916                                         if(static_old.data == staticdata_new &&
1917                                                         (static_old.pos - objectpos).getLength() < save_movem)
1918                                                 data_changed = false;
1919                                 } else {
1920                                         errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1921                                                         <<"id="<<id<<" m_static_exists=true but "
1922                                                         <<"static data doesn't actually exist in "
1923                                                         <<PP(obj->m_static_block)<<std::endl;
1924                                 }
1925                         }
1926
1927                         bool shall_be_written = (!stays_in_same_block || data_changed);
1928                         
1929                         // Delete old static object
1930                         if(obj->m_static_exists)
1931                         {
1932                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1933                                 if(block)
1934                                 {
1935                                         block->m_static_objects.remove(id);
1936                                         obj->m_static_exists = false;
1937                                         // Only mark block as modified if data changed considerably
1938                                         if(shall_be_written)
1939                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1940                                                                 "deactivateFarObjects: Static data "
1941                                                                 "changed considerably");
1942                                 }
1943                         }
1944
1945                         // Add to the block where the object is located in
1946                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1947                         // Get or generate the block
1948                         MapBlock *block = NULL;
1949                         try{
1950                                 block = m_map->emergeBlock(blockpos);
1951                         } catch(InvalidPositionException &e){
1952                                 // Handled via NULL pointer
1953                                 // NOTE: emergeBlock's failure is usually determined by it
1954                                 //       actually returning NULL
1955                         }
1956
1957                         if(block)
1958                         {
1959                                 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1960                                         errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1961                                                         <<" statically but block "<<PP(blockpos)
1962                                                         <<" already contains "
1963                                                         <<block->m_static_objects.m_stored.size()
1964                                                         <<" objects."
1965                                                         <<" Forcing delete."<<std::endl;
1966                                         force_delete = true;
1967                                 } else {
1968                                         // If static counterpart already exists in target block,
1969                                         // remove it first.
1970                                         // This shouldn't happen because the object is removed from
1971                                         // the previous block before this according to
1972                                         // obj->m_static_block, but happens rarely for some unknown
1973                                         // reason. Unsuccessful attempts have been made to find
1974                                         // said reason.
1975                                         if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1976                                                 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1977                                                                 <<std::endl;
1978                                                 block->m_static_objects.remove(id);
1979                                         }
1980                                         // Store static data
1981                                         u16 store_id = pending_delete ? id : 0;
1982                                         block->m_static_objects.insert(store_id, s_obj);
1983                                         
1984                                         // Only mark block as modified if data changed considerably
1985                                         if(shall_be_written)
1986                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1987                                                                 "deactivateFarObjects: Static data "
1988                                                                 "changed considerably");
1989                                         
1990                                         obj->m_static_exists = true;
1991                                         obj->m_static_block = block->getPos();
1992                                 }
1993                         }
1994                         else{
1995                                 if(!force_delete){
1996                                         v3s16 p = floatToInt(objectpos, BS);
1997                                         errorstream<<"ServerEnv: Could not find or generate "
1998                                                         <<"a block for storing id="<<obj->getId()
1999                                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
2000                                         continue;
2001                                 }
2002                         }
2003                 }
2004
2005                 /*
2006                         If known by some client, set pending deactivation.
2007                         Otherwise delete it immediately.
2008                 */
2009
2010                 if(pending_delete && !force_delete)
2011                 {
2012                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2013                                         <<"object id="<<id<<" is known by clients"
2014                                         <<"; not deleting yet"<<std::endl;
2015
2016                         obj->m_pending_deactivation = true;
2017                         continue;
2018                 }
2019                 
2020                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2021                                 <<"object id="<<id<<" is not known by clients"
2022                                 <<"; deleting"<<std::endl;
2023
2024                 // Tell the object about removal
2025                 obj->removingFromEnvironment();
2026                 // Deregister in scripting api
2027                 m_script->removeObjectReference(obj);
2028
2029                 // Delete active object
2030                 if(obj->environmentDeletes())
2031                         delete obj;
2032                 // Id to be removed from m_active_objects
2033                 objects_to_remove.push_back(id);
2034         }
2035
2036         // Remove references from m_active_objects
2037         for(std::list<u16>::iterator i = objects_to_remove.begin();
2038                         i != objects_to_remove.end(); ++i)
2039         {
2040                 m_active_objects.erase(*i);
2041         }
2042 }
2043
2044
2045 #ifndef SERVER
2046
2047 #include "clientsimpleobject.h"
2048
2049 /*
2050         ClientEnvironment
2051 */
2052
2053 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2054                 ITextureSource *texturesource, IGameDef *gamedef,
2055                 IrrlichtDevice *irr):
2056         m_map(map),
2057         m_smgr(smgr),
2058         m_texturesource(texturesource),
2059         m_gamedef(gamedef),
2060         m_irr(irr)
2061 {
2062 }
2063
2064 ClientEnvironment::~ClientEnvironment()
2065 {
2066         // delete active objects
2067         for(std::map<u16, ClientActiveObject*>::iterator
2068                         i = m_active_objects.begin();
2069                         i != m_active_objects.end(); ++i)
2070         {
2071                 delete i->second;
2072         }
2073
2074         for(std::list<ClientSimpleObject*>::iterator
2075                         i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2076         {
2077                 delete *i;
2078         }
2079
2080         // Drop/delete map
2081         m_map->drop();
2082 }
2083
2084 Map & ClientEnvironment::getMap()
2085 {
2086         return *m_map;
2087 }
2088
2089 ClientMap & ClientEnvironment::getClientMap()
2090 {
2091         return *m_map;
2092 }
2093
2094 void ClientEnvironment::addPlayer(Player *player)
2095 {
2096         DSTACK(__FUNCTION_NAME);
2097         /*
2098                 It is a failure if player is local and there already is a local
2099                 player
2100         */
2101         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2102
2103         Environment::addPlayer(player);
2104 }
2105
2106 LocalPlayer * ClientEnvironment::getLocalPlayer()
2107 {
2108         for(std::list<Player*>::iterator i = m_players.begin();
2109                         i != m_players.end(); ++i)
2110         {
2111                 Player *player = *i;
2112                 if(player->isLocal())
2113                         return (LocalPlayer*)player;
2114         }
2115         return NULL;
2116 }
2117
2118 void ClientEnvironment::step(float dtime)
2119 {
2120         DSTACK(__FUNCTION_NAME);
2121
2122         /* Step time of day */
2123         stepTimeOfDay(dtime);
2124
2125         // Get some settings
2126         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2127         bool free_move = fly_allowed && g_settings->getBool("free_move");
2128
2129         // Get local player
2130         LocalPlayer *lplayer = getLocalPlayer();
2131         assert(lplayer);
2132         // collision info queue
2133         std::list<CollisionInfo> player_collisions;
2134         
2135         /*
2136                 Get the speed the player is going
2137         */
2138         bool is_climbing = lplayer->is_climbing;
2139         
2140         f32 player_speed = lplayer->getSpeed().getLength();
2141         
2142         /*
2143                 Maximum position increment
2144         */
2145         //f32 position_max_increment = 0.05*BS;
2146         f32 position_max_increment = 0.1*BS;
2147
2148         // Maximum time increment (for collision detection etc)
2149         // time = distance / speed
2150         f32 dtime_max_increment = 1;
2151         if(player_speed > 0.001)
2152                 dtime_max_increment = position_max_increment / player_speed;
2153         
2154         // Maximum time increment is 10ms or lower
2155         if(dtime_max_increment > 0.01)
2156                 dtime_max_increment = 0.01;
2157         
2158         // Don't allow overly huge dtime
2159         if(dtime > 0.5)
2160                 dtime = 0.5;
2161         
2162         f32 dtime_downcount = dtime;
2163
2164         /*
2165                 Stuff that has a maximum time increment
2166         */
2167
2168         u32 loopcount = 0;
2169         do
2170         {
2171                 loopcount++;
2172
2173                 f32 dtime_part;
2174                 if(dtime_downcount > dtime_max_increment)
2175                 {
2176                         dtime_part = dtime_max_increment;
2177                         dtime_downcount -= dtime_part;
2178                 }
2179                 else
2180                 {
2181                         dtime_part = dtime_downcount;
2182                         /*
2183                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
2184                                 when dtime_part is so small that dtime_downcount -= dtime_part
2185                                 does nothing
2186                         */
2187                         dtime_downcount = 0;
2188                 }
2189                 
2190                 /*
2191                         Handle local player
2192                 */
2193                 
2194                 {
2195                         // Apply physics
2196                         if(free_move == false && is_climbing == false)
2197                         {
2198                                 // Gravity
2199                                 v3f speed = lplayer->getSpeed();
2200                                 if(lplayer->in_liquid == false)
2201                                         speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2202
2203                                 // Liquid floating / sinking
2204                                 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2205                                         speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2206
2207                                 // Liquid resistance
2208                                 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2209                                 {
2210                                         // How much the node's viscosity blocks movement, ranges between 0 and 1
2211                                         // Should match the scale at which viscosity increase affects other liquid attributes
2212                                         const f32 viscosity_factor = 0.3;
2213
2214                                         v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2215                                         f32 dl = d_wanted.getLength();
2216                                         if(dl > lplayer->movement_liquid_fluidity_smooth)
2217                                                 dl = lplayer->movement_liquid_fluidity_smooth;
2218                                         dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2219                                         
2220                                         v3f d = d_wanted.normalize() * dl;
2221                                         speed += d;
2222                                         
2223 #if 0 // old code
2224                                         if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth)      speed.X -= lplayer->movement_liquid_fluidity_smooth;
2225                                         if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth)     speed.X += 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.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth)     speed.Y += 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                                         if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth)     speed.Z += lplayer->movement_liquid_fluidity_smooth;
2230 #endif
2231                                 }
2232
2233                                 lplayer->setSpeed(speed);
2234                         }
2235
2236                         /*
2237                                 Move the lplayer.
2238                                 This also does collision detection.
2239                         */
2240                         lplayer->move(dtime_part, this, position_max_increment,
2241                                         &player_collisions);
2242                 }
2243         }
2244         while(dtime_downcount > 0.001);
2245                 
2246         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2247         
2248         for(std::list<CollisionInfo>::iterator
2249                         i = player_collisions.begin();
2250                         i != player_collisions.end(); ++i)
2251         {
2252                 CollisionInfo &info = *i;
2253                 v3f speed_diff = info.new_speed - info.old_speed;;
2254                 // Handle only fall damage
2255                 // (because otherwise walking against something in fast_move kills you)
2256                 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2257                         continue;
2258                 // Get rid of other components
2259                 speed_diff.X = 0;
2260                 speed_diff.Z = 0;
2261                 f32 pre_factor = 1; // 1 hp per node/s
2262                 f32 tolerance = BS*14; // 5 without damage
2263                 f32 post_factor = 1; // 1 hp per node/s
2264                 if(info.type == COLLISION_NODE)
2265                 {
2266                         const ContentFeatures &f = m_gamedef->ndef()->
2267                                         get(m_map->getNodeNoEx(info.node_p));
2268                         // Determine fall damage multiplier
2269                         int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2270                         pre_factor = 1.0 + (float)addp/100.0;
2271                 }
2272                 float speed = pre_factor * speed_diff.getLength();
2273                 if(speed > tolerance)
2274                 {
2275                         f32 damage_f = (speed - tolerance)/BS * post_factor;
2276                         u16 damage = (u16)(damage_f+0.5);
2277                         if(damage != 0){
2278                                 damageLocalPlayer(damage, true);
2279                                 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2280                                 m_gamedef->event()->put(e);
2281                         }
2282                 }
2283         }
2284         
2285         /*
2286                 A quick draft of lava damage
2287         */
2288         if(m_lava_hurt_interval.step(dtime, 1.0))
2289         {
2290                 v3f pf = lplayer->getPosition();
2291                 
2292                 // Feet, middle and head
2293                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2294                 MapNode n1 = m_map->getNodeNoEx(p1);
2295                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2296                 MapNode n2 = m_map->getNodeNoEx(p2);
2297                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2298                 MapNode n3 = m_map->getNodeNoEx(p3);
2299
2300                 u32 damage_per_second = 0;
2301                 damage_per_second = MYMAX(damage_per_second,
2302                                 m_gamedef->ndef()->get(n1).damage_per_second);
2303                 damage_per_second = MYMAX(damage_per_second,
2304                                 m_gamedef->ndef()->get(n2).damage_per_second);
2305                 damage_per_second = MYMAX(damage_per_second,
2306                                 m_gamedef->ndef()->get(n3).damage_per_second);
2307                 
2308                 if(damage_per_second != 0)
2309                 {
2310                         damageLocalPlayer(damage_per_second, true);
2311                 }
2312         }
2313
2314         /*
2315                 Drowning
2316         */
2317         if(m_drowning_interval.step(dtime, 2.0))
2318         {
2319                 v3f pf = lplayer->getPosition();
2320
2321                 // head
2322                 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2323                 MapNode n = m_map->getNodeNoEx(p);
2324                 ContentFeatures c = m_gamedef->ndef()->get(n);
2325                 u8 drowning_damage = c.drowning;
2326                 if(drowning_damage > 0 && lplayer->hp > 0){
2327                         u16 breath = lplayer->getBreath();
2328                         if(breath > 10){
2329                                 breath = 11;
2330                         }
2331                         if(breath > 0){
2332                                 breath -= 1;
2333                         }
2334                         lplayer->setBreath(breath);
2335                         updateLocalPlayerBreath(breath);
2336                 }
2337
2338                 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2339                         damageLocalPlayer(drowning_damage, true);
2340                 }
2341         }
2342         if(m_breathing_interval.step(dtime, 0.5))
2343         {
2344                 v3f pf = lplayer->getPosition();
2345
2346                 // head
2347                 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2348                 MapNode n = m_map->getNodeNoEx(p);
2349                 ContentFeatures c = m_gamedef->ndef()->get(n);
2350                 if (!lplayer->hp){
2351                         lplayer->setBreath(11);
2352                 }
2353                 else if(c.drowning == 0){
2354                         u16 breath = lplayer->getBreath();
2355                         if(breath <= 10){
2356                                 breath += 1;
2357                                 lplayer->setBreath(breath);
2358                                 updateLocalPlayerBreath(breath);
2359                         }
2360                 }
2361         }
2362
2363         /*
2364                 Stuff that can be done in an arbitarily large dtime
2365         */
2366         for(std::list<Player*>::iterator i = m_players.begin();
2367                         i != m_players.end(); ++i)
2368         {
2369                 Player *player = *i;
2370                 
2371                 /*
2372                         Handle non-local players
2373                 */
2374                 if(player->isLocal() == false)
2375                 {
2376                         // Move
2377                         player->move(dtime, *m_map, 100*BS);
2378
2379                 }
2380                 
2381                 // Update lighting on all players on client
2382                 float light = 1.0;
2383                 try{
2384                         // Get node at head
2385                         v3s16 p = player->getLightPosition();
2386                         MapNode n = m_map->getNode(p);
2387                         light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2388                 }
2389                 catch(InvalidPositionException &e){
2390                         light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2391                 }
2392                 player->light = light;
2393         }
2394         
2395         /*
2396                 Step active objects and update lighting of them
2397         */
2398         
2399         g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2400         bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2401         for(std::map<u16, ClientActiveObject*>::iterator
2402                         i = m_active_objects.begin();
2403                         i != m_active_objects.end(); ++i)
2404         {
2405                 ClientActiveObject* obj = i->second;
2406                 // Step object
2407                 obj->step(dtime, this);
2408
2409                 if(update_lighting)
2410                 {
2411                         // Update lighting
2412                         u8 light = 0;
2413                         try{
2414                                 // Get node at head
2415                                 v3s16 p = obj->getLightPosition();
2416                                 MapNode n = m_map->getNode(p);
2417                                 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2418                         }
2419                         catch(InvalidPositionException &e){
2420                                 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2421                         }
2422                         obj->updateLight(light);
2423                 }
2424         }
2425
2426         /*
2427                 Step and handle simple objects
2428         */
2429         g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2430         for(std::list<ClientSimpleObject*>::iterator
2431                         i = m_simple_objects.begin(); i != m_simple_objects.end();)
2432         {
2433                 ClientSimpleObject *simple = *i;
2434                 std::list<ClientSimpleObject*>::iterator cur = i;
2435                 ++i;
2436                 simple->step(dtime);
2437                 if(simple->m_to_be_removed){
2438                         delete simple;
2439                         m_simple_objects.erase(cur);
2440                 }
2441         }
2442 }
2443         
2444 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2445 {
2446         m_simple_objects.push_back(simple);
2447 }
2448
2449 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2450 {
2451         std::map<u16, ClientActiveObject*>::iterator n;
2452         n = m_active_objects.find(id);
2453         if(n == m_active_objects.end())
2454                 return NULL;
2455         return n->second;
2456 }
2457
2458 bool isFreeClientActiveObjectId(u16 id,
2459                 std::map<u16, ClientActiveObject*> &objects)
2460 {
2461         if(id == 0)
2462                 return false;
2463
2464         return objects.find(id) == objects.end();
2465 }
2466
2467 u16 getFreeClientActiveObjectId(
2468                 std::map<u16, ClientActiveObject*> &objects)
2469 {
2470         //try to reuse id's as late as possible
2471         static u16 last_used_id = 0;
2472         u16 startid = last_used_id;
2473         for(;;)
2474         {
2475                 last_used_id ++;
2476                 if(isFreeClientActiveObjectId(last_used_id, objects))
2477                         return last_used_id;
2478                 
2479                 if(last_used_id == startid)
2480                         return 0;
2481         }
2482 }
2483
2484 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2485 {
2486         assert(object);
2487         if(object->getId() == 0)
2488         {
2489                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2490                 if(new_id == 0)
2491                 {
2492                         infostream<<"ClientEnvironment::addActiveObject(): "
2493                                         <<"no free ids available"<<std::endl;
2494                         delete object;
2495                         return 0;
2496                 }
2497                 object->setId(new_id);
2498         }
2499         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2500         {
2501                 infostream<<"ClientEnvironment::addActiveObject(): "
2502                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2503                 delete object;
2504                 return 0;
2505         }
2506         infostream<<"ClientEnvironment::addActiveObject(): "
2507                         <<"added (id="<<object->getId()<<")"<<std::endl;
2508         m_active_objects[object->getId()] = object;
2509         object->addToScene(m_smgr, m_texturesource, m_irr);
2510         { // Update lighting immediately
2511                 u8 light = 0;
2512                 try{
2513                         // Get node at head
2514                         v3s16 p = object->getLightPosition();
2515                         MapNode n = m_map->getNode(p);
2516                         light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2517                 }
2518                 catch(InvalidPositionException &e){
2519                         light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2520                 }
2521                 object->updateLight(light);
2522         }
2523         return object->getId();
2524 }
2525
2526 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2527                 const std::string &init_data)
2528 {
2529         ClientActiveObject* obj =
2530                         ClientActiveObject::create(type, m_gamedef, this);
2531         if(obj == NULL)
2532         {
2533                 infostream<<"ClientEnvironment::addActiveObject(): "
2534                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2535                                 <<std::endl;
2536                 return;
2537         }
2538         
2539         obj->setId(id);
2540
2541         try
2542         {
2543                 obj->initialize(init_data);
2544         }
2545         catch(SerializationError &e)
2546         {
2547                 errorstream<<"ClientEnvironment::addActiveObject():"
2548                                 <<" id="<<id<<" type="<<type
2549                                 <<": SerializationError in initialize(): "
2550                                 <<e.what()
2551                                 <<": init_data="<<serializeJsonString(init_data)
2552                                 <<std::endl;
2553         }
2554
2555         addActiveObject(obj);
2556 }
2557
2558 void ClientEnvironment::removeActiveObject(u16 id)
2559 {
2560         verbosestream<<"ClientEnvironment::removeActiveObject(): "
2561                         <<"id="<<id<<std::endl;
2562         ClientActiveObject* obj = getActiveObject(id);
2563         if(obj == NULL)
2564         {
2565                 infostream<<"ClientEnvironment::removeActiveObject(): "
2566                                 <<"id="<<id<<" not found"<<std::endl;
2567                 return;
2568         }
2569         obj->removeFromScene(true);
2570         delete obj;
2571         m_active_objects.erase(id);
2572 }
2573
2574 void ClientEnvironment::processActiveObjectMessage(u16 id,
2575                 const std::string &data)
2576 {
2577         ClientActiveObject* obj = getActiveObject(id);
2578         if(obj == NULL)
2579         {
2580                 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2581                                 <<" got message for id="<<id<<", which doesn't exist."
2582                                 <<std::endl;
2583                 return;
2584         }
2585         try
2586         {
2587                 obj->processMessage(data);
2588         }
2589         catch(SerializationError &e)
2590         {
2591                 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2592                                 <<" id="<<id<<" type="<<obj->getType()
2593                                 <<" SerializationError in processMessage(),"
2594                                 <<" message="<<serializeJsonString(data)
2595                                 <<std::endl;
2596         }
2597 }
2598
2599 /*
2600         Callbacks for activeobjects
2601 */
2602
2603 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2604 {
2605         LocalPlayer *lplayer = getLocalPlayer();
2606         assert(lplayer);
2607         
2608         if(handle_hp){
2609                 if(lplayer->hp > damage)
2610                         lplayer->hp -= damage;
2611                 else
2612                         lplayer->hp = 0;
2613         }
2614
2615         ClientEnvEvent event;
2616         event.type = CEE_PLAYER_DAMAGE;
2617         event.player_damage.amount = damage;
2618         event.player_damage.send_to_server = handle_hp;
2619         m_client_event_queue.push_back(event);
2620 }
2621
2622 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2623 {
2624         ClientEnvEvent event;
2625         event.type = CEE_PLAYER_BREATH;
2626         event.player_breath.amount = breath;
2627         m_client_event_queue.push_back(event);
2628 }
2629
2630 /*
2631         Client likes to call these
2632 */
2633         
2634 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2635                 std::vector<DistanceSortedActiveObject> &dest)
2636 {
2637         for(std::map<u16, ClientActiveObject*>::iterator
2638                         i = m_active_objects.begin();
2639                         i != m_active_objects.end(); ++i)
2640         {
2641                 ClientActiveObject* obj = i->second;
2642
2643                 f32 d = (obj->getPosition() - origin).getLength();
2644
2645                 if(d > max_d)
2646                         continue;
2647
2648                 DistanceSortedActiveObject dso(obj, d);
2649
2650                 dest.push_back(dso);
2651         }
2652 }
2653
2654 ClientEnvEvent ClientEnvironment::getClientEvent()
2655 {
2656         ClientEnvEvent event;
2657         if(m_client_event_queue.empty())
2658                 event.type = CEE_NONE;
2659         else {
2660                 event = m_client_event_queue.front();
2661                 m_client_event_queue.pop_front();
2662         }
2663         return event;
2664 }
2665
2666 #endif // #ifndef SERVER
2667
2668