Newer
Older
Perttu Ahola
committed
s32 total_sending = 0;
Perttu Ahola
committed
{
ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
Perttu Ahola
committed
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
i.atEnd() == false; i++)
{
RemoteClient *client = i.getNode()->getValue();
assert(client->peer_id == i.getNode()->getKey());
// If definitions and textures have not been sent, don't
// send MapBlocks either
if(!client->definitions_sent)
continue;
total_sending += client->SendingCount();
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
client->GetNextBlocks(this, dtime, queue);
}
Perttu Ahola
committed
}
// Sort.
// Lowest priority number comes first.
// Lowest is most important.
queue.sort();
for(u32 i=0; i<queue.size(); i++)
{
//TODO: Calculate limit dynamically
if(total_sending >= g_settings->getS32
Perttu Ahola
committed
("max_simultaneous_block_sends_server_total"))
break;
PrioritySortedBlockTransfer q = queue[i];
MapBlock *block = NULL;
try
{
Perttu Ahola
committed
}
catch(InvalidPositionException &e)
{
continue;
}
RemoteClient *client = getClient(q.peer_id);
SendBlockNoLock(q.peer_id, block, client->serialization_version);
Perttu Ahola
committed
client->SentBlock(q.pos);
total_sending++;
}
}
Perttu Ahola
committed
DSTACK(__FUNCTION_NAME);
infostream<<"Server: Calculating media file checksums"<<std::endl;
// Collect all media file paths
std::list<std::string> paths;
Perttu Ahola
committed
for(core::list<ModSpec>::Iterator i = m_mods.begin();
i != m_mods.end(); i++){
const ModSpec &mod = *i;
paths.push_back(mod.path + DIR_DELIM + "textures");
paths.push_back(mod.path + DIR_DELIM + "sounds");
paths.push_back(mod.path + DIR_DELIM + "media");
paths.push_back(mod.path + DIR_DELIM + "models");
std::string path_all = "textures";
paths.push_back(path_all + DIR_DELIM + "all");
// Collect media file information from paths into cache
for(std::list<std::string>::iterator i = paths.begin();
i != paths.end(); i++)
{
std::string mediapath = *i;
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
for(u32 j=0; j<dirlist.size(); j++){
if(dirlist[j].dir) // Ignode dirs
continue;
std::string filename = dirlist[j].name;
// If name contains illegal characters, ignore the file
if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
infostream<<"Server: ignoring illegal file name: \""
<<filename<<"\""<<std::endl;
// If name is not in a supported format, ignore it
const char *supported_ext[] = {
".png", ".jpg", ".bmp", ".tga",
".pcx", ".ppm", ".psd", ".wal", ".rgb",
".ogg",
MirceaKitsune
committed
".x", ".b3d", ".md2", ".obj",
NULL
};
if(removeStringEnd(filename, supported_ext) == ""){
infostream<<"Server: ignoring unsupported file extension: \""
<<filename<<"\""<<std::endl;
continue;
}
// Ok, attempt to load the file and add to cache
std::string filepath = mediapath + DIR_DELIM + filename;
std::ifstream fis(filepath.c_str(), std::ios_base::binary);
errorstream<<"Server::fillMediaCache(): Could not open \""
<<filename<<"\" for reading"<<std::endl;
continue;
}
std::ostringstream tmp_os(std::ios_base::binary);
bool bad = false;
for(;;){
char buf[1024];
fis.read(buf, 1024);
std::streamsize len = fis.gcount();
tmp_os.write(buf, len);
if(fis.eof())
break;
if(!fis.good()){
bad = true;
break;
errorstream<<"Server::fillMediaCache(): Failed to read \""
<<filename<<"\""<<std::endl;
continue;
}
if(tmp_os.str().length() == 0){
errorstream<<"Server::fillMediaCache(): Empty file \""
<<filepath<<"\""<<std::endl;
Perttu Ahola
committed
SHA1 sha1;
sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
Perttu Ahola
committed
unsigned char *digest = sha1.getDigest();
std::string sha1_base64 = base64_encode(digest, 20);
std::string sha1_hex = hex_encode((char*)digest, 20);
Perttu Ahola
committed
this->m_media[filename] = MediaInfo(filepath, sha1_base64);
verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
Perttu Ahola
committed
}
}
struct SendableMediaAnnouncement
{
std::string name;
std::string sha1_digest;
Perttu Ahola
committed
SendableMediaAnnouncement(const std::string name_="",
const std::string sha1_digest_=""):
name(name_),
sha1_digest(sha1_digest_)
{}
};
Perttu Ahola
committed
void Server::sendMediaAnnouncement(u16 peer_id)
{
Perttu Ahola
committed
DSTACK(__FUNCTION_NAME);
verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
Perttu Ahola
committed
core::list<SendableMediaAnnouncement> file_announcements;
Perttu Ahola
committed
for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
i != m_media.end(); i++){
Perttu Ahola
committed
// Put in list
file_announcements.push_back(
SendableMediaAnnouncement(i->first, i->second.sha1_digest));
Perttu Ahola
committed
}
// Make packet
std::ostringstream os(std::ios_base::binary);
Perttu Ahola
committed
/*
u16 command
Perttu Ahola
committed
for each texture {
u16 length of name
string name
Perttu Ahola
committed
string sha1_digest
}
*/
writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
writeU16(os, file_announcements.size());
Perttu Ahola
committed
for(core::list<SendableMediaAnnouncement>::Iterator
j = file_announcements.begin();
j != file_announcements.end(); j++){
Perttu Ahola
committed
os<<serializeString(j->name);
os<<serializeString(j->sha1_digest);
}
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
{
std::string name;
std::string path;
std::string data;
SendableMedia(const std::string &name_="", const std::string path_="",
const std::string &data_=""):
name(name_),
path(path_),
data(data_)
{}
};
void Server::sendRequestedMedia(u16 peer_id,
const core::list<MediaRequest> &tosend)
{
verbosestream<<"Server::sendRequestedMedia(): "
<<"Sending files to client"<<std::endl;
Perttu Ahola
committed
Perttu Ahola
committed
// Put 5kB in one bunch (this is not accurate)
u32 bytes_per_bunch = 5000;
Perttu Ahola
committed
core::array< core::list<SendableMedia> > file_bunches;
file_bunches.push_back(core::list<SendableMedia>());
Perttu Ahola
committed
Perttu Ahola
committed
for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
i != tosend.end(); i++)
{
if(m_media.find(i->name) == m_media.end()){
errorstream<<"Server::sendRequestedMedia(): Client asked for "
<<"unknown file \""<<(i->name)<<"\""<<std::endl;
Perttu Ahola
committed
//TODO get path + name
std::string tpath = m_media[(*i).name].path;
Perttu Ahola
committed
// Read data
std::ifstream fis(tpath.c_str(), std::ios_base::binary);
if(fis.good() == false){
errorstream<<"Server::sendRequestedMedia(): Could not open \""
Perttu Ahola
committed
<<tpath<<"\" for reading"<<std::endl;
continue;
}
std::ostringstream tmp_os(std::ios_base::binary);
bool bad = false;
for(;;){
char buf[1024];
fis.read(buf, 1024);
std::streamsize len = fis.gcount();
tmp_os.write(buf, len);
Perttu Ahola
committed
if(fis.eof())
break;
if(!fis.good()){
bad = true;
break;
Perttu Ahola
committed
if(bad){
errorstream<<"Server::sendRequestedMedia(): Failed to read \""
Perttu Ahola
committed
<<(*i).name<<"\""<<std::endl;
continue;
}
/*infostream<<"Server::sendRequestedMedia(): Loaded \""
Perttu Ahola
committed
<<tname<<"\""<<std::endl;*/
// Put in list
file_bunches[file_bunches.size()-1].push_back(
SendableMedia((*i).name, tpath, tmp_os.str()));
Perttu Ahola
committed
// Start next bunch if got enough data
if(file_size_bunch_total >= bytes_per_bunch){
file_bunches.push_back(core::list<SendableMedia>());
file_size_bunch_total = 0;
Perttu Ahola
committed
}
Perttu Ahola
committed
u32 num_bunches = file_bunches.size();
for(u32 i=0; i<num_bunches; i++)
{
std::ostringstream os(std::ios_base::binary);
Perttu Ahola
committed
/*
u16 command
u16 total number of texture bunches
u16 index of this bunch
u32 number of files in this bunch
for each file {
u16 length of name
string name
u32 length of data
data
writeU16(os, TOCLIENT_MEDIA);
writeU16(os, num_bunches);
writeU16(os, i);
writeU32(os, file_bunches[i].size());
Perttu Ahola
committed
for(core::list<SendableMedia>::Iterator
j = file_bunches[i].begin();
j != file_bunches[i].end(); j++){
os<<serializeString(j->name);
os<<serializeLongString(j->data);
}
Perttu Ahola
committed
// Make data buffer
std::string s = os.str();
verbosestream<<"Server::sendRequestedMedia(): bunch "
<<i<<"/"<<num_bunches
<<" files="<<file_bunches[i].size()
<<" size=" <<s.size()<<std::endl;
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
{
if(m_detached_inventories.count(name) == 0){
errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
return;
}
Inventory *inv = m_detached_inventories[name];
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_DETACHED_INVENTORY);
os<<serializeString(name);
inv->serialize(os);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::sendDetachedInventoryToAll(const std::string &name)
{
DSTACK(__FUNCTION_NAME);
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
i.atEnd() == false; i++){
RemoteClient *client = i.getNode()->getValue();
sendDetachedInventory(name, client->peer_id);
}
}
void Server::sendDetachedInventories(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
for(std::map<std::string, Inventory*>::iterator
i = m_detached_inventories.begin();
i != m_detached_inventories.end(); i++){
const std::string &name = i->first;
//Inventory *inv = i->second;
sendDetachedInventory(name, peer_id);
}
}
Perttu Ahola
committed
/*
Something random
*/
void Server::DiePlayer(u16 peer_id)
DSTACK(__FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
infostream<<"Server::DiePlayer(): Player "
<<playersao->getPlayer()->getName()
<<" dies"<<std::endl;
playersao->setHP(0);
scriptapi_on_dieplayer(m_lua, playersao);
SendPlayerHP(peer_id);
SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
void Server::RespawnPlayer(u16 peer_id)
DSTACK(__FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
infostream<<"Server::RespawnPlayer(): Player "
<<playersao->getPlayer()->getName()
<<" respawns"<<std::endl;
playersao->setHP(PLAYER_MAX_HP);
bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
if(!repositioned){
v3f pos = findSpawnPos(m_env->getServerMap());
playersao->setPos(pos);
Perttu Ahola
committed
void Server::UpdateCrafting(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
Perttu Ahola
committed
assert(player);
// Get a preview for crafting
ItemStack preview;
getCraftingResult(&player->inventory, preview, false, this);
// Put the new preview in
InventoryList *plist = player->inventory.getList("craftpreview");
assert(plist);
assert(plist->getSize() >= 1);
plist->changeItem(0, preview);
}
RemoteClient* Server::getClient(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
//JMutexAutoLock lock(m_con_mutex);
core::map<u16, RemoteClient*>::Node *n;
n = m_clients.find(peer_id);
// A client should exist for all peers
assert(n != NULL);
return n->getValue();
}
std::wstring Server::getStatusString()
{
std::wostringstream os(std::ios_base::binary);
os<<L"# Server: ";
// Version
os<<L"version="<<narrow_to_wide(VERSION_STRING);
// Uptime
// Information about clients
core::map<u16, RemoteClient*>::Iterator i;
bool first;
os<<L", clients={";
for(i = m_clients.getIterator(), first = true;
i.atEnd() == false; i++)
{
// Get client and check that it is valid
RemoteClient *client = i.getNode()->getValue();
assert(client->peer_id == i.getNode()->getKey());
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
// Get player
// Get name of player
std::wstring name = L"unknown";
if(player != NULL)
name = narrow_to_wide(player->getName());
// Add name to information string
if(!first)
os<<L",";
else
first = false;
os<<name;
}
os<<L"}";
if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
Perttu Ahola
committed
os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
if(g_settings->get("motd") != "")
os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
return os.str();
}
Perttu Ahola
committed
std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
Perttu Ahola
committed
std::set<std::string> privs;
scriptapi_get_auth(m_lua, name, NULL, &privs);
return privs;
Perttu Ahola
committed
bool Server::checkPriv(const std::string &name, const std::string &priv)
{
Perttu Ahola
committed
std::set<std::string> privs = getPlayerEffectivePrivs(name);
return (privs.count(priv) != 0);
}
Perttu Ahola
committed
void Server::reportPrivsModified(const std::string &name)
{
if(name == ""){
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
i.atEnd() == false; i++){
RemoteClient *client = i.getNode()->getValue();
Perttu Ahola
committed
Player *player = m_env->getPlayer(client->peer_id);
reportPrivsModified(player->getName());
Perttu Ahola
committed
}
} else {
Player *player = m_env->getPlayer(name.c_str());
if(!player)
return;
SendPlayerPrivileges(player->peer_id);
PlayerSAO *sao = player->getPlayerSAO();
if(!sao)
return;
sao->updatePrivileges(
getPlayerEffectivePrivs(name),
isSingleplayer());
Perttu Ahola
committed
}
}
void Server::reportInventoryFormspecModified(const std::string &name)
{
Player *player = m_env->getPlayer(name.c_str());
if(!player)
return;
SendPlayerInventoryFormspec(player->peer_id);
}
// Saves g_settings to configpath given at initialization
void Server::saveConfig()
{
if(m_path_config != "")
g_settings->updateConfigFile(m_path_config.c_str());
void Server::notifyPlayer(const char *name, const std::wstring msg)
{
if(!player)
return;
SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
}
void Server::notifyPlayers(const std::wstring msg)
{
BroadcastChatMessage(msg);
}
void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
{
u8 flags = 0;
if(!allow_generate)
flags |= BLOCK_EMERGE_FLAG_FROMDISK;
m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
}
Inventory* Server::createDetachedInventory(const std::string &name)
{
if(m_detached_inventories.count(name) > 0){
infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
delete m_detached_inventories[name];
} else {
infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
}
Inventory *inv = new Inventory(m_itemdef);
assert(inv);
m_detached_inventories[name] = inv;
sendDetachedInventoryToAll(name);
return inv;
}
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
class BoolScopeSet
{
public:
BoolScopeSet(bool *dst, bool val):
m_dst(dst)
{
m_orig_state = *m_dst;
*m_dst = val;
}
~BoolScopeSet()
{
*m_dst = m_orig_state;
}
private:
bool *m_dst;
bool m_orig_state;
};
// actions: time-reversed list
// Return value: success/failure
bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
std::list<std::string> *log)
{
infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
ServerMap *map = (ServerMap*)(&m_env->getMap());
// Disable rollback report sink while reverting
BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
// Fail if no actions to handle
if(actions.empty()){
log->push_back("Nothing to do.");
return false;
}
int num_tried = 0;
int num_failed = 0;
for(std::list<RollbackAction>::const_iterator
i = actions.begin();
i != actions.end(); i++)
{
const RollbackAction &action = *i;
num_tried++;
bool success = action.applyRevert(map, this, this);
if(!success){
num_failed++;
std::ostringstream os;
os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
if(log)
log->push_back(os.str());
}else{
std::ostringstream os;
os<<"Succesfully reverted step ("<<num_tried<<") "<<action.toString();
infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
if(log)
log->push_back(os.str());
}
}
infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
<<" failed"<<std::endl;
// Call it done if less than half failed
return num_failed <= num_tried/2;
}
IItemDefManager* Server::getItemDefManager()
return m_itemdef;
}
INodeDefManager* Server::getNodeDefManager()
{
Perttu Ahola
committed
return m_nodedef;
ICraftDefManager* Server::getCraftDefManager()
{
return m_craftdef;
}
ITextureSource* Server::getTextureSource()
{
return NULL;
}
u16 Server::allocateUnknownNodeId(const std::string &name)
{
Perttu Ahola
committed
return m_nodedef->allocateDummy(name);
ISoundManager* Server::getSoundManager()
{
return &dummySoundManager;
}
MtEventManager* Server::getEventManager()
{
return m_event;
}
IRollbackReportSink* Server::getRollbackReportSink()
{
if(!m_enable_rollback_recording)
return NULL;
if(!m_rollback_sink_enabled)
return NULL;
return m_rollback;
}
IWritableItemDefManager* Server::getWritableItemDefManager()
return m_itemdef;
}
IWritableNodeDefManager* Server::getWritableNodeDefManager()
{
Perttu Ahola
committed
return m_nodedef;
IWritableCraftDefManager* Server::getWritableCraftDefManager()
{
return m_craftdef;
}
const ModSpec* Server::getModSpec(const std::string &modname)
{
for(core::list<ModSpec>::Iterator i = m_mods.begin();
i != m_mods.end(); i++){
const ModSpec &mod = *i;
if(mod.name == modname)
return &mod;
}
return NULL;
}
void Server::getModNames(core::list<std::string> &modlist)
{
for(core::list<ModSpec>::Iterator i = m_mods.begin(); i != m_mods.end(); i++)
{
modlist.push_back((*i).name);
}
}
Perttu Ahola
committed
std::string Server::getBuiltinLuaPath()
{
return porting::path_share + DIR_DELIM + "builtin";
}
Perttu Ahola
committed
v3f findSpawnPos(ServerMap &map)
{
Perttu Ahola
committed
//return v3f(50,50,50)*BS;
Perttu Ahola
committed
Perttu Ahola
committed
Perttu Ahola
committed
#if 0
nodepos = v2s16(0,0);
groundheight = 20;
#endif
#if 1
Perttu Ahola
committed
// Try to find a good place a few times
for(s32 i=0; i<1000; i++)
{
s32 range = 1 + i;
// We're going to try to throw the player to this position
v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
Perttu Ahola
committed
-range + (myrand()%(range*2)));
//v2s16 sectorpos = getNodeSectorPos(nodepos2d);
Perttu Ahola
committed
// Get ground height at point (fallbacks to heightmap function)
s16 groundheight = map.findGroundLevel(nodepos2d);
Perttu Ahola
committed
// Don't go underwater
if(groundheight < WATER_LEVEL)
{
//infostream<<"-> Underwater"<<std::endl;
Perttu Ahola
committed
continue;
}
// Don't go to high places
if(groundheight > WATER_LEVEL + 4)
{
//infostream<<"-> Underwater"<<std::endl;
Perttu Ahola
committed
continue;
}
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
bool is_good = false;
s32 air_count = 0;
for(s32 i=0; i<10; i++){
v3s16 blockpos = getNodeBlockPos(nodepos);
map.emergeBlock(blockpos, true);
MapNode n = map.getNodeNoEx(nodepos);
if(n.getContent() == CONTENT_AIR){
air_count++;
if(air_count >= 2){
is_good = true;
nodepos.Y -= 1;
break;
}
}
nodepos.Y++;
}
if(is_good){
// Found a good place
//infostream<<"Searched through "<<i<<" places."<<std::endl;
break;
}
Perttu Ahola
committed
}
Perttu Ahola
committed
#endif
Perttu Ahola
committed
return intToFloat(nodepos, BS);
Perttu Ahola
committed
}
PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
RemotePlayer *player = NULL;
bool newplayer = false;
/*
Try to get an existing player
*/
player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
// If player is already connected, cancel
if(player != NULL && player->peer_id != 0)
{
infostream<<"emergePlayer(): Player already connected"<<std::endl;
return NULL;
/*
If player with the wanted peer_id already exists, cancel.
*/
{
infostream<<"emergePlayer(): Player with wrong name but same"
" peer_id already exists"<<std::endl;
return NULL;
}
/*
Create a new player if it doesn't exist yet
*/
if(player == NULL)
newplayer = true;
player = new RemotePlayer(this);
player->updateName(name);
infostream<<"Server: Finding spawn place for player \""
<<name<<"\""<<std::endl;
player->setPosition(pos);
/* Add player to environment */
/*
Create a new player active object
*/
Perttu Ahola
committed
PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
getPlayerEffectivePrivs(player->getName()),
isSingleplayer());
/* Add object to environment */
m_env->addActiveObject(playersao);
/* Run scripts */
if(newplayer)
scriptapi_on_newplayer(m_lua, playersao);
scriptapi_on_joinplayer(m_lua, playersao);
}
void Server::handlePeerChange(PeerChange &c)
{
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
if(c.type == PEER_ADDED)
{
/*
Add
*/
// Error check
core::map<u16, RemoteClient*>::Node *n;
n = m_clients.find(c.peer_id);
// The client shouldn't already exist
assert(n == NULL);
// Create client
RemoteClient *client = new RemoteClient();
client->peer_id = c.peer_id;
m_clients.insert(client->peer_id, client);
} // PEER_ADDED
else if(c.type == PEER_REMOVED)
{
/*
Delete
*/
// Error check
core::map<u16, RemoteClient*>::Node *n;
n = m_clients.find(c.peer_id);
// The client should exist
assert(n != NULL);
/*
Mark objects to be not known by the client
*/
RemoteClient *client = n->getValue();
// Handle objects
for(core::map<u16, bool>::Iterator
i = client->m_known_objects.getIterator();
i.atEnd()==false; i++)
{
// Get object
u16 id = i.getNode()->getKey();
if(obj && obj->m_known_by_count > 0)
obj->m_known_by_count--;
}
/*
Clear references to playing sounds
*/
for(std::map<s32, ServerPlayingSound>::iterator
i = m_playing_sounds.begin();
i != m_playing_sounds.end();)
{
ServerPlayingSound &psound = i->second;
psound.clients.erase(c.peer_id);
if(psound.clients.size() == 0)
m_playing_sounds.erase(i++);
else
i++;
}
Player *player = m_env->getPlayer(c.peer_id);
// Collect information about leaving in chat
std::wstring message;
{
if(player != NULL)
{
std::wstring name = narrow_to_wide(player->getName());
message += L"*** ";
message += name;
message += L" left the game.";
if(c.timeout)
message += L" (timed out)";
}
/* Run scripts and remove from environment */
{
if(player != NULL)
{
PlayerSAO *playersao = player->getPlayerSAO();
assert(playersao);
scriptapi_on_leaveplayer(m_lua, playersao);
playersao->disconnected();
}
}
Perttu Ahola
committed
if(player != NULL)
{
std::ostringstream os(std::ios_base::binary);
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
i.atEnd() == false; i++)
{
RemoteClient *client = i.getNode()->getValue();
assert(client->peer_id == i.getNode()->getKey());
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
// Get player
if(!player)
continue;
// Get name of player
os<<player->getName()<<" ";
}
Perttu Ahola
committed
actionstream<<player->getName()<<" "
<<(c.timeout?"times out.":"leaves game.")
<<" List of players: "
<<os.str()<<std::endl;
}
}
// Delete client
delete m_clients[c.peer_id];
m_clients.remove(c.peer_id);
// Send player info to all remaining clients
Perttu Ahola
committed
//SendPlayerInfos();
// Send leave chat message to all remaining clients
Kahrl
committed
if(message.length() != 0)
BroadcastChatMessage(message);