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