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