a01bf1037908d6bc905cf040616e7dcc660960a0
[oweals/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->raiseModified(MOD_STATE_WRITE_NEEDED,
587                                                                         "spawnRandomObjects");
588                                                 }
589                                         }
590                                 }
591                         }
592                         last_node_walkable = false;
593                 }
594         }
595 }
596 #endif
597
598 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
599 {
600         // Get time difference
601         u32 dtime_s = 0;
602         u32 stamp = block->getTimestamp();
603         if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
604                 dtime_s = m_game_time - block->getTimestamp();
605         dtime_s += additional_dtime;
606
607         // Set current time as timestamp (and let it set ChangedFlag)
608         block->setTimestamp(m_game_time);
609
610         //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
611         
612         // Activate stored objects
613         activateObjects(block);
614
615         // Run node metadata
616         bool changed = block->m_node_metadata->step((float)dtime_s);
617         if(changed)
618         {
619                 MapEditEvent event;
620                 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
621                 event.p = block->getPos();
622                 m_map->dispatchEvent(&event);
623
624                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
625                                 "node metadata modified in activateBlock");
626         }
627
628         // TODO: Do something
629         // TODO: Implement usage of ActiveBlockModifier
630         
631         // Here's a quick demonstration
632         v3s16 p0;
633         for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
634         for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
635         for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
636         {
637                 v3s16 p = p0 + block->getPosRelative();
638                 MapNode n = block->getNodeNoEx(p0);
639 #if 1
640                 // Test something:
641                 // Convert all mud under proper day lighting to grass
642                 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_MUD"))
643                 {
644                         if(dtime_s > 300)
645                         {
646                                 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
647                                 if(m_gamedef->ndef()->get(n_top).light_propagates &&
648                                                 !m_gamedef->ndef()->get(n_top).isLiquid() &&
649                                                 n_top.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) >= 13)
650                                 {
651                                         n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS"));
652                                         m_map->addNodeWithEvent(p, n);
653                                 }
654                         }
655                 }
656 #endif
657         }
658 }
659
660 void ServerEnvironment::clearAllObjects()
661 {
662         infostream<<"ServerEnvironment::clearAllObjects(): "
663                         <<"Removing all active objects"<<std::endl;
664         core::list<u16> objects_to_remove;
665         for(core::map<u16, ServerActiveObject*>::Iterator
666                         i = m_active_objects.getIterator();
667                         i.atEnd()==false; i++)
668         {
669                 ServerActiveObject* obj = i.getNode()->getValue();
670                 u16 id = i.getNode()->getKey();         
671                 v3f objectpos = obj->getBasePosition(); 
672                 // Delete static object if block is loaded
673                 if(obj->m_static_exists){
674                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
675                         if(block){
676                                 block->m_static_objects.remove(id);
677                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
678                                                 "clearAllObjects");
679                                 obj->m_static_exists = false;
680                         }
681                 }
682                 // If known by some client, don't delete immediately
683                 if(obj->m_known_by_count > 0){
684                         obj->m_pending_deactivation = true;
685                         obj->m_removed = true;
686                         continue;
687                 }
688                 // Deregister in scripting api
689                 scriptapi_rm_object_reference(m_lua, obj);
690                 // Delete active object
691                 delete obj;
692                 // Id to be removed from m_active_objects
693                 objects_to_remove.push_back(id);
694         }
695         // Remove references from m_active_objects
696         for(core::list<u16>::Iterator i = objects_to_remove.begin();
697                         i != objects_to_remove.end(); i++)
698         {
699                 m_active_objects.remove(*i);
700         }
701
702         core::list<v3s16> loadable_blocks;
703         infostream<<"ServerEnvironment::clearAllObjects(): "
704                         <<"Listing all loadable blocks"<<std::endl;
705         m_map->listAllLoadableBlocks(loadable_blocks);
706         infostream<<"ServerEnvironment::clearAllObjects(): "
707                         <<"Done listing all loadable blocks: "
708                         <<loadable_blocks.size()
709                         <<", now clearing"<<std::endl;
710         u32 report_interval = loadable_blocks.size() / 10;
711         u32 num_blocks_checked = 0;
712         u32 num_blocks_cleared = 0;
713         u32 num_objs_cleared = 0;
714         for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
715                         i != loadable_blocks.end(); i++)
716         {
717                 v3s16 p = *i;
718                 MapBlock *block = m_map->emergeBlock(p, false);
719                 if(!block){
720                         errorstream<<"ServerEnvironment::clearAllObjects(): "
721                                         <<"Failed to emerge block "<<PP(p)<<std::endl;
722                         continue;
723                 }
724                 u32 num_stored = block->m_static_objects.m_stored.size();
725                 u32 num_active = block->m_static_objects.m_active.size();
726                 if(num_stored != 0 || num_active != 0){
727                         block->m_static_objects.m_stored.clear();
728                         block->m_static_objects.m_active.clear();
729                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
730                                         "clearAllObjects");
731                         num_objs_cleared += num_stored + num_active;
732                         num_blocks_cleared++;
733                 }
734                 num_blocks_checked++;
735
736                 if(num_blocks_checked % report_interval == 0){
737                         float percent = 100.0 * (float)num_blocks_checked /
738                                         loadable_blocks.size();
739                         infostream<<"ServerEnvironment::clearAllObjects(): "
740                                         <<"Cleared "<<num_objs_cleared<<" objects"
741                                         <<" in "<<num_blocks_cleared<<" blocks ("
742                                         <<percent<<"%)"<<std::endl;
743                 }
744         }
745         infostream<<"ServerEnvironment::clearAllObjects(): "
746                         <<"Finished: Cleared "<<num_objs_cleared<<" objects"
747                         <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
748 }
749
750 static void getMob_dungeon_master(Settings &properties)
751 {
752         properties.set("looks", "dungeon_master");
753         properties.setFloat("yaw", 1.57);
754         properties.setFloat("hp", 30);
755         properties.setBool("bright_shooting", true);
756         properties.set("shoot_type", "fireball");
757         properties.set("shoot_y", "0.7");
758         properties.set("player_hit_damage", "1");
759         properties.set("player_hit_distance", "1.0");
760         properties.set("player_hit_interval", "0.5");
761         properties.setBool("mindless_rage", myrand_range(0,100)==0);
762 }
763
764 void ServerEnvironment::step(float dtime)
765 {
766         DSTACK(__FUNCTION_NAME);
767         
768         //TimeTaker timer("ServerEnv step");
769
770         // Get some settings
771         bool footprints = g_settings->getBool("footprints");
772
773         /*
774                 Increment game time
775         */
776         {
777                 m_game_time_fraction_counter += dtime;
778                 u32 inc_i = (u32)m_game_time_fraction_counter;
779                 m_game_time += inc_i;
780                 m_game_time_fraction_counter -= (float)inc_i;
781         }
782         
783         /*
784                 Handle players
785         */
786         {
787                 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
788                 for(core::list<Player*>::Iterator i = m_players.begin();
789                                 i != m_players.end(); i++)
790                 {
791                         Player *player = *i;
792                         
793                         // Ignore disconnected players
794                         if(player->peer_id == 0)
795                                 continue;
796
797                         v3f playerpos = player->getPosition();
798                         
799                         // Move
800                         player->move(dtime, *m_map, 100*BS);
801                         
802                         /*
803                                 Add footsteps to grass
804                         */
805                         if(footprints)
806                         {
807                                 // Get node that is at BS/4 under player
808                                 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
809                                 try{
810                                         MapNode n = m_map->getNode(bottompos);
811                                         if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
812                                         {
813                                                 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
814                                                 m_map->setNode(bottompos, n);
815                                         }
816                                 }
817                                 catch(InvalidPositionException &e)
818                                 {
819                                 }
820                         }
821                 }
822         }
823
824         /*
825                 Manage active block list
826         */
827         if(m_active_blocks_management_interval.step(dtime, 2.0))
828         {
829                 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
830                 /*
831                         Get player block positions
832                 */
833                 core::list<v3s16> players_blockpos;
834                 for(core::list<Player*>::Iterator
835                                 i = m_players.begin();
836                                 i != m_players.end(); i++)
837                 {
838                         Player *player = *i;
839                         // Ignore disconnected players
840                         if(player->peer_id == 0)
841                                 continue;
842                         v3s16 blockpos = getNodeBlockPos(
843                                         floatToInt(player->getPosition(), BS));
844                         players_blockpos.push_back(blockpos);
845                 }
846                 
847                 /*
848                         Update list of active blocks, collecting changes
849                 */
850                 const s16 active_block_range = g_settings->getS16("active_block_range");
851                 core::map<v3s16, bool> blocks_removed;
852                 core::map<v3s16, bool> blocks_added;
853                 m_active_blocks.update(players_blockpos, active_block_range,
854                                 blocks_removed, blocks_added);
855
856                 /*
857                         Handle removed blocks
858                 */
859
860                 // Convert active objects that are no more in active blocks to static
861                 deactivateFarObjects(false);
862                 
863                 for(core::map<v3s16, bool>::Iterator
864                                 i = blocks_removed.getIterator();
865                                 i.atEnd()==false; i++)
866                 {
867                         v3s16 p = i.getNode()->getKey();
868
869                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
870                                         <<") became inactive"<<std::endl;*/
871                         
872                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
873                         if(block==NULL)
874                                 continue;
875                         
876                         // Set current time as timestamp (and let it set ChangedFlag)
877                         block->setTimestamp(m_game_time);
878                 }
879
880                 /*
881                         Handle added blocks
882                 */
883
884                 for(core::map<v3s16, bool>::Iterator
885                                 i = blocks_added.getIterator();
886                                 i.atEnd()==false; i++)
887                 {
888                         v3s16 p = i.getNode()->getKey();
889                         
890                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
891                                         <<") became active"<<std::endl;*/
892
893                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
894                         if(block==NULL)
895                                 continue;
896
897                         activateBlock(block);
898                 }
899         }
900
901         /*
902                 Mess around in active blocks
903         */
904         if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
905         {
906                 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
907                 
908                 float dtime = 1.0;
909
910                 for(core::map<v3s16, bool>::Iterator
911                                 i = m_active_blocks.m_list.getIterator();
912                                 i.atEnd()==false; i++)
913                 {
914                         v3s16 p = i.getNode()->getKey();
915                         
916                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
917                                         <<") being handled"<<std::endl;*/
918
919                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
920                         if(block==NULL)
921                                 continue;
922
923                         // Reset block usage timer
924                         block->resetUsageTimer();
925                         
926                         // Set current time as timestamp
927                         block->setTimestampNoChangedFlag(m_game_time);
928
929                         // Run node metadata
930                         bool changed = block->m_node_metadata->step(dtime);
931                         if(changed)
932                         {
933                                 MapEditEvent event;
934                                 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
935                                 event.p = p;
936                                 m_map->dispatchEvent(&event);
937
938                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
939                                                 "node metadata modified in step");
940                         }
941                 }
942         }
943         
944         if(m_active_blocks_test_interval.step(dtime, 10.0))
945         {
946                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
947                 //float dtime = 10.0;
948                 TimeTaker timer("modify in active blocks");
949
950                 INodeDefManager *ndef = m_gamedef->ndef();
951                 
952                 // Pre-fetch content ids for the luge loop
953                 content_t c_dirt = ndef->getId("dirt");
954                 content_t c_grass = ndef->getId("dirt_with_grass");
955                 content_t c_tree = ndef->getId("tree");
956                 content_t c_jungletree = ndef->getId("jungletree");
957                 content_t c_stone = ndef->getId("stone");
958                 content_t c_mossycobble = ndef->getId("mossycobble");
959                 content_t c_sapling = ndef->getId("sapling");
960                 
961                 for(core::map<v3s16, bool>::Iterator
962                                 i = m_active_blocks.m_list.getIterator();
963                                 i.atEnd()==false; i++)
964                 {
965                         v3s16 p = i.getNode()->getKey();
966                         
967                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
968                                         <<") being handled"<<std::endl;*/
969
970                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
971                         if(block==NULL)
972                                 continue;
973                         
974                         // Set current time as timestamp
975                         block->setTimestampNoChangedFlag(m_game_time);
976
977                         /*
978                                 Do stuff!
979
980                                 Note that map modifications should be done using the event-
981                                 making map methods so that the server gets information
982                                 about them.
983
984                                 Reading can be done quickly directly from the block.
985
986                                 Everything should bind to inside this single content
987                                 searching loop to keep things fast.
988                         */
989                         // TODO: Implement usage of ActiveBlockModifier
990                         
991                         // Find out how many objects the block contains
992                         //u32 active_object_count = block->m_static_objects.m_active.size();
993                         // Find out how many objects this and all the neighbors contain
994                         u32 active_object_count_wider = 0;
995                         for(s16 x=-1; x<=1; x++)
996                         for(s16 y=-1; y<=1; y++)
997                         for(s16 z=-1; z<=1; z++)
998                         {
999                                 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
1000                                 if(block==NULL)
1001                                         continue;
1002                                 active_object_count_wider +=
1003                                                 block->m_static_objects.m_active.size()
1004                                                 + block->m_static_objects.m_stored.size();
1005                                 
1006                                 /*if(block->m_static_objects.m_stored.size() != 0){
1007                                         errorstream<<"ServerEnvironment::step(): "
1008                                                         <<PP(block->getPos())<<" contains "
1009                                                         <<block->m_static_objects.m_stored.size()
1010                                                         <<" stored objects; "
1011                                                         <<"when spawning objects, when counting active "
1012                                                         <<"objects in wide area. relative position: "
1013                                                         <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
1014                                 }*/
1015                         }
1016
1017                         v3s16 p0;
1018                         for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
1019                         for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
1020                         for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
1021                         {
1022                                 v3s16 p = p0 + block->getPosRelative();
1023                                 MapNode n = block->getNodeNoEx(p0);
1024
1025                                 /*
1026                                         Test something:
1027                                         Convert dirt under proper lighting to grass
1028                                 */
1029                                 if(n.getContent() == c_dirt)
1030                                 {
1031                                         if(myrand()%20 == 0)
1032                                         {
1033                                                 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1034                                                 if(m_gamedef->ndef()->get(n_top).light_propagates &&
1035                                                                 !m_gamedef->ndef()->get(n_top).isLiquid() &&
1036                                                                 n_top.getLightBlend(getDayNightRatio(),
1037                                                                                 m_gamedef->ndef()) >= 13)
1038                                                 {
1039                                                         n.setContent(c_grass);
1040                                                         m_map->addNodeWithEvent(p, n);
1041                                                 }
1042                                         }
1043                                 }
1044                                 /*
1045                                         Convert grass into dirt if under something else than air
1046                                 */
1047                                 if(n.getContent() == c_grass)
1048                                 {
1049                                         //if(myrand()%20 == 0)
1050                                         {
1051                                                 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1052                                                 if(!m_gamedef->ndef()->get(n_top).light_propagates ||
1053                                                                 m_gamedef->ndef()->get(n_top).isLiquid())
1054                                                 {
1055                                                         n.setContent(c_dirt);
1056                                                         m_map->addNodeWithEvent(p, n);
1057                                                 }
1058                                         }
1059                                 }
1060                                 /*
1061                                         Rats spawn around regular trees
1062                                 */
1063                                 if(n.getContent() == c_tree ||
1064                                                 n.getContent() == c_jungletree)
1065                                 {
1066                                         if(myrand()%200 == 0 && active_object_count_wider == 0)
1067                                         {
1068                                                 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1069                                                                 0, myrand_range(-2, 2));
1070                                                 MapNode n1 = m_map->getNodeNoEx(p1);
1071                                                 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1072                                                 if(n1b.getContent() == c_grass &&
1073                                                                 n1.getContent() == CONTENT_AIR)
1074                                                 {
1075                                                         v3f pos = intToFloat(p1, BS);
1076                                                         ServerActiveObject *obj = new RatSAO(this, pos);
1077                                                         addActiveObject(obj);
1078                                                 }
1079                                         }
1080                                 }
1081                                 /*
1082                                         Fun things spawn in caves and dungeons
1083                                 */
1084                                 if(n.getContent() == c_stone ||
1085                                                 n.getContent() == c_mossycobble)
1086                                 {
1087                                         if(myrand()%200 == 0 && active_object_count_wider == 0)
1088                                         {
1089                                                 v3s16 p1 = p + v3s16(0,1,0);
1090                                                 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1091                                                 if(n1a.getLightBlend(getDayNightRatio(),
1092                                                                 m_gamedef->ndef()) <= 3){
1093                                                         MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1094                                                         if(n1a.getContent() == CONTENT_AIR &&
1095                                                                         n1b.getContent() == CONTENT_AIR)
1096                                                         {
1097                                                                 v3f pos = intToFloat(p1, BS);
1098                                                                 int i = myrand()%5;
1099                                                                 if(i == 0 || i == 1){
1100                                                                         actionstream<<"A dungeon master spawns at "
1101                                                                                         <<PP(p1)<<std::endl;
1102                                                                         Settings properties;
1103                                                                         getMob_dungeon_master(properties);
1104                                                                         ServerActiveObject *obj = new MobV2SAO(
1105                                                                                         this, pos, &properties);
1106                                                                         addActiveObject(obj);
1107                                                                 } else if(i == 2 || i == 3){
1108                                                                         actionstream<<"Rats spawn at "
1109                                                                                         <<PP(p1)<<std::endl;
1110                                                                         for(int j=0; j<3; j++){
1111                                                                                 ServerActiveObject *obj = new RatSAO(
1112                                                                                                 this, pos);
1113                                                                                 addActiveObject(obj);
1114                                                                         }
1115                                                                 } else {
1116                                                                         actionstream<<"An oerkki spawns at "
1117                                                                                         <<PP(p1)<<std::endl;
1118                                                                         ServerActiveObject *obj = new Oerkki1SAO(
1119                                                                                         this, pos);
1120                                                                         addActiveObject(obj);
1121                                                                 }
1122                                                         }
1123                                                 }
1124                                         }
1125                                 }
1126                                 /*
1127                                         Make trees from saplings!
1128                                 */
1129                                 if(n.getContent() == c_sapling)
1130                                 {
1131                                         if(myrand()%50 == 0)
1132                                         {
1133                                                 actionstream<<"A sapling grows into a tree at "
1134                                                                 <<PP(p)<<std::endl;
1135
1136                                                 core::map<v3s16, MapBlock*> modified_blocks;
1137                                                 v3s16 tree_p = p;
1138                                                 ManualMapVoxelManipulator vmanip(m_map);
1139                                                 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1140                                                 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1141                                                 bool is_apple_tree = myrand()%4 == 0;
1142                                                 mapgen::make_tree(vmanip, tree_p, is_apple_tree,
1143                                                                 m_gamedef->ndef());
1144                                                 vmanip.blitBackAll(&modified_blocks);
1145
1146                                                 // update lighting
1147                                                 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1148                                                 for(core::map<v3s16, MapBlock*>::Iterator
1149                                                         i = modified_blocks.getIterator();
1150                                                         i.atEnd() == false; i++)
1151                                                 {
1152                                                         lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1153                                                 }
1154                                                 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1155
1156                                                 // Send a MEET_OTHER event
1157                                                 MapEditEvent event;
1158                                                 event.type = MEET_OTHER;
1159                                                 for(core::map<v3s16, MapBlock*>::Iterator
1160                                                         i = modified_blocks.getIterator();
1161                                                         i.atEnd() == false; i++)
1162                                                 {
1163                                                         v3s16 p = i.getNode()->getKey();
1164                                                         event.modified_blocks.insert(p, true);
1165                                                 }
1166                                                 m_map->dispatchEvent(&event);
1167                                         }
1168                                 }
1169                         }
1170                 }
1171
1172                 u32 time_ms = timer.stop(true);
1173                 u32 max_time_ms = 200;
1174                 if(time_ms > max_time_ms){
1175                         infostream<<"WARNING: active block modifiers took "
1176                                         <<time_ms<<"ms (longer than "
1177                                         <<max_time_ms<<"ms)"<<std::endl;
1178                 }
1179         }
1180         
1181         /*
1182                 Step script environment (run global on_step())
1183         */
1184         scriptapi_environment_step(m_lua, dtime);
1185
1186         /*
1187                 Step active objects
1188         */
1189         {
1190                 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1191                 //TimeTaker timer("Step active objects");
1192
1193                 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1194                 
1195                 // This helps the objects to send data at the same time
1196                 bool send_recommended = false;
1197                 m_send_recommended_timer += dtime;
1198                 if(m_send_recommended_timer > getSendRecommendedInterval())
1199                 {
1200                         m_send_recommended_timer -= getSendRecommendedInterval();
1201                         send_recommended = true;
1202                 }
1203
1204                 for(core::map<u16, ServerActiveObject*>::Iterator
1205                                 i = m_active_objects.getIterator();
1206                                 i.atEnd()==false; i++)
1207                 {
1208                         ServerActiveObject* obj = i.getNode()->getValue();
1209                         // Remove non-peaceful mobs on peaceful mode
1210                         if(g_settings->getBool("only_peaceful_mobs")){
1211                                 if(!obj->isPeaceful())
1212                                         obj->m_removed = true;
1213                         }
1214                         // Don't step if is to be removed or stored statically
1215                         if(obj->m_removed || obj->m_pending_deactivation)
1216                                 continue;
1217                         // Step object
1218                         obj->step(dtime, send_recommended);
1219                         // Read messages from object
1220                         while(obj->m_messages_out.size() > 0)
1221                         {
1222                                 m_active_object_messages.push_back(
1223                                                 obj->m_messages_out.pop_front());
1224                         }
1225                 }
1226         }
1227         
1228         /*
1229                 Manage active objects
1230         */
1231         if(m_object_management_interval.step(dtime, 0.5))
1232         {
1233                 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1234                 /*
1235                         Remove objects that satisfy (m_removed && m_known_by_count==0)
1236                 */
1237                 removeRemovedObjects();
1238         }
1239
1240         if(g_settings->getBool("enable_experimental"))
1241         {
1242
1243         /*
1244                 TEST CODE
1245         */
1246 #if 0
1247         m_random_spawn_timer -= dtime;
1248         if(m_random_spawn_timer < 0)
1249         {
1250                 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1251                 //m_random_spawn_timer += 2.0;
1252                 m_random_spawn_timer += 200.0;
1253
1254                 /*
1255                         Find some position
1256                 */
1257
1258                 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1259                 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1260                 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1261                 
1262                 Player *player = getRandomConnectedPlayer();
1263                 v3f pos(0,0,0);
1264                 if(player)
1265                         pos = player->getPosition();
1266                 pos += v3f(
1267                         myrand_range(-3,3)*BS,
1268                         5,
1269                         myrand_range(-3,3)*BS
1270                 );
1271
1272                 /*
1273                         Create a ServerActiveObject
1274                 */
1275
1276                 //TestSAO *obj = new TestSAO(this, pos);
1277                 //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
1278                 //ServerActiveObject *obj = new RatSAO(this, pos);
1279                 //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
1280                 //ServerActiveObject *obj = new FireflySAO(this, pos);
1281
1282                 infostream<<"Server: Spawning MobV2SAO at "
1283                                 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1284                 
1285                 Settings properties;
1286                 getMob_dungeon_master(properties);
1287                 ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
1288                 addActiveObject(obj);
1289         }
1290 #endif
1291
1292         } // enable_experimental
1293 }
1294
1295 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1296 {
1297         core::map<u16, ServerActiveObject*>::Node *n;
1298         n = m_active_objects.find(id);
1299         if(n == NULL)
1300                 return NULL;
1301         return n->getValue();
1302 }
1303
1304 bool isFreeServerActiveObjectId(u16 id,
1305                 core::map<u16, ServerActiveObject*> &objects)
1306 {
1307         if(id == 0)
1308                 return false;
1309         
1310         for(core::map<u16, ServerActiveObject*>::Iterator
1311                         i = objects.getIterator();
1312                         i.atEnd()==false; i++)
1313         {
1314                 if(i.getNode()->getKey() == id)
1315                         return false;
1316         }
1317         return true;
1318 }
1319
1320 u16 getFreeServerActiveObjectId(
1321                 core::map<u16, ServerActiveObject*> &objects)
1322 {
1323         u16 new_id = 1;
1324         for(;;)
1325         {
1326                 if(isFreeServerActiveObjectId(new_id, objects))
1327                         return new_id;
1328                 
1329                 if(new_id == 65535)
1330                         return 0;
1331
1332                 new_id++;
1333         }
1334 }
1335
1336 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1337 {
1338         assert(object);
1339         u16 id = addActiveObjectRaw(object, true);
1340         return id;
1341 }
1342
1343 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1344 {
1345         assert(obj);
1346
1347         v3f objectpos = obj->getBasePosition(); 
1348
1349         // The block in which the object resides in
1350         v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1351
1352         /*
1353                 Update the static data
1354         */
1355
1356         // Create new static object
1357         std::string staticdata = obj->getStaticData();
1358         StaticObject s_obj(obj->getType(), objectpos, staticdata);
1359         // Add to the block where the object is located in
1360         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1361         // Get or generate the block
1362         MapBlock *block = m_map->emergeBlock(blockpos);
1363
1364         bool succeeded = false;
1365
1366         if(block)
1367         {
1368                 block->m_static_objects.insert(0, s_obj);
1369                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1370                                 "addActiveObjectAsStatic");
1371                 succeeded = true;
1372         }
1373         else{
1374                 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1375                                 <<"Could not find or generate "
1376                                 <<"a block for storing static object"<<std::endl;
1377                 succeeded = false;
1378         }
1379
1380         delete obj;
1381
1382         return succeeded;
1383 }
1384
1385 /*
1386         Finds out what new objects have been added to
1387         inside a radius around a position
1388 */
1389 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1390                 core::map<u16, bool> &current_objects,
1391                 core::map<u16, bool> &added_objects)
1392 {
1393         v3f pos_f = intToFloat(pos, BS);
1394         f32 radius_f = radius * BS;
1395         /*
1396                 Go through the object list,
1397                 - discard m_removed objects,
1398                 - discard objects that are too far away,
1399                 - discard objects that are found in current_objects.
1400                 - add remaining objects to added_objects
1401         */
1402         for(core::map<u16, ServerActiveObject*>::Iterator
1403                         i = m_active_objects.getIterator();
1404                         i.atEnd()==false; i++)
1405         {
1406                 u16 id = i.getNode()->getKey();
1407                 // Get object
1408                 ServerActiveObject *object = i.getNode()->getValue();
1409                 if(object == NULL)
1410                         continue;
1411                 // Discard if removed
1412                 if(object->m_removed)
1413                         continue;
1414                 // Discard if too far
1415                 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1416                 if(distance_f > radius_f)
1417                         continue;
1418                 // Discard if already on current_objects
1419                 core::map<u16, bool>::Node *n;
1420                 n = current_objects.find(id);
1421                 if(n != NULL)
1422                         continue;
1423                 // Add to added_objects
1424                 added_objects.insert(id, false);
1425         }
1426 }
1427
1428 /*
1429         Finds out what objects have been removed from
1430         inside a radius around a position
1431 */
1432 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1433                 core::map<u16, bool> &current_objects,
1434                 core::map<u16, bool> &removed_objects)
1435 {
1436         v3f pos_f = intToFloat(pos, BS);
1437         f32 radius_f = radius * BS;
1438         /*
1439                 Go through current_objects; object is removed if:
1440                 - object is not found in m_active_objects (this is actually an
1441                   error condition; objects should be set m_removed=true and removed
1442                   only after all clients have been informed about removal), or
1443                 - object has m_removed=true, or
1444                 - object is too far away
1445         */
1446         for(core::map<u16, bool>::Iterator
1447                         i = current_objects.getIterator();
1448                         i.atEnd()==false; i++)
1449         {
1450                 u16 id = i.getNode()->getKey();
1451                 ServerActiveObject *object = getActiveObject(id);
1452                 if(object == NULL)
1453                 {
1454                         infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1455                                         <<" object in current_objects is NULL"<<std::endl;
1456                 }
1457                 else if(object->m_removed == false)
1458                 {
1459                         f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1460                         /*infostream<<"removed == false"
1461                                         <<"distance_f = "<<distance_f
1462                                         <<", radius_f = "<<radius_f<<std::endl;*/
1463                         if(distance_f < radius_f)
1464                         {
1465                                 // Not removed
1466                                 continue;
1467                         }
1468                 }
1469                 removed_objects.insert(id, false);
1470         }
1471 }
1472
1473 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1474 {
1475         if(m_active_object_messages.size() == 0)
1476                 return ActiveObjectMessage(0);
1477         
1478         return m_active_object_messages.pop_front();
1479 }
1480
1481 /*
1482         ************ Private methods *************
1483 */
1484
1485 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1486                 bool set_changed)
1487 {
1488         assert(object);
1489         if(object->getId() == 0){
1490                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1491                 if(new_id == 0)
1492                 {
1493                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1494                                         <<"no free ids available"<<std::endl;
1495                         delete object;
1496                         return 0;
1497                 }
1498                 object->setId(new_id);
1499         }
1500         else{
1501                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1502                                 <<"supplied with id "<<object->getId()<<std::endl;
1503         }
1504         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1505         {
1506                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1507                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1508                 delete object;
1509                 return 0;
1510         }
1511         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1512                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1513                         
1514         m_active_objects.insert(object->getId(), object);
1515   
1516         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1517                         <<"Added id="<<object->getId()<<"; there are now "
1518                         <<m_active_objects.size()<<" active objects."
1519                         <<std::endl;
1520         
1521         // Add static object to active static list of the block
1522         v3f objectpos = object->getBasePosition();
1523         std::string staticdata = object->getStaticData();
1524         StaticObject s_obj(object->getType(), objectpos, staticdata);
1525         // Add to the block where the object is located in
1526         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1527         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1528         if(block)
1529         {
1530                 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1531                 object->m_static_exists = true;
1532                 object->m_static_block = blockpos;
1533
1534                 if(set_changed)
1535                         block->raiseModified(MOD_STATE_WRITE_NEEDED, 
1536                                         "addActiveObjectRaw");
1537         }
1538         else{
1539                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1540                                 <<"could not find block for storing id="<<object->getId()
1541                                 <<" statically"<<std::endl;
1542         }
1543         
1544         // Register reference in scripting api (must be done before post-init)
1545         scriptapi_add_object_reference(m_lua, object);
1546         // Post-initialize object
1547         object->addedToEnvironment();
1548
1549         return object->getId();
1550 }
1551
1552 /*
1553         Remove objects that satisfy (m_removed && m_known_by_count==0)
1554 */
1555 void ServerEnvironment::removeRemovedObjects()
1556 {
1557         core::list<u16> objects_to_remove;
1558         for(core::map<u16, ServerActiveObject*>::Iterator
1559                         i = m_active_objects.getIterator();
1560                         i.atEnd()==false; i++)
1561         {
1562                 u16 id = i.getNode()->getKey();
1563                 ServerActiveObject* obj = i.getNode()->getValue();
1564                 // This shouldn't happen but check it
1565                 if(obj == NULL)
1566                 {
1567                         infostream<<"NULL object found in ServerEnvironment"
1568                                         <<" while finding removed objects. id="<<id<<std::endl;
1569                         // Id to be removed from m_active_objects
1570                         objects_to_remove.push_back(id);
1571                         continue;
1572                 }
1573
1574                 /*
1575                         We will delete objects that are marked as removed or thatare
1576                         waiting for deletion after deactivation
1577                 */
1578                 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1579                         continue;
1580
1581                 /*
1582                         Delete static data from block if is marked as removed
1583                 */
1584                 if(obj->m_static_exists && obj->m_removed)
1585                 {
1586                         MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1587                         if(block)
1588                         {
1589                                 block->m_static_objects.remove(id);
1590                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1591                                                 "removeRemovedObjects");
1592                                 obj->m_static_exists = false;
1593                         }
1594                 }
1595
1596                 // If m_known_by_count > 0, don't actually remove.
1597                 if(obj->m_known_by_count > 0)
1598                         continue;
1599                 
1600                 // Deregister in scripting api
1601                 scriptapi_rm_object_reference(m_lua, obj);
1602
1603                 // Delete
1604                 delete obj;
1605                 // Id to be removed from m_active_objects
1606                 objects_to_remove.push_back(id);
1607         }
1608         // Remove references from m_active_objects
1609         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1610                         i != objects_to_remove.end(); i++)
1611         {
1612                 m_active_objects.remove(*i);
1613         }
1614 }
1615
1616 static void print_hexdump(std::ostream &o, const std::string &data)
1617 {
1618         const int linelength = 16;
1619         for(int l=0; ; l++){
1620                 int i0 = linelength * l;
1621                 bool at_end = false;
1622                 int thislinelength = linelength;
1623                 if(i0 + thislinelength > (int)data.size()){
1624                         thislinelength = data.size() - i0;
1625                         at_end = true;
1626                 }
1627                 for(int di=0; di<linelength; di++){
1628                         int i = i0 + di;
1629                         char buf[4];
1630                         if(di<thislinelength)
1631                                 snprintf(buf, 4, "%.2x ", data[i]);
1632                         else
1633                                 snprintf(buf, 4, "   ");
1634                         o<<buf;
1635                 }
1636                 o<<" ";
1637                 for(int di=0; di<thislinelength; di++){
1638                         int i = i0 + di;
1639                         if(data[i] >= 32)
1640                                 o<<data[i];
1641                         else
1642                                 o<<".";
1643                 }
1644                 o<<std::endl;
1645                 if(at_end)
1646                         break;
1647         }
1648 }
1649
1650 /*
1651         Convert stored objects from blocks near the players to active.
1652 */
1653 void ServerEnvironment::activateObjects(MapBlock *block)
1654 {
1655         if(block==NULL)
1656                 return;
1657         // Ignore if no stored objects (to not set changed flag)
1658         if(block->m_static_objects.m_stored.size() == 0)
1659                 return;
1660         verbosestream<<"ServerEnvironment::activateObjects(): "
1661                         <<"activating objects of block "<<PP(block->getPos())
1662                         <<" ("<<block->m_static_objects.m_stored.size()
1663                         <<" objects)"<<std::endl;
1664         bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1665         if(large_amount){
1666                 errorstream<<"suspiciously large amount of objects detected: "
1667                                 <<block->m_static_objects.m_stored.size()<<" in "
1668                                 <<PP(block->getPos())
1669                                 <<"; removing all of them."<<std::endl;
1670                 // Clear stored list
1671                 block->m_static_objects.m_stored.clear();
1672                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1673                                 "stored list cleared in activateObjects due to "
1674                                 "large amount of objects");
1675                 return;
1676         }
1677         // A list for objects that couldn't be converted to static for some
1678         // reason. They will be stored back.
1679         core::list<StaticObject> new_stored;
1680         // Loop through stored static objects
1681         for(core::list<StaticObject>::Iterator
1682                         i = block->m_static_objects.m_stored.begin();
1683                         i != block->m_static_objects.m_stored.end(); i++)
1684         {
1685                 /*infostream<<"Server: Creating an active object from "
1686                                 <<"static data"<<std::endl;*/
1687                 StaticObject &s_obj = *i;
1688                 // Create an active object from the data
1689                 ServerActiveObject *obj = ServerActiveObject::create
1690                                 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1691                 // If couldn't create object, store static data back.
1692                 if(obj==NULL)
1693                 {
1694                         errorstream<<"ServerEnvironment::activateObjects(): "
1695                                         <<"failed to create active object from static object "
1696                                         <<"in block "<<PP(s_obj.pos/BS)
1697                                         <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1698                         print_hexdump(verbosestream, s_obj.data);
1699                         
1700                         new_stored.push_back(s_obj);
1701                         continue;
1702                 }
1703                 verbosestream<<"ServerEnvironment::activateObjects(): "
1704                                 <<"activated static object pos="<<PP(s_obj.pos/BS)
1705                                 <<" type="<<(int)s_obj.type<<std::endl;
1706                 // This will also add the object to the active static list
1707                 addActiveObjectRaw(obj, false);
1708         }
1709         // Clear stored list
1710         block->m_static_objects.m_stored.clear();
1711         // Add leftover failed stuff to stored list
1712         for(core::list<StaticObject>::Iterator
1713                         i = new_stored.begin();
1714                         i != new_stored.end(); i++)
1715         {
1716                 StaticObject &s_obj = *i;
1717                 block->m_static_objects.m_stored.push_back(s_obj);
1718         }
1719         /*
1720                 Note: Block hasn't really been modified here.
1721                 The objects have just been activated and moved from the stored
1722                 static list to the active static list.
1723                 As such, the block is essentially the same.
1724                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1725                 Otherwise there would be a huge amount of unnecessary I/O.
1726         */
1727 }
1728
1729 /*
1730         Convert objects that are not standing inside active blocks to static.
1731
1732         If m_known_by_count != 0, active object is not deleted, but static
1733         data is still updated.
1734
1735         If force_delete is set, active object is deleted nevertheless. It
1736         shall only be set so in the destructor of the environment.
1737 */
1738 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1739 {
1740         core::list<u16> objects_to_remove;
1741         for(core::map<u16, ServerActiveObject*>::Iterator
1742                         i = m_active_objects.getIterator();
1743                         i.atEnd()==false; i++)
1744         {
1745                 ServerActiveObject* obj = i.getNode()->getValue();
1746
1747                 // This shouldn't happen but check it
1748                 if(obj == NULL)
1749                 {
1750                         errorstream<<"NULL object found in ServerEnvironment"
1751                                         <<std::endl;
1752                         assert(0);
1753                         continue;
1754                 }
1755
1756                 // If pending deactivation, let removeRemovedObjects() do it
1757                 if(obj->m_pending_deactivation)
1758                         continue;
1759
1760                 u16 id = i.getNode()->getKey();         
1761                 v3f objectpos = obj->getBasePosition(); 
1762
1763                 // The block in which the object resides in
1764                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1765
1766                 // If block is active, don't remove
1767                 if(m_active_blocks.contains(blockpos_o))
1768                         continue;
1769
1770                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1771                                 <<"deactivating object id="<<id<<" on inactive block "
1772                                 <<PP(blockpos_o)<<std::endl;
1773
1774                 // If known by some client, don't immediately delete.
1775                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1776
1777                 /*
1778                         Update the static data
1779                 */
1780
1781                 // Create new static object
1782                 std::string staticdata_new = obj->getStaticData();
1783                 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1784                 
1785                 bool stays_in_same_block = false;
1786                 bool data_changed = true;
1787
1788                 if(obj->m_static_exists){
1789                         if(obj->m_static_block == blockpos_o)
1790                                 stays_in_same_block = true;
1791
1792                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1793                         
1794                         core::map<u16, StaticObject>::Node *n =
1795                                         block->m_static_objects.m_active.find(id);
1796                         if(n){
1797                                 StaticObject static_old = n->getValue();
1798
1799                                 float save_movem = obj->getMinimumSavedMovement();
1800
1801                                 if(static_old.data == staticdata_new &&
1802                                                 (static_old.pos - objectpos).getLength() < save_movem)
1803                                         data_changed = false;
1804                         } else {
1805                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1806                                                 <<"id="<<id<<" m_static_exists=true but "
1807                                                 <<"static data doesn't actually exist in "
1808                                                 <<PP(obj->m_static_block)<<std::endl;
1809                         }
1810                 }
1811
1812                 bool shall_be_written = (!stays_in_same_block || data_changed);
1813                 
1814                 // Delete old static object
1815                 if(obj->m_static_exists)
1816                 {
1817                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1818                         if(block)
1819                         {
1820                                 block->m_static_objects.remove(id);
1821                                 obj->m_static_exists = false;
1822                                 // Only mark block as modified if data changed considerably
1823                                 if(shall_be_written)
1824                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1825                                                         "deactivateFarObjects: Static data "
1826                                                         "changed considerably");
1827                         }
1828                 }
1829
1830                 // Add to the block where the object is located in
1831                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1832                 // Get or generate the block
1833                 MapBlock *block = m_map->emergeBlock(blockpos);
1834
1835                 if(block)
1836                 {
1837                         if(block->m_static_objects.m_stored.size() >= 49){
1838                                 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1839                                                 <<" statically but block "<<PP(blockpos)
1840                                                 <<" already contains "
1841                                                 <<block->m_static_objects.m_stored.size()
1842                                                 <<" (over 49) objects."
1843                                                 <<" Forcing delete."<<std::endl;
1844                                 force_delete = true;
1845                         } else {
1846                                 u16 new_id = pending_delete ? id : 0;
1847                                 block->m_static_objects.insert(new_id, s_obj);
1848                                 
1849                                 // Only mark block as modified if data changed considerably
1850                                 if(shall_be_written)
1851                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1852                                                         "deactivateFarObjects: Static data "
1853                                                         "changed considerably");
1854                                 
1855                                 obj->m_static_exists = true;
1856                                 obj->m_static_block = block->getPos();
1857                         }
1858                 }
1859                 else{
1860                         errorstream<<"ServerEnv: Could not find or generate "
1861                                         <<"a block for storing id="<<obj->getId()
1862                                         <<" statically"<<std::endl;
1863                         continue;
1864                 }
1865
1866                 /*
1867                         If known by some client, set pending deactivation.
1868                         Otherwise delete it immediately.
1869                 */
1870
1871                 if(pending_delete)
1872                 {
1873                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1874                                         <<"object id="<<id<<" is known by clients"
1875                                         <<"; not deleting yet"<<std::endl;
1876
1877                         obj->m_pending_deactivation = true;
1878                         continue;
1879                 }
1880                 
1881                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1882                                 <<"object id="<<id<<" is not known by clients"
1883                                 <<"; deleting"<<std::endl;
1884
1885                 // Deregister in scripting api
1886                 scriptapi_rm_object_reference(m_lua, obj);
1887
1888                 // Delete active object
1889                 delete obj;
1890                 // Id to be removed from m_active_objects
1891                 objects_to_remove.push_back(id);
1892         }
1893
1894         // Remove references from m_active_objects
1895         for(core::list<u16>::Iterator i = objects_to_remove.begin();
1896                         i != objects_to_remove.end(); i++)
1897         {
1898                 m_active_objects.remove(*i);
1899         }
1900 }
1901
1902
1903 #ifndef SERVER
1904
1905 /*
1906         ClientEnvironment
1907 */
1908
1909 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1910                 ITextureSource *texturesource, IGameDef *gamedef):
1911         m_map(map),
1912         m_smgr(smgr),
1913         m_texturesource(texturesource),
1914         m_gamedef(gamedef)
1915 {
1916         assert(m_map);
1917         assert(m_smgr);
1918 }
1919
1920 ClientEnvironment::~ClientEnvironment()
1921 {
1922         // delete active objects
1923         for(core::map<u16, ClientActiveObject*>::Iterator
1924                         i = m_active_objects.getIterator();
1925                         i.atEnd()==false; i++)
1926         {
1927                 delete i.getNode()->getValue();
1928         }
1929
1930         // Drop/delete map
1931         m_map->drop();
1932 }
1933
1934 void ClientEnvironment::addPlayer(Player *player)
1935 {
1936         DSTACK(__FUNCTION_NAME);
1937         /*
1938                 It is a failure if player is local and there already is a local
1939                 player
1940         */
1941         assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1942
1943         Environment::addPlayer(player);
1944 }
1945
1946 LocalPlayer * ClientEnvironment::getLocalPlayer()
1947 {
1948         for(core::list<Player*>::Iterator i = m_players.begin();
1949                         i != m_players.end(); i++)
1950         {
1951                 Player *player = *i;
1952                 if(player->isLocal())
1953                         return (LocalPlayer*)player;
1954         }
1955         return NULL;
1956 }
1957
1958 void ClientEnvironment::step(float dtime)
1959 {
1960         DSTACK(__FUNCTION_NAME);
1961
1962         // Get some settings
1963         bool free_move = g_settings->getBool("free_move");
1964         bool footprints = g_settings->getBool("footprints");
1965
1966         // Get local player
1967         LocalPlayer *lplayer = getLocalPlayer();
1968         assert(lplayer);
1969         // collision info queue
1970         core::list<CollisionInfo> player_collisions;
1971         
1972         /*
1973                 Get the speed the player is going
1974         */
1975         bool is_climbing = lplayer->is_climbing;
1976         
1977         f32 player_speed = 0.001; // just some small value
1978         player_speed = lplayer->getSpeed().getLength();
1979         
1980         /*
1981                 Maximum position increment
1982         */
1983         //f32 position_max_increment = 0.05*BS;
1984         f32 position_max_increment = 0.1*BS;
1985
1986         // Maximum time increment (for collision detection etc)
1987         // time = distance / speed
1988         f32 dtime_max_increment = position_max_increment / player_speed;
1989         
1990         // Maximum time increment is 10ms or lower
1991         if(dtime_max_increment > 0.01)
1992                 dtime_max_increment = 0.01;
1993         
1994         // Don't allow overly huge dtime
1995         if(dtime > 0.5)
1996                 dtime = 0.5;
1997         
1998         f32 dtime_downcount = dtime;
1999
2000         /*
2001                 Stuff that has a maximum time increment
2002         */
2003
2004         u32 loopcount = 0;
2005         do
2006         {
2007                 loopcount++;
2008
2009                 f32 dtime_part;
2010                 if(dtime_downcount > dtime_max_increment)
2011                 {
2012                         dtime_part = dtime_max_increment;
2013                         dtime_downcount -= dtime_part;
2014                 }
2015                 else
2016                 {
2017                         dtime_part = dtime_downcount;
2018                         /*
2019                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
2020                                 when dtime_part is so small that dtime_downcount -= dtime_part
2021                                 does nothing
2022                         */
2023                         dtime_downcount = 0;
2024                 }
2025                 
2026                 /*
2027                         Handle local player
2028                 */
2029                 
2030                 {
2031                         v3f lplayerpos = lplayer->getPosition();
2032                         
2033                         // Apply physics
2034                         if(free_move == false && is_climbing == false)
2035                         {
2036                                 // Gravity
2037                                 v3f speed = lplayer->getSpeed();
2038                                 if(lplayer->swimming_up == false)
2039                                         speed.Y -= 9.81 * BS * dtime_part * 2;
2040
2041                                 // Water resistance
2042                                 if(lplayer->in_water_stable || lplayer->in_water)
2043                                 {
2044                                         f32 max_down = 2.0*BS;
2045                                         if(speed.Y < -max_down) speed.Y = -max_down;
2046
2047                                         f32 max = 2.5*BS;
2048                                         if(speed.getLength() > max)
2049                                         {
2050                                                 speed = speed / speed.getLength() * max;
2051                                         }
2052                                 }
2053
2054                                 lplayer->setSpeed(speed);
2055                         }
2056
2057                         /*
2058                                 Move the lplayer.
2059                                 This also does collision detection.
2060                         */
2061                         lplayer->move(dtime_part, *m_map, position_max_increment,
2062                                         &player_collisions);
2063                 }
2064         }
2065         while(dtime_downcount > 0.001);
2066                 
2067         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2068         
2069         for(core::list<CollisionInfo>::Iterator
2070                         i = player_collisions.begin();
2071                         i != player_collisions.end(); i++)
2072         {
2073                 CollisionInfo &info = *i;
2074                 if(info.t == COLLISION_FALL)
2075                 {
2076                         //f32 tolerance = BS*10; // 2 without damage
2077                         f32 tolerance = BS*12; // 3 without damage
2078                         f32 factor = 1;
2079                         if(info.speed > tolerance)
2080                         {
2081                                 f32 damage_f = (info.speed - tolerance)/BS*factor;
2082                                 u16 damage = (u16)(damage_f+0.5);
2083                                 if(lplayer->hp > damage)
2084                                         lplayer->hp -= damage;
2085                                 else
2086                                         lplayer->hp = 0;
2087
2088                                 ClientEnvEvent event;
2089                                 event.type = CEE_PLAYER_DAMAGE;
2090                                 event.player_damage.amount = damage;
2091                                 m_client_event_queue.push_back(event);
2092                         }
2093                 }
2094         }
2095         
2096         /*
2097                 A quick draft of lava damage
2098         */
2099         if(m_lava_hurt_interval.step(dtime, 1.0))
2100         {
2101                 v3f pf = lplayer->getPosition();
2102                 
2103                 // Feet, middle and head
2104                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2105                 MapNode n1 = m_map->getNodeNoEx(p1);
2106                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2107                 MapNode n2 = m_map->getNodeNoEx(p2);
2108                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2109                 MapNode n3 = m_map->getNodeNoEx(p2);
2110
2111                 u32 damage_per_second = 0;
2112                 damage_per_second = MYMAX(damage_per_second,
2113                                 m_gamedef->ndef()->get(n1).damage_per_second);
2114                 damage_per_second = MYMAX(damage_per_second,
2115                                 m_gamedef->ndef()->get(n2).damage_per_second);
2116                 damage_per_second = MYMAX(damage_per_second,
2117                                 m_gamedef->ndef()->get(n3).damage_per_second);
2118                 
2119                 if(damage_per_second != 0)
2120                 {
2121                         ClientEnvEvent event;
2122                         event.type = CEE_PLAYER_DAMAGE;
2123                         event.player_damage.amount = damage_per_second;
2124                         m_client_event_queue.push_back(event);
2125                 }
2126         }
2127         
2128         /*
2129                 Stuff that can be done in an arbitarily large dtime
2130         */
2131         for(core::list<Player*>::Iterator i = m_players.begin();
2132                         i != m_players.end(); i++)
2133         {
2134                 Player *player = *i;
2135                 v3f playerpos = player->getPosition();
2136                 
2137                 /*
2138                         Handle non-local players
2139                 */
2140                 if(player->isLocal() == false)
2141                 {
2142                         // Move
2143                         player->move(dtime, *m_map, 100*BS);
2144
2145                 }
2146                 
2147                 // Update lighting on all players on client
2148                 u8 light = LIGHT_MAX;
2149                 try{
2150                         // Get node at head
2151                         v3s16 p = player->getLightPosition();
2152                         MapNode n = m_map->getNode(p);
2153                         light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2154                 }
2155                 catch(InvalidPositionException &e) {}
2156                 player->updateLight(light);
2157
2158                 /*
2159                         Add footsteps to grass
2160                 */
2161                 if(footprints)
2162                 {
2163                         // Get node that is at BS/4 under player
2164                         v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2165                         try{
2166                                 MapNode n = m_map->getNode(bottompos);
2167                                 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
2168                                 {
2169                                         n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
2170                                         m_map->setNode(bottompos, n);
2171                                         // Update mesh on client
2172                                         if(m_map->mapType() == MAPTYPE_CLIENT)
2173                                         {
2174                                                 v3s16 p_blocks = getNodeBlockPos(bottompos);
2175                                                 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2176                                                 //b->updateMesh(getDayNightRatio());
2177                                                 b->setMeshExpired(true);
2178                                         }
2179                                 }
2180                         }
2181                         catch(InvalidPositionException &e)
2182                         {
2183                         }
2184                 }
2185         }
2186         
2187         /*
2188                 Step active objects and update lighting of them
2189         */
2190         
2191         for(core::map<u16, ClientActiveObject*>::Iterator
2192                         i = m_active_objects.getIterator();
2193                         i.atEnd()==false; i++)
2194         {
2195                 ClientActiveObject* obj = i.getNode()->getValue();
2196                 // Step object
2197                 obj->step(dtime, this);
2198
2199                 if(m_active_object_light_update_interval.step(dtime, 0.21))
2200                 {
2201                         // Update lighting
2202                         //u8 light = LIGHT_MAX;
2203                         u8 light = 0;
2204                         try{
2205                                 // Get node at head
2206                                 v3s16 p = obj->getLightPosition();
2207                                 MapNode n = m_map->getNode(p);
2208                                 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2209                         }
2210                         catch(InvalidPositionException &e) {}
2211                         obj->updateLight(light);
2212                 }
2213         }
2214 }
2215
2216 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2217 {
2218         m_map->updateMeshes(blockpos, getDayNightRatio());
2219 }
2220
2221 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2222 {
2223         m_map->expireMeshes(only_daynight_diffed);
2224 }
2225
2226 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2227 {
2228         core::map<u16, ClientActiveObject*>::Node *n;
2229         n = m_active_objects.find(id);
2230         if(n == NULL)
2231                 return NULL;
2232         return n->getValue();
2233 }
2234
2235 bool isFreeClientActiveObjectId(u16 id,
2236                 core::map<u16, ClientActiveObject*> &objects)
2237 {
2238         if(id == 0)
2239                 return false;
2240         
2241         for(core::map<u16, ClientActiveObject*>::Iterator
2242                         i = objects.getIterator();
2243                         i.atEnd()==false; i++)
2244         {
2245                 if(i.getNode()->getKey() == id)
2246                         return false;
2247         }
2248         return true;
2249 }
2250
2251 u16 getFreeClientActiveObjectId(
2252                 core::map<u16, ClientActiveObject*> &objects)
2253 {
2254         u16 new_id = 1;
2255         for(;;)
2256         {
2257                 if(isFreeClientActiveObjectId(new_id, objects))
2258                         return new_id;
2259                 
2260                 if(new_id == 65535)
2261                         return 0;
2262
2263                 new_id++;
2264         }
2265 }
2266
2267 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2268 {
2269         assert(object);
2270         if(object->getId() == 0)
2271         {
2272                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2273                 if(new_id == 0)
2274                 {
2275                         infostream<<"ClientEnvironment::addActiveObject(): "
2276                                         <<"no free ids available"<<std::endl;
2277                         delete object;
2278                         return 0;
2279                 }
2280                 object->setId(new_id);
2281         }
2282         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2283         {
2284                 infostream<<"ClientEnvironment::addActiveObject(): "
2285                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2286                 delete object;
2287                 return 0;
2288         }
2289         infostream<<"ClientEnvironment::addActiveObject(): "
2290                         <<"added (id="<<object->getId()<<")"<<std::endl;
2291         m_active_objects.insert(object->getId(), object);
2292         // TODO: Make g_texturesource non-global
2293         object->addToScene(m_smgr, m_texturesource);
2294         return object->getId();
2295 }
2296
2297 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2298                 const std::string &init_data)
2299 {
2300         ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2301         if(obj == NULL)
2302         {
2303                 infostream<<"ClientEnvironment::addActiveObject(): "
2304                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2305                                 <<std::endl;
2306                 return;
2307         }
2308         
2309         obj->setId(id);
2310
2311         obj->initialize(init_data);
2312         
2313         addActiveObject(obj);
2314 }
2315
2316 void ClientEnvironment::removeActiveObject(u16 id)
2317 {
2318         infostream<<"ClientEnvironment::removeActiveObject(): "
2319                         <<"id="<<id<<std::endl;
2320         ClientActiveObject* obj = getActiveObject(id);
2321         if(obj == NULL)
2322         {
2323                 infostream<<"ClientEnvironment::removeActiveObject(): "
2324                                 <<"id="<<id<<" not found"<<std::endl;
2325                 return;
2326         }
2327         obj->removeFromScene();
2328         delete obj;
2329         m_active_objects.remove(id);
2330 }
2331
2332 void ClientEnvironment::processActiveObjectMessage(u16 id,
2333                 const std::string &data)
2334 {
2335         ClientActiveObject* obj = getActiveObject(id);
2336         if(obj == NULL)
2337         {
2338                 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2339                                 <<" got message for id="<<id<<", which doesn't exist."
2340                                 <<std::endl;
2341                 return;
2342         }
2343         obj->processMessage(data);
2344 }
2345
2346 /*
2347         Callbacks for activeobjects
2348 */
2349
2350 void ClientEnvironment::damageLocalPlayer(u8 damage)
2351 {
2352         LocalPlayer *lplayer = getLocalPlayer();
2353         assert(lplayer);
2354
2355         if(lplayer->hp > damage)
2356                 lplayer->hp -= damage;
2357         else
2358                 lplayer->hp = 0;
2359
2360         ClientEnvEvent event;
2361         event.type = CEE_PLAYER_DAMAGE;
2362         event.player_damage.amount = damage;
2363         m_client_event_queue.push_back(event);
2364 }
2365
2366 /*
2367         Client likes to call these
2368 */
2369         
2370 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2371                 core::array<DistanceSortedActiveObject> &dest)
2372 {
2373         for(core::map<u16, ClientActiveObject*>::Iterator
2374                         i = m_active_objects.getIterator();
2375                         i.atEnd()==false; i++)
2376         {
2377                 ClientActiveObject* obj = i.getNode()->getValue();
2378
2379                 f32 d = (obj->getPosition() - origin).getLength();
2380
2381                 if(d > max_d)
2382                         continue;
2383
2384                 DistanceSortedActiveObject dso(obj, d);
2385
2386                 dest.push_back(dso);
2387         }
2388 }
2389
2390 ClientEnvEvent ClientEnvironment::getClientEvent()
2391 {
2392         if(m_client_event_queue.size() == 0)
2393         {
2394                 ClientEnvEvent event;
2395                 event.type = CEE_NONE;
2396                 return event;
2397         }
2398         return m_client_event_queue.pop_front();
2399 }
2400
2401 #endif // #ifndef SERVER
2402
2403