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