3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
30 Environment::Environment():
35 Environment::~Environment()
38 for(core::list<Player*>::Iterator i = m_players.begin();
39 i != m_players.end(); i++)
45 void Environment::addPlayer(Player *player)
47 DSTACK(__FUNCTION_NAME);
49 Check that peer_ids are unique.
50 Also check that names are unique.
51 Exception: there can be multiple players with peer_id=0
53 // If peer id is non-zero, it has to be unique.
54 if(player->peer_id != 0)
55 assert(getPlayer(player->peer_id) == NULL);
56 // Name has to be unique.
57 assert(getPlayer(player->getName()) == NULL);
59 m_players.push_back(player);
62 void Environment::removePlayer(u16 peer_id)
64 DSTACK(__FUNCTION_NAME);
66 for(core::list<Player*>::Iterator i = m_players.begin();
67 i != m_players.end(); i++)
70 if(player->peer_id != peer_id)
75 // See if there is an another one
76 // (shouldn't be, but just to be sure)
81 Player * Environment::getPlayer(u16 peer_id)
83 for(core::list<Player*>::Iterator i = m_players.begin();
84 i != m_players.end(); i++)
87 if(player->peer_id == peer_id)
93 Player * Environment::getPlayer(const char *name)
95 for(core::list<Player*>::Iterator i = m_players.begin();
96 i != m_players.end(); i++)
99 if(strcmp(player->getName(), name) == 0)
105 Player * Environment::getRandomConnectedPlayer()
107 core::list<Player*> connected_players = getPlayers(true);
108 u32 chosen_one = myrand() % connected_players.size();
110 for(core::list<Player*>::Iterator
111 i = connected_players.begin();
112 i != connected_players.end(); i++)
124 Player * Environment::getNearestConnectedPlayer(v3f pos)
126 core::list<Player*> connected_players = getPlayers(true);
128 Player *nearest_player = NULL;
129 for(core::list<Player*>::Iterator
130 i = connected_players.begin();
131 i != connected_players.end(); i++)
134 f32 d = player->getPosition().getDistanceFrom(pos);
135 if(d < nearest_d || nearest_player == NULL)
138 nearest_player = player;
141 return nearest_player;
144 core::list<Player*> Environment::getPlayers()
149 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
151 core::list<Player*> newlist;
152 for(core::list<Player*>::Iterator
153 i = m_players.begin();
154 i != m_players.end(); i++)
158 if(ignore_disconnected)
160 // Ignore disconnected players
161 if(player->peer_id == 0)
165 newlist.push_back(player);
170 void Environment::printPlayers(std::ostream &o)
172 o<<"Players in environment:"<<std::endl;
173 for(core::list<Player*>::Iterator i = m_players.begin();
174 i != m_players.end(); i++)
177 o<<"Player peer_id="<<player->peer_id<<std::endl;
181 /*void Environment::setDayNightRatio(u32 r)
183 getDayNightRatio() = r;
186 u32 Environment::getDayNightRatio()
188 //return getDayNightRatio();
189 return time_to_daynight_ratio(m_time_of_day);
196 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
199 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
200 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
201 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
208 void ActiveBlockList::update(core::list<v3s16> &active_positions,
210 core::map<v3s16, bool> &blocks_removed,
211 core::map<v3s16, bool> &blocks_added)
216 core::map<v3s16, bool> newlist;
217 for(core::list<v3s16>::Iterator i = active_positions.begin();
218 i != active_positions.end(); i++)
220 fillRadiusBlock(*i, radius, newlist);
224 Find out which blocks on the old list are not on the new list
226 // Go through old list
227 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
228 i.atEnd()==false; i++)
230 v3s16 p = i.getNode()->getKey();
231 // If not on new list, it's been removed
232 if(newlist.find(p) == NULL)
233 blocks_removed.insert(p, true);
237 Find out which blocks on the new list are not on the old list
239 // Go through new list
240 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
241 i.atEnd()==false; i++)
243 v3s16 p = i.getNode()->getKey();
244 // If not on old list, it's been added
245 if(m_list.find(p) == NULL)
246 blocks_added.insert(p, true);
253 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
254 i.atEnd()==false; i++)
256 v3s16 p = i.getNode()->getKey();
257 m_list.insert(p, true);
265 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
268 m_random_spawn_timer(3),
269 m_send_recommended_timer(0),
271 m_game_time_fraction_counter(0)
275 ServerEnvironment::~ServerEnvironment()
277 // Clear active block list.
278 // This makes the next one delete all active objects.
279 m_active_blocks.clear();
281 // Convert all objects to static and delete the active objects
282 deactivateFarObjects(true);
288 void ServerEnvironment::serializePlayers(const std::string &savedir)
290 std::string players_path = savedir + "/players";
291 fs::CreateDir(players_path);
293 core::map<Player*, bool> saved_players;
295 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
296 for(u32 i=0; i<player_files.size(); i++)
298 if(player_files[i].dir)
301 // Full path to this file
302 std::string path = players_path + "/" + player_files[i].name;
304 //dstream<<"Checking player file "<<path<<std::endl;
306 // Load player to see what is its name
307 ServerRemotePlayer testplayer;
309 // Open file and deserialize
310 std::ifstream is(path.c_str(), std::ios_base::binary);
311 if(is.good() == false)
313 dstream<<"Failed to read "<<path<<std::endl;
316 testplayer.deSerialize(is);
319 //dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
321 // Search for the player
322 std::string playername = testplayer.getName();
323 Player *player = getPlayer(playername.c_str());
326 dstream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
330 //dstream<<"Found matching player, overwriting."<<std::endl;
332 // OK, found. Save player there.
334 // Open file and serialize
335 std::ofstream os(path.c_str(), std::ios_base::binary);
336 if(os.good() == false)
338 dstream<<"Failed to overwrite "<<path<<std::endl;
341 player->serialize(os);
342 saved_players.insert(player, true);
346 for(core::list<Player*>::Iterator i = m_players.begin();
347 i != m_players.end(); i++)
350 if(saved_players.find(player) != NULL)
352 /*dstream<<"Player "<<player->getName()
353 <<" was already saved."<<std::endl;*/
356 std::string playername = player->getName();
357 // Don't save unnamed player
360 //dstream<<"Not saving unnamed player."<<std::endl;
366 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
367 playername = "player";
368 std::string path = players_path + "/" + playername;
370 for(u32 i=0; i<1000; i++)
372 if(fs::PathExists(path) == false)
377 path = players_path + "/" + playername + itos(i);
381 dstream<<"WARNING: Didn't find free file for player"<<std::endl;
386 /*dstream<<"Saving player "<<player->getName()<<" to "
388 // Open file and serialize
389 std::ofstream os(path.c_str(), std::ios_base::binary);
390 if(os.good() == false)
392 dstream<<"WARNING: Failed to overwrite "<<path<<std::endl;
395 player->serialize(os);
396 saved_players.insert(player, true);
400 //dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
403 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
405 std::string players_path = savedir + "/players";
407 core::map<Player*, bool> saved_players;
409 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
410 for(u32 i=0; i<player_files.size(); i++)
412 if(player_files[i].dir)
415 // Full path to this file
416 std::string path = players_path + "/" + player_files[i].name;
418 dstream<<"Checking player file "<<path<<std::endl;
420 // Load player to see what is its name
421 ServerRemotePlayer testplayer;
423 // Open file and deserialize
424 std::ifstream is(path.c_str(), std::ios_base::binary);
425 if(is.good() == false)
427 dstream<<"Failed to read "<<path<<std::endl;
430 testplayer.deSerialize(is);
433 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
435 dstream<<"Not loading player with invalid name: "
436 <<testplayer.getName()<<std::endl;
439 dstream<<"Loaded test player with name "<<testplayer.getName()
442 // Search for the player
443 std::string playername = testplayer.getName();
444 Player *player = getPlayer(playername.c_str());
445 bool newplayer = false;
448 dstream<<"Is a new player"<<std::endl;
449 player = new ServerRemotePlayer();
455 dstream<<"Reading player "<<testplayer.getName()<<" from "
457 // Open file and deserialize
458 std::ifstream is(path.c_str(), std::ios_base::binary);
459 if(is.good() == false)
461 dstream<<"Failed to read "<<path<<std::endl;
464 player->deSerialize(is);
472 void ServerEnvironment::saveMeta(const std::string &savedir)
474 std::string path = savedir + "/env_meta.txt";
476 // Open file and serialize
477 std::ofstream os(path.c_str(), std::ios_base::binary);
478 if(os.good() == false)
480 dstream<<"WARNING: ServerEnvironment::saveMeta(): Failed to open "
482 throw SerializationError("Couldn't save env meta");
486 args.setU64("game_time", m_game_time);
487 args.setU64("time_of_day", getTimeOfDay());
492 void ServerEnvironment::loadMeta(const std::string &savedir)
494 std::string path = savedir + "/env_meta.txt";
496 // Open file and deserialize
497 std::ifstream is(path.c_str(), std::ios_base::binary);
498 if(is.good() == false)
500 dstream<<"WARNING: ServerEnvironment::loadMeta(): Failed to open "
502 throw SerializationError("Couldn't load env meta");
510 throw SerializationError
511 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
513 std::getline(is, line);
514 std::string trimmedline = trim(line);
515 if(trimmedline == "EnvArgsEnd")
517 args.parseConfigLine(line);
521 m_game_time = args.getU64("game_time");
522 }catch(SettingNotFoundException &e){
523 // Getting this is crucial, otherwise timestamps are useless
524 throw SerializationError("Couldn't load env meta game_time");
528 m_time_of_day = args.getU64("time_of_day");
529 }catch(SettingNotFoundException &e){
530 // This is not as important
531 m_time_of_day = 9000;
536 // This is probably very useless
537 void spawnRandomObjects(MapBlock *block)
539 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
540 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
542 bool last_node_walkable = false;
543 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
546 MapNode n = block->getNodeNoEx(p);
547 if(n.getContent() == CONTENT_IGNORE)
549 if(content_features(n).liquid_type != LIQUID_NONE)
551 if(content_features(n).walkable)
553 last_node_walkable = true;
556 if(last_node_walkable)
558 // If block contains light information
559 if(content_features(n).param_type == CPT_LIGHT)
561 if(n.getLight(LIGHTBANK_DAY) <= 5)
563 if(myrand() % 1000 == 0)
565 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
567 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
568 std::string data = obj->getStaticData();
569 StaticObject s_obj(obj->getType(),
570 obj->getBasePosition(), data);
572 block->m_static_objects.insert(0, s_obj);
574 block->setChangedFlag();
579 last_node_walkable = false;
585 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
587 // Get time difference
589 u32 stamp = block->getTimestamp();
590 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
591 dtime_s = m_game_time - block->getTimestamp();
592 dtime_s += additional_dtime;
594 // Set current time as timestamp (and let it set ChangedFlag)
595 block->setTimestamp(m_game_time);
597 //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
599 // Activate stored objects
600 activateObjects(block);
603 bool changed = block->m_node_metadata.step((float)dtime_s);
607 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
608 event.p = block->getPos();
609 m_map->dispatchEvent(&event);
611 block->setChangedFlag();
614 // TODO: Do something
615 // TODO: Implement usage of ActiveBlockModifier
617 // Here's a quick demonstration
619 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
620 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
621 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
623 v3s16 p = p0 + block->getPosRelative();
624 MapNode n = block->getNodeNoEx(p0);
627 // Convert all mud under proper day lighting to grass
628 if(n.getContent() == CONTENT_MUD)
632 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
633 if(content_features(n_top).air_equivalent &&
634 n_top.getLight(LIGHTBANK_DAY) >= 13)
636 n.setContent(CONTENT_GRASS);
637 m_map->addNodeWithEvent(p, n);
645 void ServerEnvironment::step(float dtime)
647 DSTACK(__FUNCTION_NAME);
649 //TimeTaker timer("ServerEnv step");
652 bool footprints = g_settings.getBool("footprints");
658 m_game_time_fraction_counter += dtime;
659 u32 inc_i = (u32)m_game_time_fraction_counter;
660 m_game_time += inc_i;
661 m_game_time_fraction_counter -= (float)inc_i;
667 for(core::list<Player*>::Iterator i = m_players.begin();
668 i != m_players.end(); i++)
672 // Ignore disconnected players
673 if(player->peer_id == 0)
676 v3f playerpos = player->getPosition();
679 player->move(dtime, *m_map, 100*BS);
682 Add footsteps to grass
686 // Get node that is at BS/4 under player
687 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
689 MapNode n = m_map->getNode(bottompos);
690 if(n.getContent() == CONTENT_GRASS)
692 n.setContent(CONTENT_GRASS_FOOTSTEPS);
693 m_map->setNode(bottompos, n);
696 catch(InvalidPositionException &e)
703 Manage active block list
705 if(m_active_blocks_management_interval.step(dtime, 2.0))
708 Get player block positions
710 core::list<v3s16> players_blockpos;
711 for(core::list<Player*>::Iterator
712 i = m_players.begin();
713 i != m_players.end(); i++)
716 // Ignore disconnected players
717 if(player->peer_id == 0)
719 v3s16 blockpos = getNodeBlockPos(
720 floatToInt(player->getPosition(), BS));
721 players_blockpos.push_back(blockpos);
725 Update list of active blocks, collecting changes
727 const s16 active_block_range = 5;
728 core::map<v3s16, bool> blocks_removed;
729 core::map<v3s16, bool> blocks_added;
730 m_active_blocks.update(players_blockpos, active_block_range,
731 blocks_removed, blocks_added);
734 Handle removed blocks
737 // Convert active objects that are no more in active blocks to static
738 deactivateFarObjects(false);
740 for(core::map<v3s16, bool>::Iterator
741 i = blocks_removed.getIterator();
742 i.atEnd()==false; i++)
744 v3s16 p = i.getNode()->getKey();
746 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
747 <<") became inactive"<<std::endl;*/
749 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
753 // Set current time as timestamp (and let it set ChangedFlag)
754 block->setTimestamp(m_game_time);
761 for(core::map<v3s16, bool>::Iterator
762 i = blocks_added.getIterator();
763 i.atEnd()==false; i++)
765 v3s16 p = i.getNode()->getKey();
767 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
768 <<") became active"<<std::endl;*/
770 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
774 activateBlock(block);
779 Mess around in active blocks
781 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
785 for(core::map<v3s16, bool>::Iterator
786 i = m_active_blocks.m_list.getIterator();
787 i.atEnd()==false; i++)
789 v3s16 p = i.getNode()->getKey();
791 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
792 <<") being handled"<<std::endl;*/
794 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
798 // Reset block usage timer
799 block->resetUsageTimer();
801 // Set current time as timestamp
802 block->setTimestampNoChangedFlag(m_game_time);
805 bool changed = block->m_node_metadata.step(dtime);
809 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
811 m_map->dispatchEvent(&event);
813 block->setChangedFlag();
817 if(m_active_blocks_test_interval.step(dtime, 10.0))
819 //float dtime = 10.0;
821 for(core::map<v3s16, bool>::Iterator
822 i = m_active_blocks.m_list.getIterator();
823 i.atEnd()==false; i++)
825 v3s16 p = i.getNode()->getKey();
827 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
828 <<") being handled"<<std::endl;*/
830 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
834 // Set current time as timestamp
835 block->setTimestampNoChangedFlag(m_game_time);
840 Note that map modifications should be done using the event-
841 making map methods so that the server gets information
844 Reading can be done quickly directly from the block.
846 Everything should bind to inside this single content
847 searching loop to keep things fast.
849 // TODO: Implement usage of ActiveBlockModifier
851 // Find out how many objects the block contains
852 u32 active_object_count = block->m_static_objects.m_active.size();
853 // Find out how many objects this and all the neighbors contain
854 u32 active_object_count_wider = 0;
855 for(s16 x=-1; x<=1; x++)
856 for(s16 y=-1; y<=1; y++)
857 for(s16 z=-1; z<=1; z++)
859 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
862 active_object_count_wider +=
863 block->m_static_objects.m_active.size();
867 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
868 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
869 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
871 v3s16 p = p0 + block->getPosRelative();
872 MapNode n = block->getNodeNoEx(p0);
876 Convert mud under proper lighting to grass
878 if(n.getContent() == CONTENT_MUD)
882 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
883 if(content_features(n_top).air_equivalent &&
884 n_top.getLightBlend(getDayNightRatio()) >= 13)
886 n.setContent(CONTENT_GRASS);
887 m_map->addNodeWithEvent(p, n);
892 Convert grass into mud if under something else than air
894 if(n.getContent() == CONTENT_GRASS)
896 //if(myrand()%20 == 0)
898 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
899 if(content_features(n_top).air_equivalent == false)
901 n.setContent(CONTENT_MUD);
902 m_map->addNodeWithEvent(p, n);
907 Rats spawn around regular trees
909 if(n.getContent() == CONTENT_TREE ||
910 n.getContent() == CONTENT_JUNGLETREE)
912 if(myrand()%200 == 0 && active_object_count_wider == 0)
914 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
915 0, myrand_range(-2, 2));
916 MapNode n1 = m_map->getNodeNoEx(p1);
917 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
918 if(n1b.getContent() == CONTENT_GRASS &&
919 n1.getContent() == CONTENT_AIR)
921 v3f pos = intToFloat(p1, BS);
922 ServerActiveObject *obj = new RatSAO(this, 0, pos);
923 addActiveObject(obj);
928 Make trees from saplings!
930 if(n.getContent() == CONTENT_SAPLING)
934 core::map<v3s16, MapBlock*> modified_blocks;
936 ManualMapVoxelManipulator vmanip(m_map);
937 v3s16 tree_blockp = getNodeBlockPos(tree_p);
938 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
939 bool is_apple_tree = myrand()%4 == 0;
940 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
941 vmanip.blitBackAll(&modified_blocks);
944 core::map<v3s16, MapBlock*> lighting_modified_blocks;
945 for(core::map<v3s16, MapBlock*>::Iterator
946 i = modified_blocks.getIterator();
947 i.atEnd() == false; i++)
949 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
951 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
953 // Send a MEET_OTHER event
955 event.type = MEET_OTHER;
956 for(core::map<v3s16, MapBlock*>::Iterator
957 i = modified_blocks.getIterator();
958 i.atEnd() == false; i++)
960 v3s16 p = i.getNode()->getKey();
961 event.modified_blocks.insert(p, true);
963 m_map->dispatchEvent(&event);
975 //TimeTaker timer("Step active objects");
977 // This helps the objects to send data at the same time
978 bool send_recommended = false;
979 m_send_recommended_timer += dtime;
980 if(m_send_recommended_timer > 0.15)
982 m_send_recommended_timer = 0;
983 send_recommended = true;
986 for(core::map<u16, ServerActiveObject*>::Iterator
987 i = m_active_objects.getIterator();
988 i.atEnd()==false; i++)
990 ServerActiveObject* obj = i.getNode()->getValue();
991 // Don't step if is to be removed or stored statically
992 if(obj->m_removed || obj->m_pending_deactivation)
995 obj->step(dtime, send_recommended);
996 // Read messages from object
997 while(obj->m_messages_out.size() > 0)
999 m_active_object_messages.push_back(
1000 obj->m_messages_out.pop_front());
1006 Manage active objects
1008 if(m_object_management_interval.step(dtime, 0.5))
1011 Remove objects that satisfy (m_removed && m_known_by_count==0)
1013 removeRemovedObjects();
1016 if(g_settings.getBool("enable_experimental"))
1023 m_random_spawn_timer -= dtime;
1024 if(m_random_spawn_timer < 0)
1026 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1027 //m_random_spawn_timer += 2.0;
1028 m_random_spawn_timer += 200.0;
1034 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1035 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1036 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1038 Player *player = getRandomConnectedPlayer();
1041 pos = player->getPosition();
1043 myrand_range(-3,3)*BS,
1045 myrand_range(-3,3)*BS
1049 Create a ServerActiveObject
1052 //TestSAO *obj = new TestSAO(this, 0, pos);
1053 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1054 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1055 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1056 ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1057 addActiveObject(obj);
1061 } // enable_experimental
1064 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1066 core::map<u16, ServerActiveObject*>::Node *n;
1067 n = m_active_objects.find(id);
1070 return n->getValue();
1073 bool isFreeServerActiveObjectId(u16 id,
1074 core::map<u16, ServerActiveObject*> &objects)
1079 for(core::map<u16, ServerActiveObject*>::Iterator
1080 i = objects.getIterator();
1081 i.atEnd()==false; i++)
1083 if(i.getNode()->getKey() == id)
1089 u16 getFreeServerActiveObjectId(
1090 core::map<u16, ServerActiveObject*> &objects)
1095 if(isFreeServerActiveObjectId(new_id, objects))
1105 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1108 u16 id = addActiveObjectRaw(object, true);
1113 Finds out what new objects have been added to
1114 inside a radius around a position
1116 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1117 core::map<u16, bool> ¤t_objects,
1118 core::map<u16, bool> &added_objects)
1120 v3f pos_f = intToFloat(pos, BS);
1121 f32 radius_f = radius * BS;
1123 Go through the object list,
1124 - discard m_removed objects,
1125 - discard objects that are too far away,
1126 - discard objects that are found in current_objects.
1127 - add remaining objects to added_objects
1129 for(core::map<u16, ServerActiveObject*>::Iterator
1130 i = m_active_objects.getIterator();
1131 i.atEnd()==false; i++)
1133 u16 id = i.getNode()->getKey();
1135 ServerActiveObject *object = i.getNode()->getValue();
1138 // Discard if removed
1139 if(object->m_removed)
1141 // Discard if too far
1142 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1143 if(distance_f > radius_f)
1145 // Discard if already on current_objects
1146 core::map<u16, bool>::Node *n;
1147 n = current_objects.find(id);
1150 // Add to added_objects
1151 added_objects.insert(id, false);
1156 Finds out what objects have been removed from
1157 inside a radius around a position
1159 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1160 core::map<u16, bool> ¤t_objects,
1161 core::map<u16, bool> &removed_objects)
1163 v3f pos_f = intToFloat(pos, BS);
1164 f32 radius_f = radius * BS;
1166 Go through current_objects; object is removed if:
1167 - object is not found in m_active_objects (this is actually an
1168 error condition; objects should be set m_removed=true and removed
1169 only after all clients have been informed about removal), or
1170 - object has m_removed=true, or
1171 - object is too far away
1173 for(core::map<u16, bool>::Iterator
1174 i = current_objects.getIterator();
1175 i.atEnd()==false; i++)
1177 u16 id = i.getNode()->getKey();
1178 ServerActiveObject *object = getActiveObject(id);
1181 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1182 <<" object in current_objects is NULL"<<std::endl;
1184 else if(object->m_removed == false)
1186 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1187 /*dstream<<"removed == false"
1188 <<"distance_f = "<<distance_f
1189 <<", radius_f = "<<radius_f<<std::endl;*/
1190 if(distance_f < radius_f)
1196 removed_objects.insert(id, false);
1200 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1202 if(m_active_object_messages.size() == 0)
1203 return ActiveObjectMessage(0);
1205 return m_active_object_messages.pop_front();
1209 ************ Private methods *************
1212 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1216 if(object->getId() == 0)
1218 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1221 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1222 <<"no free ids available"<<std::endl;
1226 object->setId(new_id);
1228 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1230 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1231 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1235 /*dstream<<"INFO: ServerEnvironment::addActiveObjectRaw(): "
1236 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1238 m_active_objects.insert(object->getId(), object);
1240 // Add static object to active static list of the block
1241 v3f objectpos = object->getBasePosition();
1242 std::string staticdata = object->getStaticData();
1243 StaticObject s_obj(object->getType(), objectpos, staticdata);
1244 // Add to the block where the object is located in
1245 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1246 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1249 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1250 object->m_static_exists = true;
1251 object->m_static_block = blockpos;
1254 block->setChangedFlag();
1257 dstream<<"WARNING: ServerEnv: Could not find a block for "
1258 <<"storing newly added static active object"<<std::endl;
1261 return object->getId();
1265 Remove objects that satisfy (m_removed && m_known_by_count==0)
1267 void ServerEnvironment::removeRemovedObjects()
1269 core::list<u16> objects_to_remove;
1270 for(core::map<u16, ServerActiveObject*>::Iterator
1271 i = m_active_objects.getIterator();
1272 i.atEnd()==false; i++)
1274 u16 id = i.getNode()->getKey();
1275 ServerActiveObject* obj = i.getNode()->getValue();
1276 // This shouldn't happen but check it
1279 dstream<<"WARNING: NULL object found in ServerEnvironment"
1280 <<" while finding removed objects. id="<<id<<std::endl;
1281 // Id to be removed from m_active_objects
1282 objects_to_remove.push_back(id);
1287 We will delete objects that are marked as removed or thatare
1288 waiting for deletion after deactivation
1290 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1294 Delete static data from block if is marked as removed
1296 if(obj->m_static_exists && obj->m_removed)
1298 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1301 block->m_static_objects.remove(id);
1302 block->setChangedFlag();
1306 // If m_known_by_count > 0, don't actually remove.
1307 if(obj->m_known_by_count > 0)
1312 // Id to be removed from m_active_objects
1313 objects_to_remove.push_back(id);
1315 // Remove references from m_active_objects
1316 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1317 i != objects_to_remove.end(); i++)
1319 m_active_objects.remove(*i);
1324 Convert stored objects from blocks near the players to active.
1326 void ServerEnvironment::activateObjects(MapBlock *block)
1330 // Ignore if no stored objects (to not set changed flag)
1331 if(block->m_static_objects.m_stored.size() == 0)
1333 // A list for objects that couldn't be converted to static for some
1334 // reason. They will be stored back.
1335 core::list<StaticObject> new_stored;
1336 // Loop through stored static objects
1337 for(core::list<StaticObject>::Iterator
1338 i = block->m_static_objects.m_stored.begin();
1339 i != block->m_static_objects.m_stored.end(); i++)
1341 /*dstream<<"INFO: Server: Creating an active object from "
1342 <<"static data"<<std::endl;*/
1343 StaticObject &s_obj = *i;
1344 // Create an active object from the data
1345 ServerActiveObject *obj = ServerActiveObject::create
1346 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1347 // If couldn't create object, store static data back.
1350 new_stored.push_back(s_obj);
1353 // This will also add the object to the active static list
1354 addActiveObjectRaw(obj, false);
1355 //u16 id = addActiveObjectRaw(obj, false);
1357 // Clear stored list
1358 block->m_static_objects.m_stored.clear();
1359 // Add leftover failed stuff to stored list
1360 for(core::list<StaticObject>::Iterator
1361 i = new_stored.begin();
1362 i != new_stored.end(); i++)
1364 StaticObject &s_obj = *i;
1365 block->m_static_objects.m_stored.push_back(s_obj);
1367 // Block has been modified
1368 // NOTE: No it has not really. Save I/O here.
1369 //block->setChangedFlag();
1373 Convert objects that are not in active blocks to static.
1375 If m_known_by_count != 0, active object is not deleted, but static
1376 data is still updated.
1378 If force_delete is set, active object is deleted nevertheless. It
1379 shall only be set so in the destructor of the environment.
1381 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1383 core::list<u16> objects_to_remove;
1384 for(core::map<u16, ServerActiveObject*>::Iterator
1385 i = m_active_objects.getIterator();
1386 i.atEnd()==false; i++)
1388 ServerActiveObject* obj = i.getNode()->getValue();
1390 // This shouldn't happen but check it
1393 dstream<<"WARNING: NULL object found in ServerEnvironment"
1399 u16 id = i.getNode()->getKey();
1400 v3f objectpos = obj->getBasePosition();
1402 // The block in which the object resides in
1403 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1405 // If block is active, don't remove
1406 if(m_active_blocks.contains(blockpos_o))
1410 Update the static data
1413 // Delete old static object
1414 MapBlock *oldblock = NULL;
1415 if(obj->m_static_exists)
1417 MapBlock *block = m_map->getBlockNoCreateNoEx
1418 (obj->m_static_block);
1421 block->m_static_objects.remove(id);
1425 // Create new static object
1426 std::string staticdata = obj->getStaticData();
1427 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1428 // Add to the block where the object is located in
1429 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1430 // Get or generate the block
1431 MapBlock *block = m_map->emergeBlock(blockpos);
1433 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1436 // Block not found. Is the old block still ok?
1439 // Load from disk or generate
1441 block = m_map->emergeBlock(blockpos);
1446 block->m_static_objects.insert(0, s_obj);
1447 block->setChangedFlag();
1448 obj->m_static_exists = true;
1449 obj->m_static_block = block->getPos();
1452 dstream<<"WARNING: ServerEnv: Could not find or generate "
1453 <<"a block for storing static object"<<std::endl;
1454 obj->m_static_exists = false;
1459 Delete active object if not known by some client,
1460 else set pending deactivation
1463 // If known by some client, don't delete.
1464 if(obj->m_known_by_count > 0 && force_delete == false)
1466 obj->m_pending_deactivation = true;
1470 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1472 // Delete active object
1474 // Id to be removed from m_active_objects
1475 objects_to_remove.push_back(id);
1478 // Remove references from m_active_objects
1479 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1480 i != objects_to_remove.end(); i++)
1482 m_active_objects.remove(*i);
1493 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1501 ClientEnvironment::~ClientEnvironment()
1503 // delete active objects
1504 for(core::map<u16, ClientActiveObject*>::Iterator
1505 i = m_active_objects.getIterator();
1506 i.atEnd()==false; i++)
1508 delete i.getNode()->getValue();
1515 void ClientEnvironment::addPlayer(Player *player)
1517 DSTACK(__FUNCTION_NAME);
1519 It is a failure if player is local and there already is a local
1522 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1524 Environment::addPlayer(player);
1527 LocalPlayer * ClientEnvironment::getLocalPlayer()
1529 for(core::list<Player*>::Iterator i = m_players.begin();
1530 i != m_players.end(); i++)
1532 Player *player = *i;
1533 if(player->isLocal())
1534 return (LocalPlayer*)player;
1539 void ClientEnvironment::step(float dtime)
1541 DSTACK(__FUNCTION_NAME);
1543 // Get some settings
1544 bool free_move = g_settings.getBool("free_move");
1545 bool footprints = g_settings.getBool("footprints");
1548 LocalPlayer *lplayer = getLocalPlayer();
1550 // collision info queue
1551 core::list<CollisionInfo> player_collisions;
1554 Get the speed the player is going
1556 bool is_climbing = lplayer->is_climbing;
1559 Check if the player is frozen (don't apply physics)
1561 bool is_frozen = lplayer->is_frozen;
1563 f32 player_speed = 0.001; // just some small value
1564 player_speed = lplayer->getSpeed().getLength();
1567 Maximum position increment
1569 //f32 position_max_increment = 0.05*BS;
1570 f32 position_max_increment = 0.1*BS;
1572 // Maximum time increment (for collision detection etc)
1573 // time = distance / speed
1574 f32 dtime_max_increment = position_max_increment / player_speed;
1576 // Maximum time increment is 10ms or lower
1577 if(dtime_max_increment > 0.01)
1578 dtime_max_increment = 0.01;
1580 // Don't allow overly huge dtime
1584 f32 dtime_downcount = dtime;
1587 Stuff that has a maximum time increment
1596 if(dtime_downcount > dtime_max_increment)
1598 dtime_part = dtime_max_increment;
1599 dtime_downcount -= dtime_part;
1603 dtime_part = dtime_downcount;
1605 Setting this to 0 (no -=dtime_part) disables an infinite loop
1606 when dtime_part is so small that dtime_downcount -= dtime_part
1609 dtime_downcount = 0;
1617 v3f lplayerpos = lplayer->getPosition();
1620 if(free_move == false && is_climbing == false && is_frozen == false)
1623 v3f speed = lplayer->getSpeed();
1624 if(lplayer->swimming_up == false)
1625 speed.Y -= 9.81 * BS * dtime_part * 2;
1628 if(lplayer->in_water_stable || lplayer->in_water)
1630 f32 max_down = 2.0*BS;
1631 if(speed.Y < -max_down) speed.Y = -max_down;
1634 if(speed.getLength() > max)
1636 speed = speed / speed.getLength() * max;
1640 lplayer->setSpeed(speed);
1645 This also does collision detection.
1647 lplayer->move(dtime_part, *m_map, position_max_increment,
1648 &player_collisions);
1651 while(dtime_downcount > 0.001);
1653 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1655 for(core::list<CollisionInfo>::Iterator
1656 i = player_collisions.begin();
1657 i != player_collisions.end(); i++)
1659 CollisionInfo &info = *i;
1660 if(info.t == COLLISION_FALL)
1662 //f32 tolerance = BS*10; // 2 without damage
1663 f32 tolerance = BS*12; // 3 without damage
1665 if(info.speed > tolerance)
1667 f32 damage_f = (info.speed - tolerance)/BS*factor;
1668 u16 damage = (u16)(damage_f+0.5);
1669 if(lplayer->hp > damage)
1670 lplayer->hp -= damage;
1674 ClientEnvEvent event;
1675 event.type = CEE_PLAYER_DAMAGE;
1676 event.player_damage.amount = damage;
1677 m_client_event_queue.push_back(event);
1683 A quick draft of lava damage
1685 if(m_lava_hurt_interval.step(dtime, 1.0))
1687 v3f pf = lplayer->getPosition();
1689 // Feet, middle and head
1690 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1691 MapNode n1 = m_map->getNodeNoEx(p1);
1692 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1693 MapNode n2 = m_map->getNodeNoEx(p2);
1694 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1695 MapNode n3 = m_map->getNodeNoEx(p2);
1697 u32 damage_per_second = 0;
1698 damage_per_second = MYMAX(damage_per_second,
1699 content_features(n1).damage_per_second);
1700 damage_per_second = MYMAX(damage_per_second,
1701 content_features(n2).damage_per_second);
1702 damage_per_second = MYMAX(damage_per_second,
1703 content_features(n3).damage_per_second);
1705 if(damage_per_second != 0)
1707 ClientEnvEvent event;
1708 event.type = CEE_PLAYER_DAMAGE;
1709 event.player_damage.amount = damage_per_second;
1710 m_client_event_queue.push_back(event);
1715 Stuff that can be done in an arbitarily large dtime
1717 for(core::list<Player*>::Iterator i = m_players.begin();
1718 i != m_players.end(); i++)
1720 Player *player = *i;
1721 v3f playerpos = player->getPosition();
1724 Handle non-local players
1726 if(player->isLocal() == false)
1729 player->move(dtime, *m_map, 100*BS);
1733 // Update lighting on all players on client
1734 u8 light = LIGHT_MAX;
1737 v3s16 p = player->getLightPosition();
1738 MapNode n = m_map->getNode(p);
1739 light = n.getLightBlend(getDayNightRatio());
1741 catch(InvalidPositionException &e) {}
1742 player->updateLight(light);
1745 Add footsteps to grass
1749 // Get node that is at BS/4 under player
1750 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1752 MapNode n = m_map->getNode(bottompos);
1753 if(n.getContent() == CONTENT_GRASS)
1755 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1756 m_map->setNode(bottompos, n);
1757 // Update mesh on client
1758 if(m_map->mapType() == MAPTYPE_CLIENT)
1760 v3s16 p_blocks = getNodeBlockPos(bottompos);
1761 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1762 //b->updateMesh(getDayNightRatio());
1763 b->setMeshExpired(true);
1767 catch(InvalidPositionException &e)
1774 Step active objects and update lighting of them
1777 for(core::map<u16, ClientActiveObject*>::Iterator
1778 i = m_active_objects.getIterator();
1779 i.atEnd()==false; i++)
1781 ClientActiveObject* obj = i.getNode()->getValue();
1783 obj->step(dtime, this);
1785 if(m_active_object_light_update_interval.step(dtime, 0.21))
1788 //u8 light = LIGHT_MAX;
1792 v3s16 p = obj->getLightPosition();
1793 MapNode n = m_map->getNode(p);
1794 light = n.getLightBlend(getDayNightRatio());
1796 catch(InvalidPositionException &e) {}
1797 obj->updateLight(light);
1802 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1804 m_map->updateMeshes(blockpos, getDayNightRatio());
1807 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1809 m_map->expireMeshes(only_daynight_diffed);
1812 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1814 core::map<u16, ClientActiveObject*>::Node *n;
1815 n = m_active_objects.find(id);
1818 return n->getValue();
1821 bool isFreeClientActiveObjectId(u16 id,
1822 core::map<u16, ClientActiveObject*> &objects)
1827 for(core::map<u16, ClientActiveObject*>::Iterator
1828 i = objects.getIterator();
1829 i.atEnd()==false; i++)
1831 if(i.getNode()->getKey() == id)
1837 u16 getFreeClientActiveObjectId(
1838 core::map<u16, ClientActiveObject*> &objects)
1843 if(isFreeClientActiveObjectId(new_id, objects))
1853 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1856 if(object->getId() == 0)
1858 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1861 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1862 <<"no free ids available"<<std::endl;
1866 object->setId(new_id);
1868 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1870 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1871 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1875 dstream<<"INFO: ClientEnvironment::addActiveObject(): "
1876 <<"added (id="<<object->getId()<<")"<<std::endl;
1877 m_active_objects.insert(object->getId(), object);
1878 object->addToScene(m_smgr);
1879 return object->getId();
1882 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1883 const std::string &init_data)
1885 ClientActiveObject* obj = ClientActiveObject::create(type);
1888 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1889 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1896 addActiveObject(obj);
1898 obj->initialize(init_data);
1901 void ClientEnvironment::removeActiveObject(u16 id)
1903 dstream<<"ClientEnvironment::removeActiveObject(): "
1904 <<"id="<<id<<std::endl;
1905 ClientActiveObject* obj = getActiveObject(id);
1908 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1909 <<"id="<<id<<" not found"<<std::endl;
1912 obj->removeFromScene();
1914 m_active_objects.remove(id);
1917 void ClientEnvironment::processActiveObjectMessage(u16 id,
1918 const std::string &data)
1920 ClientActiveObject* obj = getActiveObject(id);
1923 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1924 <<" got message for id="<<id<<", which doesn't exist."
1928 obj->processMessage(data);
1932 Callbacks for activeobjects
1935 void ClientEnvironment::damageLocalPlayer(u8 damage)
1937 LocalPlayer *lplayer = getLocalPlayer();
1940 if(lplayer->hp > damage)
1941 lplayer->hp -= damage;
1945 ClientEnvEvent event;
1946 event.type = CEE_PLAYER_DAMAGE;
1947 event.player_damage.amount = damage;
1948 m_client_event_queue.push_back(event);
1952 Client likes to call these
1955 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1956 core::array<DistanceSortedActiveObject> &dest)
1958 for(core::map<u16, ClientActiveObject*>::Iterator
1959 i = m_active_objects.getIterator();
1960 i.atEnd()==false; i++)
1962 ClientActiveObject* obj = i.getNode()->getValue();
1964 f32 d = (obj->getPosition() - origin).getLength();
1969 DistanceSortedActiveObject dso(obj, d);
1971 dest.push_back(dso);
1975 ClientEnvEvent ClientEnvironment::getClientEvent()
1977 if(m_client_event_queue.size() == 0)
1979 ClientEnvEvent event;
1980 event.type = CEE_NONE;
1983 return m_client_event_queue.pop_front();
1986 #endif // #ifndef SERVER