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