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