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