]> git.lizzy.rs Git - minetest.git/blob - src/environment.cpp
GameDef compiles
[minetest.git] / src / environment.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "mapgen.h"
29 #include "settings.h"
30 #include "log.h"
31 #include "profiler.h"
32 #include "scriptapi.h"
33 #include "nodedef.h"
34 #include "nodemetadata.h"
35 #include "main.h" // For g_settings, g_profiler
36 #include "gamedef.h"
37
38 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
39
40 Environment::Environment():
41         m_time_of_day(9000)
42 {
43 }
44
45 Environment::~Environment()
46 {
47         // Deallocate players
48         for(core::list<Player*>::Iterator i = m_players.begin();
49                         i != m_players.end(); i++)
50         {
51                 delete (*i);
52         }
53 }
54
55 void Environment::addPlayer(Player *player)
56 {
57         DSTACK(__FUNCTION_NAME);
58         /*
59                 Check that peer_ids are unique.
60                 Also check that names are unique.
61                 Exception: there can be multiple players with peer_id=0
62         */
63         // If peer id is non-zero, it has to be unique.
64         if(player->peer_id != 0)
65                 assert(getPlayer(player->peer_id) == NULL);
66         // Name has to be unique.
67         assert(getPlayer(player->getName()) == NULL);
68         // Add.
69         m_players.push_back(player);
70 }
71
72 void Environment::removePlayer(u16 peer_id)
73 {
74         DSTACK(__FUNCTION_NAME);
75 re_search:
76         for(core::list<Player*>::Iterator i = m_players.begin();
77                         i != m_players.end(); i++)
78         {
79                 Player *player = *i;
80                 if(player->peer_id != peer_id)
81                         continue;
82                 
83                 delete player;
84                 m_players.erase(i);
85                 // See if there is an another one
86                 // (shouldn't be, but just to be sure)
87                 goto re_search;
88         }
89 }
90
91 Player * Environment::getPlayer(u16 peer_id)
92 {
93         for(core::list<Player*>::Iterator i = m_players.begin();
94                         i != m_players.end(); i++)
95         {
96                 Player *player = *i;
97                 if(player->peer_id == peer_id)
98                         return player;
99         }
100         return NULL;
101 }
102
103 Player * Environment::getPlayer(const char *name)
104 {
105         for(core::list<Player*>::Iterator i = m_players.begin();
106                         i != m_players.end(); i++)
107         {
108                 Player *player = *i;
109                 if(strcmp(player->getName(), name) == 0)
110                         return player;
111         }
112         return NULL;
113 }
114
115 Player * Environment::getRandomConnectedPlayer()
116 {
117         core::list<Player*> connected_players = getPlayers(true);
118         u32 chosen_one = myrand() % connected_players.size();
119         u32 j = 0;
120         for(core::list<Player*>::Iterator
121                         i = connected_players.begin();
122                         i != connected_players.end(); i++)
123         {
124                 if(j == chosen_one)
125                 {
126                         Player *player = *i;
127                         return player;
128                 }
129                 j++;
130         }
131         return NULL;
132 }
133
134 Player * Environment::getNearestConnectedPlayer(v3f pos)
135 {
136         core::list<Player*> connected_players = getPlayers(true);
137         f32 nearest_d = 0;
138         Player *nearest_player = NULL;
139         for(core::list<Player*>::Iterator
140                         i = connected_players.begin();
141                         i != connected_players.end(); i++)
142         {
143                 Player *player = *i;
144                 f32 d = player->getPosition().getDistanceFrom(pos);
145                 if(d < nearest_d || nearest_player == NULL)
146                 {
147                         nearest_d = d;
148                         nearest_player = player;
149                 }
150         }
151         return nearest_player;
152 }
153
154 core::list<Player*> Environment::getPlayers()
155 {
156         return m_players;
157 }
158
159 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
160 {
161         core::list<Player*> newlist;
162         for(core::list<Player*>::Iterator
163                         i = m_players.begin();
164                         i != m_players.end(); i++)
165         {
166                 Player *player = *i;
167                 
168                 if(ignore_disconnected)
169                 {
170                         // Ignore disconnected players
171                         if(player->peer_id == 0)
172                                 continue;
173                 }
174
175                 newlist.push_back(player);
176         }
177         return newlist;
178 }
179
180 void Environment::printPlayers(std::ostream &o)
181 {
182         o<<"Players in environment:"<<std::endl;
183         for(core::list<Player*>::Iterator i = m_players.begin();
184                         i != m_players.end(); i++)
185         {
186                 Player *player = *i;
187                 o<<"Player peer_id="<<player->peer_id<<std::endl;
188         }
189 }
190
191 /*void Environment::setDayNightRatio(u32 r)
192 {
193         getDayNightRatio() = r;
194 }*/
195
196 u32 Environment::getDayNightRatio()
197 {
198         //return getDayNightRatio();
199         return time_to_daynight_ratio(m_time_of_day);
200 }
201
202 /*
203         ActiveBlockList
204 */
205
206 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
207 {
208         v3s16 p;
209         for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
210         for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
211         for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
212         {
213                 // Set in list
214                 list[p] = true;
215         }
216 }
217
218 void ActiveBlockList::update(core::list<v3s16> &active_positions,
219                 s16 radius,
220                 core::map<v3s16, bool> &blocks_removed,
221                 core::map<v3s16, bool> &blocks_added)
222 {
223         /*
224                 Create the new list
225         */
226         core::map<v3s16, bool> newlist;
227         for(core::list<v3s16>::Iterator i = active_positions.begin();
228                         i != active_positions.end(); i++)
229         {
230                 fillRadiusBlock(*i, radius, newlist);
231         }
232
233         /*
234                 Find out which blocks on the old list are not on the new list
235         */
236         // Go through old list
237         for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
238                         i.atEnd()==false; i++)
239         {
240                 v3s16 p = i.getNode()->getKey();
241                 // If not on new list, it's been removed
242                 if(newlist.find(p) == NULL)
243                         blocks_removed.insert(p, true);
244         }
245
246         /*
247                 Find out which blocks on the new list are not on the old list
248         */
249         // Go through new list
250         for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
251                         i.atEnd()==false; i++)
252         {
253                 v3s16 p = i.getNode()->getKey();
254                 // If not on old list, it's been added
255                 if(m_list.find(p) == NULL)
256                         blocks_added.insert(p, true);
257         }
258
259         /*
260                 Update m_list
261         */
262         m_list.clear();
263         for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
264                         i.atEnd()==false; i++)
265         {
266                 v3s16 p = i.getNode()->getKey();
267                 m_list.insert(p, true);
268         }
269 }
270
271 /*
272         ServerEnvironment
273 */
274
275 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
276                 IGameDef *gamedef):
277         m_map(map),
278         m_lua(L),
279         m_gamedef(gamedef),
280         m_random_spawn_timer(3),
281         m_send_recommended_timer(0),
282         m_game_time(0),
283         m_game_time_fraction_counter(0)
284 {
285 }
286
287 ServerEnvironment::~ServerEnvironment()
288 {
289         // Clear active block list.
290         // This makes the next one delete all active objects.
291         m_active_blocks.clear();
292
293         // Convert all objects to static and delete the active objects
294         deactivateFarObjects(true);
295
296         // Drop/delete map
297         m_map->drop();
298 }
299
300 void ServerEnvironment::serializePlayers(const std::string &savedir)
301 {
302         std::string players_path = savedir + "/players";
303         fs::CreateDir(players_path);
304
305         core::map<Player*, bool> saved_players;
306
307         std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
308         for(u32 i=0; i<player_files.size(); i++)
309         {
310                 if(player_files[i].dir)
311                         continue;
312                 
313                 // Full path to this file
314                 std::string path = players_path + "/" + player_files[i].name;
315
316                 //infostream<<"Checking player file "<<path<<std::endl;
317
318                 // Load player to see what is its name
319                 ServerRemotePlayer testplayer(this);
320                 {
321                         // Open file and deserialize
322                         std::ifstream is(path.c_str(), std::ios_base::binary);
323                         if(is.good() == false)
324                         {
325                                 infostream<<"Failed to read "<<path<<std::endl;
326                                 continue;
327                         }
328                         testplayer.deSerialize(is);
329                 }
330
331                 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
332                 
333                 // Search for the player
334                 std::string playername = testplayer.getName();
335                 Player *player = getPlayer(playername.c_str());
336                 if(player == NULL)
337                 {
338                         infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
339                         continue;
340                 }
341
342                 //infostream<<"Found matching player, overwriting."<<std::endl;
343
344                 // OK, found. Save player there.
345                 {
346                         // Open file and serialize
347                         std::ofstream os(path.c_str(), std::ios_base::binary);
348                         if(os.good() == false)
349                         {
350                                 infostream<<"Failed to overwrite "<<path<<std::endl;
351                                 continue;
352                         }
353                         player->serialize(os);
354                         saved_players.insert(player, true);
355                 }
356         }
357
358         for(core::list<Player*>::Iterator i = m_players.begin();
359                         i != m_players.end(); i++)
360         {
361                 Player *player = *i;
362                 if(saved_players.find(player) != NULL)
363                 {
364                         /*infostream<<"Player "<<player->getName()
365                                         <<" was already saved."<<std::endl;*/
366                         continue;
367                 }
368                 std::string playername = player->getName();
369                 // Don't save unnamed player
370                 if(playername == "")
371                 {
372                         //infostream<<"Not saving unnamed player."<<std::endl;
373                         continue;
374                 }
375                 /*
376                         Find a sane filename
377                 */
378                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
379                         playername = "player";
380                 std::string path = players_path + "/" + playername;
381                 bool found = false;
382                 for(u32 i=0; i<1000; i++)
383                 {
384                         if(fs::PathExists(path) == false)
385                         {
386                                 found = true;
387                                 break;
388                         }
389                         path = players_path + "/" + playername + itos(i);
390                 }
391                 if(found == false)
392                 {
393                         infostream<<"Didn't find free file for player"<<std::endl;
394                         continue;
395                 }
396
397                 {
398                         /*infostream<<"Saving player "<<player->getName()<<" to "
399                                         <<path<<std::endl;*/
400                         // Open file and serialize
401                         std::ofstream os(path.c_str(), std::ios_base::binary);
402                         if(os.good() == false)
403                         {
404                                 infostream<<"Failed to overwrite "<<path<<std::endl;
405                                 continue;
406                         }
407                         player->serialize(os);
408                         saved_players.insert(player, true);
409                 }
410         }
411
412         //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
413 }
414
415 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
416 {
417         std::string players_path = savedir + "/players";
418
419         core::map<Player*, bool> saved_players;
420
421         std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
422         for(u32 i=0; i<player_files.size(); i++)
423         {
424                 if(player_files[i].dir)
425                         continue;
426                 
427                 // Full path to this file
428                 std::string path = players_path + "/" + player_files[i].name;
429
430                 infostream<<"Checking player file "<<path<<std::endl;
431
432                 // Load player to see what is its name
433                 ServerRemotePlayer testplayer(this);
434                 {
435                         // Open file and deserialize
436                         std::ifstream is(path.c_str(), std::ios_base::binary);
437                         if(is.good() == false)
438                         {
439                                 infostream<<"Failed to read "<<path<<std::endl;
440                                 continue;
441                         }
442                         testplayer.deSerialize(is);
443                 }
444
445                 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
446                 {
447                         infostream<<"Not loading player with invalid name: "
448                                         <<testplayer.getName()<<std::endl;
449                 }
450
451                 infostream<<"Loaded test player with name "<<testplayer.getName()
452                                 <<std::endl;
453                 
454                 // Search for the player
455                 std::string playername = testplayer.getName();
456                 Player *player = getPlayer(playername.c_str());
457                 bool newplayer = false;
458                 if(player == NULL)
459                 {
460                         infostream<<"Is a new player"<<std::endl;
461                         player = new ServerRemotePlayer(this);
462                         newplayer = true;
463                 }
464
465                 // Load player
466                 {
467                         infostream<<"Reading player "<<testplayer.getName()<<" from "
468                                         <<path<<std::endl;
469                         // Open file and deserialize
470                         std::ifstream is(path.c_str(), std::ios_base::binary);
471                         if(is.good() == false)
472                         {
473                                 infostream<<"Failed to read "<<path<<std::endl;
474                                 continue;
475                         }
476                         player->deSerialize(is);
477                 }
478
479                 if(newplayer)
480                         addPlayer(player);
481         }
482 }
483
484 void ServerEnvironment::saveMeta(const std::string &savedir)
485 {
486         std::string path = savedir + "/env_meta.txt";
487
488         // Open file and serialize
489         std::ofstream os(path.c_str(), std::ios_base::binary);
490         if(os.good() == false)
491         {
492                 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
493                                 <<path<<std::endl;
494                 throw SerializationError("Couldn't save env meta");
495         }
496
497         Settings args;
498         args.setU64("game_time", m_game_time);
499         args.setU64("time_of_day", getTimeOfDay());
500         args.writeLines(os);
501         os<<"EnvArgsEnd\n";
502 }
503
504 void ServerEnvironment::loadMeta(const std::string &savedir)
505 {
506         std::string path = savedir + "/env_meta.txt";
507
508         // Open file and deserialize
509         std::ifstream is(path.c_str(), std::ios_base::binary);
510         if(is.good() == false)
511         {
512                 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
513                                 <<path<<std::endl;
514                 throw SerializationError("Couldn't load env meta");
515         }
516
517         Settings args;
518         
519         for(;;)
520         {
521                 if(is.eof())
522                         throw SerializationError
523                                         ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
524                 std::string line;
525                 std::getline(is, line);
526                 std::string trimmedline = trim(line);
527                 if(trimmedline == "EnvArgsEnd")
528                         break;
529                 args.parseConfigLine(line);
530         }
531         
532         try{
533                 m_game_time = args.getU64("game_time");
534         }catch(SettingNotFoundException &e){
535                 // Getting this is crucial, otherwise timestamps are useless
536                 throw SerializationError("Couldn't load env meta game_time");
537         }
538
539         try{
540                 m_time_of_day = args.getU64("time_of_day");
541         }catch(SettingNotFoundException &e){
542                 // This is not as important
543                 m_time_of_day = 9000;
544         }
545 }
546
547 #if 0
548 // This is probably very useless
549 void spawnRandomObjects(MapBlock *block)
550 {
551         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
552         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
553         {
554                 bool last_node_walkable = false;
555                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
556                 {
557                         v3s16 p(x0,y0,z0);
558                         MapNode n = block->getNodeNoEx(p);
559                         if(n.getContent() == CONTENT_IGNORE)
560                                 continue;
561                         if(m_gamedef->ndef()->get(n).liquid_type != LIQUID_NONE)
562                                 continue;
563                         if(m_gamedef->ndef()->get(n).walkable)
564                         {
565                                 last_node_walkable = true;
566                                 continue;
567                         }
568                         if(last_node_walkable)
569                         {
570                                 // If block contains light information
571                                 if(m_gamedef->ndef()->get(n).param_type == CPT_LIGHT)
572                                 {
573                                         if(n.getLight(LIGHTBANK_DAY) <= 5)
574                                         {
575                                                 if(myrand() % 1000 == 0)
576                                                 {
577                                                         v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
578                                                         pos_f.Y -= BS*0.4;
579                                                         ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
580                                                         std::string data = obj->getStaticData();
581                                                         StaticObject s_obj(obj->getType(),
582                                                                         obj->getBasePosition(), data);
583                                                         // Add one
584                                                         block->m_static_objects.insert(0, s_obj);
585                                                         delete obj;
586                                                         block->setChangedFlag();
587                                                 }
588                                         }
589                                 }
590                         }
591                         last_node_walkable = false;
592                 }
593         }
594 }
595 #endif
596
597 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
598 {
599         // Get time difference
600         u32 dtime_s = 0;
601         u32 stamp = block->getTimestamp();
602         if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
603                 dtime_s = m_game_time - block->getTimestamp();
604         dtime_s += additional_dtime;
605
606         // Set current time as timestamp (and let it set ChangedFlag)
607         block->setTimestamp(m_game_time);
608
609         //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
610         
611         // Activate stored objects
612         activateObjects(block);
613
614         // Run node metadata
615         bool changed = block->m_node_metadata->step((float)dtime_s);
616         if(changed)
617         {
618                 MapEditEvent event;
619                 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
620                 event.p = block->getPos();
621                 m_map->dispatchEvent(&event);
622
623                 block->setChangedFlag();
624         }
625
626         // TODO: Do something
627         // TODO: Implement usage of ActiveBlockModifier
628         
629         // Here's a quick demonstration
630         v3s16 p0;
631         for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
632         for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
633         for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
634         {
635                 v3s16 p = p0 + block->getPosRelative();
636                 MapNode n = block->getNodeNoEx(p0);
637 #if 1
638                 // Test something:
639                 // Convert all mud under proper day lighting to grass
640                 if(n.getContent() == CONTENT_MUD)
641                 {
642                         if(dtime_s > 300)
643                         {
644                                 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
645                                 if(m_gamedef->ndef()->get(n_top).air_equivalent &&
646                                                 n_top.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) >= 13)
647                                 {
648                                         n.setContent(CONTENT_GRASS);
649                                         m_map->addNodeWithEvent(p, n);
650                                 }
651                         }
652                 }
653 #endif
654         }
655 }
656
657 void ServerEnvironment::clearAllObjects()
658 {
659         infostream<<"ServerEnvironment::clearAllObjects(): "
660                         <<"Removing all active objects"<<std::endl;
661         core::list<u16> objects_to_remove;
662         for(core::map<u16, ServerActiveObject*>::Iterator
663                         i = m_active_objects.getIterator();
664                         i.atEnd()==false; i++)
665         {
666                 ServerActiveObject* obj = i.getNode()->getValue();
667                 u16 id = i.getNode()->getKey();         
668                 v3f objectpos = obj->getBasePosition(); 
669                 // Delete static object if block is loaded
670                 if(obj->m_static_exists){
671                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
672                         if(block){
673                                 block->m_static_objects.remove(id);
674                                 block->raiseModified(MOD_STATE_WRITE_NEEDED);
675                                 obj->m_static_exists = false;
676                         }
677                 }
678                 // If known by some client, don't delete immediately
679                 if(obj->m_known_by_count > 0){
680                         obj->m_pending_deactivation = true;
681                         obj->m_removed = true;
682                         continue;
683                 }
684                 // Deregister in scripting api
685                 scriptapi_rm_object_reference(m_lua, obj);
686                 // Delete active object
687                 delete obj;
688                 // Id to be removed from m_active_objects
689                 objects_to_remove.push_back(id);
690         }
691         // Remove references from m_active_objects
692         for(core::list<u16>::Iterator i = objects_to_remove.begin();
693                         i != objects_to_remove.end(); i++)
694         {
695                 m_active_objects.remove(*i);
696         }
697
698         core::list<v3s16> loadable_blocks;
699         infostream<<"ServerEnvironment::clearAllObjects(): "
700                         <<"Listing all loadable blocks"<<std::endl;
701         m_map->listAllLoadableBlocks(loadable_blocks);
702         infostream<<"ServerEnvironment::clearAllObjects(): "
703                         <<"Done listing all loadable blocks: "
704                         <<loadable_blocks.size()
705                         <<", now clearing"<<std::endl;
706         u32 report_interval = loadable_blocks.size() / 10;
707         u32 num_blocks_checked = 0;
708         u32 num_blocks_cleared = 0;
709         u32 num_objs_cleared = 0;
710         for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
711                         i != loadable_blocks.end(); i++)
712         {
713                 v3s16 p = *i;
714                 MapBlock *block = m_map->emergeBlock(p, false);
715                 if(!block){
716                         errorstream<<"ServerEnvironment::clearAllObjects(): "
717                                         <<"Failed to emerge block "<<PP(p)<<std::endl;
718                         continue;
719                 }
720                 u32 num_stored = block->m_static_objects.m_stored.size();
721                 u32 num_active = block->m_static_objects.m_active.size();
722                 if(num_stored != 0 || num_active != 0){
723                         block->m_static_objects.m_stored.clear();
724                         block->m_static_objects.m_active.clear();
725                         block->raiseModified(MOD_STATE_WRITE_NEEDED);
726                         num_objs_cleared += num_stored + num_active;
727                         num_blocks_cleared++;
728                 }
729                 num_blocks_checked++;
730
731                 if(num_blocks_checked % report_interval == 0){
732                         float percent = 100.0 * (float)num_blocks_checked /
733                                         loadable_blocks.size();
734                         infostream<<"ServerEnvironment::clearAllObjects(): "
735                                         <<"Cleared "<<num_objs_cleared<<" objects"
736                                         <<" in "<<num_blocks_cleared<<" blocks ("
737                                         <<percent<<"%)"<<std::endl;
738                 }
739         }
740         infostream<<"ServerEnvironment::clearAllObjects(): "
741                         <<"Finished: Cleared "<<num_objs_cleared<<" objects"
742                         <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
743 }
744
745 static void getMob_dungeon_master(Settings &properties)
746 {
747         properties.set("looks", "dungeon_master");
748         properties.setFloat("yaw", 1.57);
749         properties.setFloat("hp", 30);
750         properties.setBool("bright_shooting", true);
751         properties.set("shoot_type", "fireball");
752         properties.set("shoot_y", "0.7");
753         properties.set("player_hit_damage", "1");
754         properties.set("player_hit_distance", "1.0");
755         properties.set("player_hit_interval", "0.5");
756         properties.setBool("mindless_rage", myrand_range(0,100)==0);
757 }
758
759 void ServerEnvironment::step(float dtime)
760 {
761         DSTACK(__FUNCTION_NAME);
762         
763         //TimeTaker timer("ServerEnv step");
764
765         // Get some settings
766         bool footprints = g_settings->getBool("footprints");
767
768         /*
769                 Increment game time
770         */
771         {
772                 m_game_time_fraction_counter += dtime;
773                 u32 inc_i = (u32)m_game_time_fraction_counter;
774                 m_game_time += inc_i;
775                 m_game_time_fraction_counter -= (float)inc_i;
776         }
777         
778         /*
779                 Handle players
780         */
781         {
782                 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
783                 for(core::list<Player*>::Iterator i = m_players.begin();
784                                 i != m_players.end(); i++)
785                 {
786                         Player *player = *i;
787                         
788                         // Ignore disconnected players
789                         if(player->peer_id == 0)
790                                 continue;
791
792                         v3f playerpos = player->getPosition();
793                         
794                         // Move
795                         player->move(dtime, *m_map, 100*BS);
796                         
797                         /*
798                                 Add footsteps to grass
799                         */
800                         if(footprints)
801                         {
802                                 // Get node that is at BS/4 under player
803                                 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
804                                 try{
805                                         MapNode n = m_map->getNode(bottompos);
806                                         if(n.getContent() == CONTENT_GRASS)
807                                         {
808                                                 n.setContent(CONTENT_GRASS_FOOTSTEPS);
809                                                 m_map->setNode(bottompos, n);
810                                         }
811                                 }
812                                 catch(InvalidPositionException &e)
813                                 {
814                                 }
815                         }
816                 }
817         }
818
819         /*
820                 Manage active block list
821         */
822         if(m_active_blocks_management_interval.step(dtime, 2.0))
823         {
824                 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
825                 /*
826                         Get player block positions
827                 */
828                 core::list<v3s16> players_blockpos;
829                 for(core::list<Player*>::Iterator
830                                 i = m_players.begin();
831                                 i != m_players.end(); i++)
832                 {
833                         Player *player = *i;
834                         // Ignore disconnected players
835                         if(player->peer_id == 0)
836                                 continue;
837                         v3s16 blockpos = getNodeBlockPos(
838                                         floatToInt(player->getPosition(), BS));
839                         players_blockpos.push_back(blockpos);
840                 }
841                 
842                 /*
843                         Update list of active blocks, collecting changes
844                 */
845                 const s16 active_block_range = g_settings->getS16("active_block_range");
846                 core::map<v3s16, bool> blocks_removed;
847                 core::map<v3s16, bool> blocks_added;
848                 m_active_blocks.update(players_blockpos, active_block_range,
849                                 blocks_removed, blocks_added);
850
851                 /*
852                         Handle removed blocks
853                 */
854
855                 // Convert active objects that are no more in active blocks to static
856                 deactivateFarObjects(false);
857                 
858                 for(core::map<v3s16, bool>::Iterator
859                                 i = blocks_removed.getIterator();
860                                 i.atEnd()==false; i++)
861                 {
862                         v3s16 p = i.getNode()->getKey();
863
864                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
865                                         <<") became inactive"<<std::endl;*/
866                         
867                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
868                         if(block==NULL)
869                                 continue;
870                         
871                         // Set current time as timestamp (and let it set ChangedFlag)
872                         block->setTimestamp(m_game_time);
873                 }
874
875                 /*
876                         Handle added blocks
877                 */
878
879                 for(core::map<v3s16, bool>::Iterator
880                                 i = blocks_added.getIterator();
881                                 i.atEnd()==false; i++)
882                 {
883                         v3s16 p = i.getNode()->getKey();
884                         
885                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
886                                         <<") became active"<<std::endl;*/
887
888                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
889                         if(block==NULL)
890                                 continue;
891
892                         activateBlock(block);
893                 }
894         }
895
896         /*
897                 Mess around in active blocks
898         */
899         if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
900         {
901                 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
902                 
903                 float dtime = 1.0;
904
905                 for(core::map<v3s16, bool>::Iterator
906                                 i = m_active_blocks.m_list.getIterator();
907                                 i.atEnd()==false; i++)
908                 {
909                         v3s16 p = i.getNode()->getKey();
910                         
911                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
912                                         <<") being handled"<<std::endl;*/
913
914                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
915                         if(block==NULL)
916                                 continue;
917
918                         // Reset block usage timer
919                         block->resetUsageTimer();
920                         
921                         // Set current time as timestamp
922                         block->setTimestampNoChangedFlag(m_game_time);
923
924                         // Run node metadata
925                         bool changed = block->m_node_metadata->step(dtime);
926                         if(changed)
927                         {
928                                 MapEditEvent event;
929                                 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
930                                 event.p = p;
931                                 m_map->dispatchEvent(&event);
932
933                                 block->setChangedFlag();
934                         }
935                 }
936         }
937         
938         if(m_active_blocks_test_interval.step(dtime, 10.0))
939         {
940                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
941                 //float dtime = 10.0;
942                 
943                 for(core::map<v3s16, bool>::Iterator
944                                 i = m_active_blocks.m_list.getIterator();
945                                 i.atEnd()==false; i++)
946                 {
947                         v3s16 p = i.getNode()->getKey();
948                         
949                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
950                                         <<") being handled"<<std::endl;*/
951
952                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
953                         if(block==NULL)
954                                 continue;
955                         
956                         // Set current time as timestamp
957                         block->setTimestampNoChangedFlag(m_game_time);
958
959                         /*
960                                 Do stuff!
961
962                                 Note that map modifications should be done using the event-
963                                 making map methods so that the server gets information
964                                 about them.
965
966                                 Reading can be done quickly directly from the block.
967
968                                 Everything should bind to inside this single content
969                                 searching loop to keep things fast.
970                         */
971                         // TODO: Implement usage of ActiveBlockModifier
972                         
973                         // Find out how many objects the block contains
974                         //u32 active_object_count = block->m_static_objects.m_active.size();
975                         // Find out how many objects this and all the neighbors contain
976                         u32 active_object_count_wider = 0;
977                         for(s16 x=-1; x<=1; x++)
978                         for(s16 y=-1; y<=1; y++)
979                         for(s16 z=-1; z<=1; z++)
980                         {
981                                 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
982                                 if(block==NULL)
983                                         continue;
984                                 active_object_count_wider +=
985                                                 block->m_static_objects.m_active.size()
986                                                 + block->m_static_objects.m_stored.size();
987                                 
988                                 /*if(block->m_static_objects.m_stored.size() != 0){
989                                         errorstream<<"ServerEnvironment::step(): "
990                                                         <<PP(block->getPos())<<" contains "
991                                                         <<block->m_static_objects.m_stored.size()
992                                                         <<" stored objects; "
993                                                         <<"when spawning objects, when counting active "
994                                                         <<"objects in wide area. relative position: "
995                                                         <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
996                                 }*/
997                         }
998
999                         v3s16 p0;
1000                         for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
1001                         for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
1002                         for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
1003                         {
1004                                 v3s16 p = p0 + block->getPosRelative();
1005                                 MapNode n = block->getNodeNoEx(p0);
1006
1007                                 /*
1008                                         Test something:
1009                                         Convert mud under proper lighting to grass
1010                                 */
1011                                 if(n.getContent() == CONTENT_MUD)
1012                                 {
1013                                         if(myrand()%20 == 0)
1014                                         {
1015                                                 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1016                                                 if(m_gamedef->ndef()->get(n_top).air_equivalent &&
1017                                                                 n_top.getLightBlend(getDayNightRatio(),
1018                                                                                 m_gamedef->ndef()) >= 13)
1019                                                 {
1020                                                         n.setContent(CONTENT_GRASS);
1021                                                         m_map->addNodeWithEvent(p, n);
1022                                                 }
1023                                         }
1024                                 }
1025                                 /*
1026                                         Convert grass into mud if under something else than air
1027                                 */
1028                                 if(n.getContent() == CONTENT_GRASS)
1029                                 {
1030                                         //if(myrand()%20 == 0)
1031                                         {
1032                                                 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1033                                                 if(m_gamedef->ndef()->get(n_top).air_equivalent == false)
1034                                                 {
1035                                                         n.setContent(CONTENT_MUD);
1036                                                         m_map->addNodeWithEvent(p, n);
1037                                                 }
1038                                         }
1039                                 }
1040                                 /*
1041                                         Rats spawn around regular trees
1042                                 */
1043                                 if(n.getContent() == CONTENT_TREE ||
1044                                                 n.getContent() == CONTENT_JUNGLETREE)
1045                                 {
1046                                         if(myrand()%200 == 0 && active_object_count_wider == 0)
1047                                         {
1048                                                 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1049                                                                 0, myrand_range(-2, 2));
1050                                                 MapNode n1 = m_map->getNodeNoEx(p1);
1051                                                 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1052                                                 if(n1b.getContent() == CONTENT_GRASS &&
1053                                                                 n1.getContent() == CONTENT_AIR)
1054                                                 {
1055                                                         v3f pos = intToFloat(p1, BS);
1056                                                         ServerActiveObject *obj = new RatSAO(this, pos);
1057                                                         addActiveObject(obj);
1058                                                 }
1059                                         }
1060                                 }
1061                                 /*
1062                                         Fun things spawn in caves and dungeons
1063                                 */
1064                                 if(n.getContent() == CONTENT_STONE ||
1065                                                 n.getContent() == CONTENT_MOSSYCOBBLE)
1066                                 {
1067                                         if(myrand()%200 == 0 && active_object_count_wider == 0)
1068                                         {
1069                                                 v3s16 p1 = p + v3s16(0,1,0);
1070                                                 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1071                                                 if(n1a.getLightBlend(getDayNightRatio(),
1072                                                                 m_gamedef->ndef()) <= 3){
1073                                                         MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1074                                                         if(n1a.getContent() == CONTENT_AIR &&
1075                                                                         n1b.getContent() == CONTENT_AIR)
1076                                                         {
1077                                                                 v3f pos = intToFloat(p1, BS);
1078                                                                 int i = myrand()%5;
1079                                                                 if(i == 0 || i == 1){
1080                                                                         actionstream<<"A dungeon master spawns at "
1081                                                                                         <<PP(p1)<<std::endl;
1082                                                                         Settings properties;
1083                                                                         getMob_dungeon_master(properties);
1084                                                                         ServerActiveObject *obj = new MobV2SAO(
1085                                                                                         this, pos, &properties);
1086                                                                         addActiveObject(obj);
1087                                                                 } else if(i == 2 || i == 3){
1088                                                                         actionstream<<"Rats spawn at "
1089                                                                                         <<PP(p1)<<std::endl;
1090                                                                         for(int j=0; j<3; j++){
1091                                                                                 ServerActiveObject *obj = new RatSAO(
1092                                                                                                 this, pos);
1093                                                                                 addActiveObject(obj);
1094                                                                         }
1095                                                                 } else {
1096                                                                         actionstream<<"An oerkki spawns at "
1097                                                                                         <<PP(p1)<<std::endl;
1098                                                                         ServerActiveObject *obj = new Oerkki1SAO(
1099                                                                                         this, pos);
1100                                                                         addActiveObject(obj);
1101                                                                 }
1102                                                         }
1103                                                 }
1104                                         }
1105                                 }
1106                                 /*
1107                                         Make trees from saplings!
1108                                 */
1109                                 if(n.getContent() == CONTENT_SAPLING)
1110                                 {
1111                                         if(myrand()%50 == 0)
1112                                         {
1113                                                 actionstream<<"A sapling grows into a tree at "
1114                                                                 <<PP(p)<<std::endl;
1115
1116                                                 core::map<v3s16, MapBlock*> modified_blocks;
1117                                                 v3s16 tree_p = p;
1118                                                 ManualMapVoxelManipulator vmanip(m_map);
1119                                                 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1120                                                 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1121                                                 bool is_apple_tree = myrand()%4 == 0;
1122                                                 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1123                                                 vmanip.blitBackAll(&modified_blocks);
1124
1125                                                 // update lighting
1126                                                 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1127                                                 for(core::map<v3s16, MapBlock*>::Iterator
1128                                                         i = modified_blocks.getIterator();
1129                                                         i.atEnd() == false; i++)
1130                                                 {
1131                                                         lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1132                                                 }
1133                                                 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1134
1135                                                 // Send a MEET_OTHER event
1136                                                 MapEditEvent event;
1137                                                 event.type = MEET_OTHER;
1138                                                 for(core::map<v3s16, MapBlock*>::Iterator
1139                                                         i = modified_blocks.getIterator();
1140                                                         i.atEnd() == false; i++)
1141                                                 {
1142                                                         v3s16 p = i.getNode()->getKey();
1143                                                         event.modified_blocks.insert(p, true);
1144                                                 }
1145                                                 m_map->dispatchEvent(&event);
1146                                         }
1147                                 }
1148                         }
1149                 }
1150         }
1151         
1152         /*
1153                 Step script environment (run global on_step())
1154         */
1155         scriptapi_environment_step(m_lua, dtime);
1156
1157         /*
1158                 Step active objects
1159         */
1160         {
1161                 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1162                 //TimeTaker timer("Step active objects");
1163
1164                 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1165                 
1166                 // This helps the objects to send data at the same time
1167                 bool send_recommended = false;
1168                 m_send_recommended_timer += dtime;
1169                 if(m_send_recommended_timer > getSendRecommendedInterval())
1170                 {
1171                         m_send_recommended_timer -= getSendRecommendedInterval();
1172                         send_recommended = true;
1173                 }
1174
1175                 for(core::map<u16, ServerActiveObject*>::Iterator
1176                                 i = m_active_objects.getIterator();
1177                                 i.atEnd()==false; i++)
1178                 {
1179                         ServerActiveObject* obj = i.getNode()->getValue();
1180                         // Remove non-peaceful mobs on peaceful mode
1181                         if(g_settings->getBool("only_peaceful_mobs")){
1182                                 if(!obj->isPeaceful())
1183                                         obj->m_removed = true;
1184                         }
1185                         // Don't step if is to be removed or stored statically
1186                         if(obj->m_removed || obj->m_pending_deactivation)
1187                                 continue;
1188                         // Step object
1189                         obj->step(dtime, send_recommended);
1190                         // Read messages from object
1191                         while(obj->m_messages_out.size() > 0)
1192                         {
1193                                 m_active_object_messages.push_back(
1194                                                 obj->m_messages_out.pop_front());
1195                         }
1196                 }
1197         }
1198         
1199         /*
1200                 Manage active objects
1201         */
1202         if(m_object_management_interval.step(dtime, 0.5))
1203         {
1204                 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1205                 /*
1206                         Remove objects that satisfy (m_removed && m_known_by_count==0)
1207                 */
1208                 removeRemovedObjects();
1209         }
1210
1211         if(g_settings->getBool("enable_experimental"))
1212         {
1213
1214         /*
1215                 TEST CODE
1216         */
1217 #if 0
1218         m_random_spawn_timer -= dtime;
1219         if(m_random_spawn_timer < 0)
1220         {
1221                 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1222                 //m_random_spawn_timer += 2.0;
1223                 m_random_spawn_timer += 200.0;
1224
1225                 /*
1226                         Find some position
1227                 */
1228
1229                 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1230                 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1231                 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1232                 
1233                 Player *player = getRandomConnectedPlayer();
1234                 v3f pos(0,0,0);
1235                 if(player)
1236                         pos = player->getPosition();
1237                 pos += v3f(
1238                         myrand_range(-3,3)*BS,
1239                         5,
1240                         myrand_range(-3,3)*BS
1241                 );
1242
1243                 /*
1244                         Create a ServerActiveObject
1245                 */
1246
1247                 //TestSAO *obj = new TestSAO(this, pos);
1248                 //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
1249                 //ServerActiveObject *obj = new RatSAO(this, pos);
1250                 //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
1251                 //ServerActiveObject *obj = new FireflySAO(this, pos);
1252
1253                 infostream<<"Server: Spawning MobV2SAO at "
1254                                 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1255                 
1256                 Settings properties;
1257                 getMob_dungeon_master(properties);
1258                 ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
1259                 addActiveObject(obj);
1260         }
1261 #endif
1262
1263         } // enable_experimental
1264 }
1265
1266 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1267 {
1268         core::map<u16, ServerActiveObject*>::Node *n;
1269         n = m_active_objects.find(id);
1270         if(n == NULL)
1271                 return NULL;
1272         return n->getValue();
1273 }
1274
1275 bool isFreeServerActiveObjectId(u16 id,
1276                 core::map<u16, ServerActiveObject*> &objects)
1277 {
1278         if(id == 0)
1279                 return false;
1280         
1281         for(core::map<u16, ServerActiveObject*>::Iterator
1282                         i = objects.getIterator();
1283                         i.atEnd()==false; i++)
1284         {
1285                 if(i.getNode()->getKey() == id)
1286                         return false;
1287         }
1288         return true;
1289 }
1290
1291 u16 getFreeServerActiveObjectId(
1292                 core::map<u16, ServerActiveObject*> &objects)
1293 {
1294         u16 new_id = 1;
1295         for(;;)
1296         {
1297                 if(isFreeServerActiveObjectId(new_id, objects))
1298                         return new_id;
1299                 
1300                 if(new_id == 65535)
1301                         return 0;
1302
1303                 new_id++;
1304         }
1305 }
1306
1307 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1308 {
1309         assert(object);
1310         u16 id = addActiveObjectRaw(object, true);
1311         return id;
1312 }
1313
1314 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1315 {
1316         assert(obj);
1317
1318         v3f objectpos = obj->getBasePosition(); 
1319
1320         // The block in which the object resides in
1321         v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1322
1323         /*
1324                 Update the static data
1325         */
1326
1327         // Create new static object
1328         std::string staticdata = obj->getStaticData();
1329         StaticObject s_obj(obj->getType(), objectpos, staticdata);
1330         // Add to the block where the object is located in
1331         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1332         // Get or generate the block
1333         MapBlock *block = m_map->emergeBlock(blockpos);
1334
1335         bool succeeded = false;
1336
1337         if(block)
1338         {
1339                 block->m_static_objects.insert(0, s_obj);
1340                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1341                 succeeded = true;
1342         }
1343         else{
1344                 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1345                                 <<"Could not find or generate "
1346                                 <<"a block for storing static object"<<std::endl;
1347                 succeeded = false;
1348         }
1349
1350         delete obj;
1351
1352         return succeeded;
1353 }
1354
1355 /*
1356         Finds out what new objects have been added to
1357         inside a radius around a position
1358 */
1359 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1360                 core::map<u16, bool> &current_objects,
1361                 core::map<u16, bool> &added_objects)
1362 {
1363         v3f pos_f = intToFloat(pos, BS);
1364         f32 radius_f = radius * BS;
1365         /*
1366                 Go through the object list,
1367                 - discard m_removed objects,
1368                 - discard objects that are too far away,
1369                 - discard objects that are found in current_objects.
1370                 - add remaining objects to added_objects
1371         */
1372         for(core::map<u16, ServerActiveObject*>::Iterator
1373                         i = m_active_objects.getIterator();
1374                         i.atEnd()==false; i++)
1375         {
1376                 u16 id = i.getNode()->getKey();
1377                 // Get object
1378                 ServerActiveObject *object = i.getNode()->getValue();
1379                 if(object == NULL)
1380                         continue;
1381                 // Discard if removed
1382                 if(object->m_removed)
1383                         continue;
1384                 // Discard if too far
1385                 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1386                 if(distance_f > radius_f)
1387                         continue;
1388                 // Discard if already on current_objects
1389                 core::map<u16, bool>::Node *n;
1390                 n = current_objects.find(id);
1391                 if(n != NULL)
1392                         continue;
1393                 // Add to added_objects
1394                 added_objects.insert(id, false);
1395         }
1396 }
1397
1398 /*
1399         Finds out what objects have been removed from
1400         inside a radius around a position
1401 */
1402 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1403                 core::map<u16, bool> &current_objects,
1404                 core::map<u16, bool> &removed_objects)
1405 {
1406         v3f pos_f = intToFloat(pos, BS);
1407         f32 radius_f = radius * BS;
1408         /*
1409                 Go through current_objects; object is removed if:
1410                 - object is not found in m_active_objects (this is actually an
1411                   error condition; objects should be set m_removed=true and removed
1412                   only after all clients have been informed about removal), or
1413                 - object has m_removed=true, or
1414                 - object is too far away
1415         */
1416         for(core::map<u16, bool>::Iterator
1417                         i = current_objects.getIterator();
1418                         i.atEnd()==false; i++)
1419         {
1420                 u16 id = i.getNode()->getKey();
1421                 ServerActiveObject *object = getActiveObject(id);
1422                 if(object == NULL)
1423                 {
1424                         infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1425                                         <<" object in current_objects is NULL"<<std::endl;
1426                 }
1427                 else if(object->m_removed == false)
1428                 {
1429                         f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1430                         /*infostream<<"removed == false"
1431                                         <<"distance_f = "<<distance_f
1432                                         <<", radius_f = "<<radius_f<<std::endl;*/
1433                         if(distance_f < radius_f)
1434                         {
1435                                 // Not removed
1436                                 continue;
1437                         }
1438                 }
1439                 removed_objects.insert(id, false);
1440         }
1441 }
1442
1443 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1444 {
1445         if(m_active_object_messages.size() == 0)
1446                 return ActiveObjectMessage(0);
1447         
1448         return m_active_object_messages.pop_front();
1449 }
1450
1451 /*
1452         ************ Private methods *************
1453 */
1454
1455 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1456                 bool set_changed)
1457 {
1458         assert(object);
1459         if(object->getId() == 0){
1460                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1461                 if(new_id == 0)
1462                 {
1463                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1464                                         <<"no free ids available"<<std::endl;
1465                         delete object;
1466                         return 0;
1467                 }
1468                 object->setId(new_id);
1469         }
1470         else{
1471                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1472                                 <<"supplied with id "<<object->getId()<<std::endl;
1473         }
1474         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1475         {
1476                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1477                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1478                 delete object;
1479                 return 0;
1480         }
1481         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1482                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1483                         
1484         m_active_objects.insert(object->getId(), object);
1485   
1486         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1487                         <<"Added id="<<object->getId()<<"; there are now "
1488                         <<m_active_objects.size()<<" active objects."
1489                         <<std::endl;
1490         
1491         // Add static object to active static list of the block
1492         v3f objectpos = object->getBasePosition();
1493         std::string staticdata = object->getStaticData();
1494         StaticObject s_obj(object->getType(), objectpos, staticdata);
1495         // Add to the block where the object is located in
1496         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1497         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1498         if(block)
1499         {
1500                 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1501                 object->m_static_exists = true;
1502                 object->m_static_block = blockpos;
1503
1504                 if(set_changed)
1505                         block->raiseModified(MOD_STATE_WRITE_NEEDED);
1506         }
1507         else{
1508                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1509                                 <<"could not find block for storing id="<<object->getId()
1510                                 <<" statically"<<std::endl;
1511         }
1512         
1513         // Register reference in scripting api (must be done before post-init)
1514         scriptapi_add_object_reference(m_lua, object);
1515         // Post-initialize object
1516         object->addedToEnvironment();
1517
1518         return object->getId();
1519 }
1520
1521 /*
1522         Remove objects that satisfy (m_removed && m_known_by_count==0)
1523 */
1524 void ServerEnvironment::removeRemovedObjects()
1525 {
1526         core::list<u16> objects_to_remove;
1527         for(core::map<u16, ServerActiveObject*>::Iterator
1528                         i = m_active_objects.getIterator();
1529                         i.atEnd()==false; i++)
1530         {
1531                 u16 id = i.getNode()->getKey();
1532                 ServerActiveObject* obj = i.getNode()->getValue();
1533                 // This shouldn't happen but check it
1534                 if(obj == NULL)
1535                 {
1536                         infostream<<"NULL object found in ServerEnvironment"
1537                                         <<" while finding removed objects. id="<<id<<std::endl;
1538                         // Id to be removed from m_active_objects
1539                         objects_to_remove.push_back(id);
1540                         continue;
1541                 }
1542
1543                 /*
1544                         We will delete objects that are marked as removed or thatare
1545                         waiting for deletion after deactivation
1546                 */
1547                 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1548                         continue;
1549
1550                 /*
1551                         Delete static data from block if is marked as removed
1552                 */
1553                 if(obj->m_static_exists && obj->m_removed)
1554                 {
1555                         MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1556                         if(block)
1557                         {
1558                                 block->m_static_objects.remove(id);
1559                                 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1560                                 obj->m_static_exists = false;
1561                         }
1562                 }
1563
1564                 // If m_known_by_count > 0, don't actually remove.
1565                 if(obj->m_known_by_count > 0)
1566                         continue;
1567                 
1568                 // Deregister in scripting api
1569                 scriptapi_rm_object_reference(m_lua, obj);
1570
1571                 // Delete
1572                 delete obj;
1573                 // Id to be removed from m_active_objects
1574                 objects_to_remove.push_back(id);
1575         }
1576         // Remove references from m_active_objects
1577         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1578                         i != objects_to_remove.end(); i++)
1579         {
1580                 m_active_objects.remove(*i);
1581         }
1582 }
1583
1584 static void print_hexdump(std::ostream &o, const std::string &data)
1585 {
1586         const int linelength = 16;
1587         for(int l=0; ; l++){
1588                 int i0 = linelength * l;
1589                 bool at_end = false;
1590                 int thislinelength = linelength;
1591                 if(i0 + thislinelength > (int)data.size()){
1592                         thislinelength = data.size() - i0;
1593                         at_end = true;
1594                 }
1595                 for(int di=0; di<linelength; di++){
1596                         int i = i0 + di;
1597                         char buf[4];
1598                         if(di<thislinelength)
1599                                 snprintf(buf, 4, "%.2x ", data[i]);
1600                         else
1601                                 snprintf(buf, 4, "   ");
1602                         o<<buf;
1603                 }
1604                 o<<" ";
1605                 for(int di=0; di<thislinelength; di++){
1606                         int i = i0 + di;
1607                         if(data[i] >= 32)
1608                                 o<<data[i];
1609                         else
1610                                 o<<".";
1611                 }
1612                 o<<std::endl;
1613                 if(at_end)
1614                         break;
1615         }
1616 }
1617
1618 /*
1619         Convert stored objects from blocks near the players to active.
1620 */
1621 void ServerEnvironment::activateObjects(MapBlock *block)
1622 {
1623         if(block==NULL)
1624                 return;
1625         // Ignore if no stored objects (to not set changed flag)
1626         if(block->m_static_objects.m_stored.size() == 0)
1627                 return;
1628         verbosestream<<"ServerEnvironment::activateObjects(): "
1629                         <<"activating objects of block "<<PP(block->getPos())
1630                         <<" ("<<block->m_static_objects.m_stored.size()
1631                         <<" objects)"<<std::endl;
1632         bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1633         if(large_amount){
1634                 errorstream<<"suspiciously large amount of objects detected: "
1635                                 <<block->m_static_objects.m_stored.size()<<" in "
1636                                 <<PP(block->getPos())
1637                                 <<"; removing all of them."<<std::endl;
1638                 // Clear stored list
1639                 block->m_static_objects.m_stored.clear();
1640                 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1641                 return;
1642         }
1643         // A list for objects that couldn't be converted to static for some
1644         // reason. They will be stored back.
1645         core::list<StaticObject> new_stored;
1646         // Loop through stored static objects
1647         for(core::list<StaticObject>::Iterator
1648                         i = block->m_static_objects.m_stored.begin();
1649                         i != block->m_static_objects.m_stored.end(); i++)
1650         {
1651                 /*infostream<<"Server: Creating an active object from "
1652                                 <<"static data"<<std::endl;*/
1653                 StaticObject &s_obj = *i;
1654                 // Create an active object from the data
1655                 ServerActiveObject *obj = ServerActiveObject::create
1656                                 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1657                 // If couldn't create object, store static data back.
1658                 if(obj==NULL)
1659                 {
1660                         errorstream<<"ServerEnvironment::activateObjects(): "
1661                                         <<"failed to create active object from static object "
1662                                         <<"in block "<<PP(s_obj.pos/BS)
1663                                         <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1664                         print_hexdump(verbosestream, s_obj.data);
1665                         
1666                         new_stored.push_back(s_obj);
1667                         continue;
1668                 }
1669                 verbosestream<<"ServerEnvironment::activateObjects(): "
1670                                 <<"activated static object pos="<<PP(s_obj.pos/BS)
1671                                 <<" type="<<(int)s_obj.type<<std::endl;
1672                 // This will also add the object to the active static list
1673                 addActiveObjectRaw(obj, false);
1674         }
1675         // Clear stored list
1676         block->m_static_objects.m_stored.clear();
1677         // Add leftover failed stuff to stored list
1678         for(core::list<StaticObject>::Iterator
1679                         i = new_stored.begin();
1680                         i != new_stored.end(); i++)
1681         {
1682                 StaticObject &s_obj = *i;
1683                 block->m_static_objects.m_stored.push_back(s_obj);
1684         }
1685         /*
1686                 Note: Block hasn't really been modified here.
1687                 The objects have just been activated and moved from the stored
1688                 static list to the active static list.
1689                 As such, the block is essentially the same.
1690                 Thus, do not call block->setChangedFlag().
1691                 Otherwise there would be a huge amount of unnecessary I/O.
1692         */
1693 }
1694
1695 /*
1696         Convert objects that are not standing inside active blocks to static.
1697
1698         If m_known_by_count != 0, active object is not deleted, but static
1699         data is still updated.
1700
1701         If force_delete is set, active object is deleted nevertheless. It
1702         shall only be set so in the destructor of the environment.
1703 */
1704 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1705 {
1706         core::list<u16> objects_to_remove;
1707         for(core::map<u16, ServerActiveObject*>::Iterator
1708                         i = m_active_objects.getIterator();
1709                         i.atEnd()==false; i++)
1710         {
1711                 ServerActiveObject* obj = i.getNode()->getValue();
1712
1713                 // This shouldn't happen but check it
1714                 if(obj == NULL)
1715                 {
1716                         errorstream<<"NULL object found in ServerEnvironment"
1717                                         <<std::endl;
1718                         assert(0);
1719                         continue;
1720                 }
1721
1722                 // If pending deactivation, let removeRemovedObjects() do it
1723                 if(obj->m_pending_deactivation)
1724                         continue;
1725
1726                 u16 id = i.getNode()->getKey();         
1727                 v3f objectpos = obj->getBasePosition(); 
1728
1729                 // The block in which the object resides in
1730                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1731
1732                 // If block is active, don't remove
1733                 if(m_active_blocks.contains(blockpos_o))
1734                         continue;
1735
1736                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1737                                 <<"deactivating object id="<<id<<" on inactive block "
1738                                 <<PP(blockpos_o)<<std::endl;
1739
1740                 // If known by some client, don't immediately delete.
1741                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1742
1743                 /*
1744                         Update the static data
1745                 */
1746
1747                 // Create new static object
1748                 std::string staticdata_new = obj->getStaticData();
1749                 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1750                 
1751                 bool stays_in_same_block = false;
1752                 bool data_changed = true;
1753
1754                 if(obj->m_static_exists){
1755                         if(obj->m_static_block == blockpos_o)
1756                                 stays_in_same_block = true;
1757
1758                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1759                         
1760                         core::map<u16, StaticObject>::Node *n =
1761                                         block->m_static_objects.m_active.find(id);
1762                         if(n){
1763                                 StaticObject static_old = n->getValue();
1764
1765                                 float save_movem = obj->getMinimumSavedMovement();
1766
1767                                 if(static_old.data == staticdata_new &&
1768                                                 (static_old.pos - objectpos).getLength() < save_movem)
1769                                         data_changed = false;
1770                         } else {
1771                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1772                                                 <<"id="<<id<<" m_static_exists=true but "
1773                                                 <<"static data doesn't actually exist in "
1774                                                 <<PP(obj->m_static_block)<<std::endl;
1775                         }
1776                 }
1777
1778                 bool shall_be_written = (!stays_in_same_block || data_changed);
1779                 
1780                 // Delete old static object
1781                 if(obj->m_static_exists)
1782                 {
1783                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1784                         if(block)
1785                         {
1786                                 block->m_static_objects.remove(id);
1787                                 obj->m_static_exists = false;
1788                                 // Only mark block as modified if data changed considerably
1789                                 if(shall_be_written)
1790                                         block->raiseModified(MOD_STATE_WRITE_NEEDED);
1791                         }
1792                 }
1793
1794                 // Add to the block where the object is located in
1795                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1796                 // Get or generate the block
1797                 MapBlock *block = m_map->emergeBlock(blockpos);
1798
1799                 if(block)
1800                 {
1801                         if(block->m_static_objects.m_stored.size() >= 49){
1802                                 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1803                                                 <<" statically but block "<<PP(blockpos)
1804                                                 <<" already contains "
1805                                                 <<block->m_static_objects.m_stored.size()
1806                                                 <<" (over 49) objects."
1807                                                 <<" Forcing delete."<<std::endl;
1808                                 force_delete = true;
1809                         } else {
1810                                 u16 new_id = pending_delete ? id : 0;
1811                                 block->m_static_objects.insert(new_id, s_obj);
1812                                 
1813                                 // Only mark block as modified if data changed considerably
1814                                 if(shall_be_written)
1815                                         block->raiseModified(MOD_STATE_WRITE_NEEDED);
1816                                 
1817                                 obj->m_static_exists = true;
1818                                 obj->m_static_block = block->getPos();
1819                         }
1820                 }
1821                 else{
1822                         errorstream<<"ServerEnv: Could not find or generate "
1823                                         <<"a block for storing id="<<obj->getId()
1824                                         <<" statically"<<std::endl;
1825                         continue;
1826                 }
1827
1828                 /*
1829                         If known by some client, set pending deactivation.
1830                         Otherwise delete it immediately.
1831                 */
1832
1833                 if(pending_delete)
1834                 {
1835                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1836                                         <<"object id="<<id<<" is known by clients"
1837                                         <<"; not deleting yet"<<std::endl;
1838
1839                         obj->m_pending_deactivation = true;
1840                         continue;
1841                 }
1842                 
1843                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1844                                 <<"object id="<<id<<" is not known by clients"
1845                                 <<"; deleting"<<std::endl;
1846
1847                 // Deregister in scripting api
1848                 scriptapi_rm_object_reference(m_lua, obj);
1849
1850                 // Delete active object
1851                 delete obj;
1852                 // Id to be removed from m_active_objects
1853                 objects_to_remove.push_back(id);
1854         }
1855
1856         // Remove references from m_active_objects
1857         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1858                         i != objects_to_remove.end(); i++)
1859         {
1860                 m_active_objects.remove(*i);
1861         }
1862 }
1863
1864
1865 #ifndef SERVER
1866
1867 /*
1868         ClientEnvironment
1869 */
1870
1871 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1872                 ITextureSource *texturesource, IGameDef *gamedef):
1873         m_map(map),
1874         m_smgr(smgr),
1875         m_texturesource(texturesource),
1876         m_gamedef(gamedef)
1877 {
1878         assert(m_map);
1879         assert(m_smgr);
1880 }
1881
1882 ClientEnvironment::~ClientEnvironment()
1883 {
1884         // delete active objects
1885         for(core::map<u16, ClientActiveObject*>::Iterator
1886                         i = m_active_objects.getIterator();
1887                         i.atEnd()==false; i++)
1888         {
1889                 delete i.getNode()->getValue();
1890         }
1891
1892         // Drop/delete map
1893         m_map->drop();
1894 }
1895
1896 void ClientEnvironment::addPlayer(Player *player)
1897 {
1898         DSTACK(__FUNCTION_NAME);
1899         /*
1900                 It is a failure if player is local and there already is a local
1901                 player
1902         */
1903         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1904
1905         Environment::addPlayer(player);
1906 }
1907
1908 LocalPlayer * ClientEnvironment::getLocalPlayer()
1909 {
1910         for(core::list<Player*>::Iterator i = m_players.begin();
1911                         i != m_players.end(); i++)
1912         {
1913                 Player *player = *i;
1914                 if(player->isLocal())
1915                         return (LocalPlayer*)player;
1916         }
1917         return NULL;
1918 }
1919
1920 void ClientEnvironment::step(float dtime)
1921 {
1922         DSTACK(__FUNCTION_NAME);
1923
1924         // Get some settings
1925         bool free_move = g_settings->getBool("free_move");
1926         bool footprints = g_settings->getBool("footprints");
1927
1928         // Get local player
1929         LocalPlayer *lplayer = getLocalPlayer();
1930         assert(lplayer);
1931         // collision info queue
1932         core::list<CollisionInfo> player_collisions;
1933         
1934         /*
1935                 Get the speed the player is going
1936         */
1937         bool is_climbing = lplayer->is_climbing;
1938         
1939         f32 player_speed = 0.001; // just some small value
1940         player_speed = lplayer->getSpeed().getLength();
1941         
1942         /*
1943                 Maximum position increment
1944         */
1945         //f32 position_max_increment = 0.05*BS;
1946         f32 position_max_increment = 0.1*BS;
1947
1948         // Maximum time increment (for collision detection etc)
1949         // time = distance / speed
1950         f32 dtime_max_increment = position_max_increment / player_speed;
1951         
1952         // Maximum time increment is 10ms or lower
1953         if(dtime_max_increment > 0.01)
1954                 dtime_max_increment = 0.01;
1955         
1956         // Don't allow overly huge dtime
1957         if(dtime > 0.5)
1958                 dtime = 0.5;
1959         
1960         f32 dtime_downcount = dtime;
1961
1962         /*
1963                 Stuff that has a maximum time increment
1964         */
1965
1966         u32 loopcount = 0;
1967         do
1968         {
1969                 loopcount++;
1970
1971                 f32 dtime_part;
1972                 if(dtime_downcount > dtime_max_increment)
1973                 {
1974                         dtime_part = dtime_max_increment;
1975                         dtime_downcount -= dtime_part;
1976                 }
1977                 else
1978                 {
1979                         dtime_part = dtime_downcount;
1980                         /*
1981                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
1982                                 when dtime_part is so small that dtime_downcount -= dtime_part
1983                                 does nothing
1984                         */
1985                         dtime_downcount = 0;
1986                 }
1987                 
1988                 /*
1989                         Handle local player
1990                 */
1991                 
1992                 {
1993                         v3f lplayerpos = lplayer->getPosition();
1994                         
1995                         // Apply physics
1996                         if(free_move == false && is_climbing == false)
1997                         {
1998                                 // Gravity
1999                                 v3f speed = lplayer->getSpeed();
2000                                 if(lplayer->swimming_up == false)
2001                                         speed.Y -= 9.81 * BS * dtime_part * 2;
2002
2003                                 // Water resistance
2004                                 if(lplayer->in_water_stable || lplayer->in_water)
2005                                 {
2006                                         f32 max_down = 2.0*BS;
2007                                         if(speed.Y < -max_down) speed.Y = -max_down;
2008
2009                                         f32 max = 2.5*BS;
2010                                         if(speed.getLength() > max)
2011                                         {
2012                                                 speed = speed / speed.getLength() * max;
2013                                         }
2014                                 }
2015
2016                                 lplayer->setSpeed(speed);
2017                         }
2018
2019                         /*
2020                                 Move the lplayer.
2021                                 This also does collision detection.
2022                         */
2023                         lplayer->move(dtime_part, *m_map, position_max_increment,
2024                                         &player_collisions);
2025                 }
2026         }
2027         while(dtime_downcount > 0.001);
2028                 
2029         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2030         
2031         for(core::list<CollisionInfo>::Iterator
2032                         i = player_collisions.begin();
2033                         i != player_collisions.end(); i++)
2034         {
2035                 CollisionInfo &info = *i;
2036                 if(info.t == COLLISION_FALL)
2037                 {
2038                         //f32 tolerance = BS*10; // 2 without damage
2039                         f32 tolerance = BS*12; // 3 without damage
2040                         f32 factor = 1;
2041                         if(info.speed > tolerance)
2042                         {
2043                                 f32 damage_f = (info.speed - tolerance)/BS*factor;
2044                                 u16 damage = (u16)(damage_f+0.5);
2045                                 if(lplayer->hp > damage)
2046                                         lplayer->hp -= damage;
2047                                 else
2048                                         lplayer->hp = 0;
2049
2050                                 ClientEnvEvent event;
2051                                 event.type = CEE_PLAYER_DAMAGE;
2052                                 event.player_damage.amount = damage;
2053                                 m_client_event_queue.push_back(event);
2054                         }
2055                 }
2056         }
2057         
2058         /*
2059                 A quick draft of lava damage
2060         */
2061         if(m_lava_hurt_interval.step(dtime, 1.0))
2062         {
2063                 v3f pf = lplayer->getPosition();
2064                 
2065                 // Feet, middle and head
2066                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2067                 MapNode n1 = m_map->getNodeNoEx(p1);
2068                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2069                 MapNode n2 = m_map->getNodeNoEx(p2);
2070                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2071                 MapNode n3 = m_map->getNodeNoEx(p2);
2072
2073                 u32 damage_per_second = 0;
2074                 damage_per_second = MYMAX(damage_per_second,
2075                                 m_gamedef->ndef()->get(n1).damage_per_second);
2076                 damage_per_second = MYMAX(damage_per_second,
2077                                 m_gamedef->ndef()->get(n2).damage_per_second);
2078                 damage_per_second = MYMAX(damage_per_second,
2079                                 m_gamedef->ndef()->get(n3).damage_per_second);
2080                 
2081                 if(damage_per_second != 0)
2082                 {
2083                         ClientEnvEvent event;
2084                         event.type = CEE_PLAYER_DAMAGE;
2085                         event.player_damage.amount = damage_per_second;
2086                         m_client_event_queue.push_back(event);
2087                 }
2088         }
2089         
2090         /*
2091                 Stuff that can be done in an arbitarily large dtime
2092         */
2093         for(core::list<Player*>::Iterator i = m_players.begin();
2094                         i != m_players.end(); i++)
2095         {
2096                 Player *player = *i;
2097                 v3f playerpos = player->getPosition();
2098                 
2099                 /*
2100                         Handle non-local players
2101                 */
2102                 if(player->isLocal() == false)
2103                 {
2104                         // Move
2105                         player->move(dtime, *m_map, 100*BS);
2106
2107                 }
2108                 
2109                 // Update lighting on all players on client
2110                 u8 light = LIGHT_MAX;
2111                 try{
2112                         // Get node at head
2113                         v3s16 p = player->getLightPosition();
2114                         MapNode n = m_map->getNode(p);
2115                         light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2116                 }
2117                 catch(InvalidPositionException &e) {}
2118                 player->updateLight(light);
2119
2120                 /*
2121                         Add footsteps to grass
2122                 */
2123                 if(footprints)
2124                 {
2125                         // Get node that is at BS/4 under player
2126                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2127                         try{
2128                                 MapNode n = m_map->getNode(bottompos);
2129                                 if(n.getContent() == CONTENT_GRASS)
2130                                 {
2131                                         n.setContent(CONTENT_GRASS_FOOTSTEPS);
2132                                         m_map->setNode(bottompos, n);
2133                                         // Update mesh on client
2134                                         if(m_map->mapType() == MAPTYPE_CLIENT)
2135                                         {
2136                                                 v3s16 p_blocks = getNodeBlockPos(bottompos);
2137                                                 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2138                                                 //b->updateMesh(getDayNightRatio());
2139                                                 b->setMeshExpired(true);
2140                                         }
2141                                 }
2142                         }
2143                         catch(InvalidPositionException &e)
2144                         {
2145                         }
2146                 }
2147         }
2148         
2149         /*
2150                 Step active objects and update lighting of them
2151         */
2152         
2153         for(core::map<u16, ClientActiveObject*>::Iterator
2154                         i = m_active_objects.getIterator();
2155                         i.atEnd()==false; i++)
2156         {
2157                 ClientActiveObject* obj = i.getNode()->getValue();
2158                 // Step object
2159                 obj->step(dtime, this);
2160
2161                 if(m_active_object_light_update_interval.step(dtime, 0.21))
2162                 {
2163                         // Update lighting
2164                         //u8 light = LIGHT_MAX;
2165                         u8 light = 0;
2166                         try{
2167                                 // Get node at head
2168                                 v3s16 p = obj->getLightPosition();
2169                                 MapNode n = m_map->getNode(p);
2170                                 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2171                         }
2172                         catch(InvalidPositionException &e) {}
2173                         obj->updateLight(light);
2174                 }
2175         }
2176 }
2177
2178 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2179 {
2180         m_map->updateMeshes(blockpos, getDayNightRatio());
2181 }
2182
2183 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2184 {
2185         m_map->expireMeshes(only_daynight_diffed);
2186 }
2187
2188 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2189 {
2190         core::map<u16, ClientActiveObject*>::Node *n;
2191         n = m_active_objects.find(id);
2192         if(n == NULL)
2193                 return NULL;
2194         return n->getValue();
2195 }
2196
2197 bool isFreeClientActiveObjectId(u16 id,
2198                 core::map<u16, ClientActiveObject*> &objects)
2199 {
2200         if(id == 0)
2201                 return false;
2202         
2203         for(core::map<u16, ClientActiveObject*>::Iterator
2204                         i = objects.getIterator();
2205                         i.atEnd()==false; i++)
2206         {
2207                 if(i.getNode()->getKey() == id)
2208                         return false;
2209         }
2210         return true;
2211 }
2212
2213 u16 getFreeClientActiveObjectId(
2214                 core::map<u16, ClientActiveObject*> &objects)
2215 {
2216         u16 new_id = 1;
2217         for(;;)
2218         {
2219                 if(isFreeClientActiveObjectId(new_id, objects))
2220                         return new_id;
2221                 
2222                 if(new_id == 65535)
2223                         return 0;
2224
2225                 new_id++;
2226         }
2227 }
2228
2229 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2230 {
2231         assert(object);
2232         if(object->getId() == 0)
2233         {
2234                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2235                 if(new_id == 0)
2236                 {
2237                         infostream<<"ClientEnvironment::addActiveObject(): "
2238                                         <<"no free ids available"<<std::endl;
2239                         delete object;
2240                         return 0;
2241                 }
2242                 object->setId(new_id);
2243         }
2244         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2245         {
2246                 infostream<<"ClientEnvironment::addActiveObject(): "
2247                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2248                 delete object;
2249                 return 0;
2250         }
2251         infostream<<"ClientEnvironment::addActiveObject(): "
2252                         <<"added (id="<<object->getId()<<")"<<std::endl;
2253         m_active_objects.insert(object->getId(), object);
2254         // TODO: Make g_texturesource non-global
2255         object->addToScene(m_smgr, m_texturesource);
2256         return object->getId();
2257 }
2258
2259 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2260                 const std::string &init_data)
2261 {
2262         ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2263         if(obj == NULL)
2264         {
2265                 infostream<<"ClientEnvironment::addActiveObject(): "
2266                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2267                                 <<std::endl;
2268                 return;
2269         }
2270         
2271         obj->setId(id);
2272
2273         obj->initialize(init_data);
2274         
2275         addActiveObject(obj);
2276 }
2277
2278 void ClientEnvironment::removeActiveObject(u16 id)
2279 {
2280         infostream<<"ClientEnvironment::removeActiveObject(): "
2281                         <<"id="<<id<<std::endl;
2282         ClientActiveObject* obj = getActiveObject(id);
2283         if(obj == NULL)
2284         {
2285                 infostream<<"ClientEnvironment::removeActiveObject(): "
2286                                 <<"id="<<id<<" not found"<<std::endl;
2287                 return;
2288         }
2289         obj->removeFromScene();
2290         delete obj;
2291         m_active_objects.remove(id);
2292 }
2293
2294 void ClientEnvironment::processActiveObjectMessage(u16 id,
2295                 const std::string &data)
2296 {
2297         ClientActiveObject* obj = getActiveObject(id);
2298         if(obj == NULL)
2299         {
2300                 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2301                                 <<" got message for id="<<id<<", which doesn't exist."
2302                                 <<std::endl;
2303                 return;
2304         }
2305         obj->processMessage(data);
2306 }
2307
2308 /*
2309         Callbacks for activeobjects
2310 */
2311
2312 void ClientEnvironment::damageLocalPlayer(u8 damage)
2313 {
2314         LocalPlayer *lplayer = getLocalPlayer();
2315         assert(lplayer);
2316
2317         if(lplayer->hp > damage)
2318                 lplayer->hp -= damage;
2319         else
2320                 lplayer->hp = 0;
2321
2322         ClientEnvEvent event;
2323         event.type = CEE_PLAYER_DAMAGE;
2324         event.player_damage.amount = damage;
2325         m_client_event_queue.push_back(event);
2326 }
2327
2328 /*
2329         Client likes to call these
2330 */
2331         
2332 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2333                 core::array<DistanceSortedActiveObject> &dest)
2334 {
2335         for(core::map<u16, ClientActiveObject*>::Iterator
2336                         i = m_active_objects.getIterator();
2337                         i.atEnd()==false; i++)
2338         {
2339                 ClientActiveObject* obj = i.getNode()->getValue();
2340
2341                 f32 d = (obj->getPosition() - origin).getLength();
2342
2343                 if(d > max_d)
2344                         continue;
2345
2346                 DistanceSortedActiveObject dso(obj, d);
2347
2348                 dest.push_back(dso);
2349         }
2350 }
2351
2352 ClientEnvEvent ClientEnvironment::getClientEvent()
2353 {
2354         if(m_client_event_queue.size() == 0)
2355         {
2356                 ClientEnvEvent event;
2357                 event.type = CEE_NONE;
2358                 return event;
2359         }
2360         return m_client_event_queue.pop_front();
2361 }
2362
2363 #endif // #ifndef SERVER
2364
2365