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