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