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