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