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