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