diff --git a/data/fontlucida.png b/data/fontlucida.png index c63fa02..4f22411 100644 Binary files a/data/fontlucida.png and b/data/fontlucida.png differ diff --git a/minetest.conf.example b/minetest.conf.example index 30adc5e..b706bc3 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -154,4 +154,9 @@ # Set to true to enable experimental features or stuff that is tested # (varies from version to version, usually not useful at all) #enable_experimental = false - +# 0 - disables crafted teleports. 1 - teleport work if target area exists and there are 2 air/water (clear) tiles +# 2 - teleport A works iff it points to clear area containing teleport B, and teleport B points back to teleport A +# 3 or more - just like 2 but there can be 3 of more teleports in loop (warning - high value is bad for server performance) +#crafted_teleports = 4 +#are players allowed to build connecting to border/edge of hostile clan territory +#build_on_borders = false diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 7f0d46a..3e1c98e 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -109,5 +109,7 @@ void set_default_settings(Settings *settings) settings->setDefault("server_map_save_interval", "10"); settings->setDefault("full_block_send_enable_min_time_from_building", "2.0"); settings->setDefault("enable_experimental", "false"); + settings->setDefault("crafted_teleports", "4"); + settings->setDefault("build_on_borders", "false"); } diff --git a/src/game.cpp b/src/game.cpp index 199d16a..07d2700 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -936,12 +936,14 @@ void the_game( L"", core::rect(0,0,500,text_height*2+5) + v2s32(100,200), //j false, false, guiroot); + guitext_info->setBackgroundColor(video::SColor(0x20000000)); // Ownership info (clans) //j gui::IGUIStaticText *guitext_ownership = guienv->addStaticText( L"", - core::rect(0,0,200,text_height+5) + v2s32(100,450), //j + core::rect(0,0,500,text_height+5) + v2s32(100,450), //j false, false, guiroot); + guitext_ownership->setBackgroundColor(video::SColor(0x20000000)); // Chat text gui::IGUIStaticText *guitext_chat = guienv->addStaticText( @@ -1765,8 +1767,12 @@ void the_game( bool canModifyNeighbour = player->canModify(&client.getEnv()->clansManager,NULL,block2,NULL,NULL); bool canModify = canModifyNeighbour && player->canModify(&client.getEnv()->clansManager,NULL,block,NULL,NULL); - if(block_owner || block2_owner ) + if(block_owner || block2_owner ){ + if(block_owner == block2_owner) ownershiptext = L"Property of clan "; + else + ownershiptext = L"Border of clan "; + } if(block_owner) ownershiptext += narrow_to_wide(clansManager->clanNameNoEx(block_owner)); @@ -1797,7 +1803,43 @@ void the_game( NodeMetadata *meta = client.getNodeMetadata(nodepos); if(meta) { + //maybe server should not be sending teleport info to clients at all? infotext = narrow_to_wide(meta->infoText()); + content_t content = map->getNodeNoEx(nodepos).getContent(); + if(content == CONTENT_TELEPORT) + { + // meta/infotext contains text inside "" quotes. + // find 3rd comma + int icomma=infotext.find(L','); + if(icomma>0) + icomma=infotext.find(L',',icomma+1); + if(icomma>0) + icomma=infotext.find(L',',icomma+1); + + if(!canModify) + { + if(icomma<0) + infotext = L"Unnamed teleport"; + else + infotext=L"Teleport: "+infotext.substr(icomma+1,infotext.length()-icomma-2); + } + else + { + if(icomma<0) + infotext = infotext.substr(0,infotext.length()-1)+L",Unnamed\""; + } + } + else + if(content == CONTENT_BORDERSTONE) + { char ts[50]; + v3s16 tp=getContainerPos(nodepos,MAP_BLOCKSIZE); + tp*=MAP_BLOCKSIZE; + snprintf(ts, 50, "Protected area: %i<=X<%i, %i<=Y<%i, %i<=Z<%i", + tp.X,tp.X+MAP_BLOCKSIZE, + tp.Y,tp.Y+MAP_BLOCKSIZE, + tp.Z,tp.Z+MAP_BLOCKSIZE); + infotext=narrow_to_wide(ts).c_str(); + } } //MapNode node = client.getNode(nodepos); @@ -1932,7 +1974,7 @@ void the_game( } } - if(canModify && input->getRightClicked()) + if(canModifyNeighbour && input->getRightClicked()) { infostream<<"Ground right-clicked"<setText(infotext.c_str()); - guitext_ownership->setText(ownershiptext.c_str()); + if(infotext.length()>0){ + guitext_info->setVisible(true); + guitext_info->setText(infotext.c_str()); + guitext_info->setDrawBackground( true ); + guitext_info->setMaxSize( + core::dimension2du( + guitext_info->getTextWidth(), + guitext_info->getTextHeight() + ) + ); + }else guitext_info->setVisible(false); + + if(ownershiptext.length()>0){ + guitext_ownership->setVisible(true); + guitext_ownership->setText(ownershiptext.c_str()); + guitext_ownership->setDrawBackground( true ); + guitext_ownership->setMaxSize( + core::dimension2du( + guitext_ownership->getTextWidth(), + guitext_ownership->getTextHeight() + ) + ); + }else guitext_ownership->setVisible(false); } /* diff --git a/src/map.cpp b/src/map.cpp index 12fe9b5..a47a877 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -901,7 +901,7 @@ void Map::updateLighting(core::map & a_blocks, /* */ void Map::addNodeAndUpdate(v3s16 p, MapNode n, - core::map &modified_blocks, std::string &player_name) + core::map &modified_blocks, std::string &player_name,NodeMetadata *initial_metadata) { /*PrintInfo(m_dout); m_dout<clone(); diff --git a/src/map.h b/src/map.h index c9bc817..06859c1 100644 --- a/src/map.h +++ b/src/map.h @@ -209,7 +209,8 @@ public: These handle lighting but not faces. */ void addNodeAndUpdate(v3s16 p, MapNode n, - core::map &modified_blocks, std::string &player_name); + core::map &modified_blocks, std::string &player_name, + NodeMetadata *initial_metadata=NULL); void removeNodeAndUpdate(v3s16 p, core::map &modified_blocks); diff --git a/src/player.cpp b/src/player.cpp index aed8ef1..b0b03c7 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -42,13 +42,15 @@ Player::Player(): craftresult_is_preview(true), hp(20), peer_id(PEER_ID_INEXISTENT), + clanOwner(0), + lastClan(0), m_selected_item(0), m_pitch(0), m_yaw(0), m_speed(0,0,0), - m_position(0,0,0), - clanOwner(0) + m_position(0,0,0) { + lastTeleportPos.X=FLT_MAX; updateName(""); resetInventory(); } @@ -848,27 +850,27 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, Check if standing on a teleport TODO: should it be at the beginning or at the end of this func? */ - v3s16 standPos = floatToInt(position - v3f(0,BS/2,0), BS); - MapNode standNode = map.getNodeNoEx(standPos); - if(standNode.getContent() == CONTENT_TELEPORT){ - SignNodeMetadata* meta = (SignNodeMetadata*)map.getNodeMetadata(standPos); - if(meta){ - v3f t; - std::string text = meta->getText(); - str_replace_char(text,',',' '); - std::istringstream is(text); - is >> t.X >> t.Y >> t.Z; + //v3s16 standPos = floatToInt(position - v3f(0,BS/2,0), BS); + //MapNode standNode = map.getNodeNoEx(standPos); + //if(standNode.getContent() == CONTENT_TELEPORT){ + // SignNodeMetadata* meta = (SignNodeMetadata*)map.getNodeMetadata(standPos); + // if(meta){ + // v3f t; + // std::string text = meta->getText(); + // str_replace_char(text,',',' '); + // std::istringstream is(text); + // is >> t.X >> t.Y >> t.Z; - //TODO: map limits! - if( t.X > 32000 || t.X < -32000 || - t.Y > 32000 || t.Y < -32000 || - t.Z > 32000 || t.Z < -32000 || - (t.X == 0 && t.Y == 0 && t.Z == 0) - ) return; + // //TODO: map limits! + // if( t.X > 32000 || t.X < -32000 || + // t.Y > 32000 || t.Y < -32000 || + // t.Z > 32000 || t.Z < -32000 || + // (t.X == 0 && t.Y == 0 && t.Z == 0) + // ) return; - setPosition(t*BS); - } - } + // setPosition(t*BS); + // } + //} } void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d) diff --git a/src/player.h b/src/player.h index 8bc1774..faff2c2 100644 --- a/src/player.h +++ b/src/player.h @@ -172,6 +172,8 @@ public: std::set clansModerator; u16 clanOwner; bool canModify(const ClansManager* clansManager, Map* map, MapBlock* block, MapNode* node, v3s16* nodepos) const; + v3f lastTeleportPos; //server: remember position player teleported to, to let him move away. + u16 lastClan; //server: remember last clan put into borderstone inline bool isClanMember(u16 clan) const { if(clan==0)return false; //do we need this? return clans.find(clan) != clans.end(); diff --git a/src/server.cpp b/src/server.cpp index 0c6b024..4e65d1b 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1816,6 +1816,41 @@ void Server::Receive() } } +//TODO: move this to some map class? +bool getTeleportTarget(/*const*/ ServerEnvironment *m_env,/*in+out*/ v3s16 &where,/*out*/v3f &tgt) +{ + // actionstream<<"Is Teleport at: "<<"("<getMap().getNodeNoEx(where).getContent() == CONTENT_TELEPORT) + meta = (SignNodeMetadata*)m_env->getMap().getNodeMetadata(where); + else { + // check player "head block" + where.Y++; + if(where.YgetMap().getNodeNoEx(where).getContent() == CONTENT_TELEPORT) + meta = (SignNodeMetadata*)m_env->getMap().getNodeMetadata(where); + } + + if(meta){ + std::string text = meta->getText(); + if(text == "") + return false; + str_replace_char(text,',',' '); + std::istringstream is(text); + is >> tgt.X >> tgt.Y >> tgt.Z; + + if( tgt.X >= MAP_GENERATION_LIMIT || tgt.X <= -MAP_GENERATION_LIMIT || + tgt.Y >= MAP_GENERATION_LIMIT || tgt.Y <= -MAP_GENERATION_LIMIT || + tgt.Z >= MAP_GENERATION_LIMIT || tgt.Z <= -MAP_GENERATION_LIMIT || + ( tgt.X == 0 && tgt.Y == 0 && tgt.Z == 0 && text.substr(0,5) != "0 0 0" ) + ) return false; + // actionstream<<"It points to: "<<"("< 32000 || t.X < -32000 || - t.Y > 32000 || t.Y < -32000 || - t.Z > 32000 || t.Z < -32000 || - (t.X == 0 && t.Y == 0 && t.Z == 0) - ) return; - - dout_server << "Teleporting: " << text << std::endl; - player->setPosition(t*BS); - SendMovePlayer(player); + if(player->lastTeleportPos.X != FLT_MAX){ + if(player->getPosition().getDistanceFrom(player->lastTeleportPos) <= BS*3/2) //1 tile away + return; //allow player to leave teleport destination + else + player->lastTeleportPos.X=FLT_MAX; //player left previous teleport destination } - } + + v3f tgtf; + if(getTeleportTarget(&m_env,tele_posi,tgtf)){ + if(player->lastTeleportPos == tgtf*BS) + return; //already checked... and failed, so skip checks. + player->lastTeleportPos=tgtf*BS; + + // check: is there known and empty place to teleport to? + v3s16 tgti=floatToInt(tgtf, 1); + content_t c1,c2; + c1=m_env.getMap().getNodeNoEx(tgti).getContent(); + tgti.Y++; + c2=m_env.getMap().getNodeNoEx(tgti).getContent(); + if((c1==CONTENT_IGNORE)||(c2==CONTENT_IGNORE)) //teleporting to unknown space? + return; + if(content_features(c1).walkable || content_features(c2).walkable) + return; + + if(teleport_option > 1){ + tgti=floatToInt(tgtf, 1); + bool loop=false; + while(!loop && teleport_option>1) + { //check if there is teleport at primary teleport target, and it loops back. + v3f tgtfnext; + if(getTeleportTarget(&m_env,tgti,tgtfnext)){ + tgti=floatToInt(tgtfnext, 1); + loop=(tele_posi.getDistanceFrom(tgti)<=1); // tile away + //actionstream<<" D:"<setPosition(tgtf*BS); + SendMovePlayer(player); + } //teleport exists + } //teleport_option } else if(command == TOSERVER_GOTBLOCKS) { @@ -2410,8 +2473,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) p_over.Z = readS16(&data[13]); u16 item_i = readU16(&data[15]); - //j - if( !player->canModify(&m_env.clansManager,&m_env.getMap(),NULL,NULL,&p_under) || !player->canModify(&m_env.clansManager,&m_env.getMap(),NULL,NULL,&p_over) ) + //j,placki + bool canModifyOver = player->canModify(&m_env.clansManager,&m_env.getMap(),NULL,NULL,&p_over); + bool canModifyUnder = player->canModify(&m_env.clansManager,&m_env.getMap(),NULL,NULL,&p_under); + static const bool canBoB = g_settings->getBool("build_on_borders"); + if( action != 2 + && ( + ( !canBoB && (!canModifyOver || !canModifyUnder) ) + || ( canBoB && ((action == 1 && !canModifyOver) || (action != 1 && !canModifyUnder)) ) + ) + ) { derr_server<<"Player isn't owner of a block"<getMaterial() == CONTENT_BORDERSTONE) + { if(player->lastClan && player->clans.find(player->lastClan) == player->clans.end()) + player->lastClan=0; // was invalid (no longer member?) + if(!player->lastClan) + { if(player->clanOwner) + player->lastClan=player->clanOwner; + else + if(!player->clans.empty()) + player->lastClan=*player->clans.begin(); + } + if(!player->lastClan) + { + actionstream<getName()<<" failed to put borderstone"<getName()<<" will put cornerstone for clan " + <lastClan + <<" ["<lastClan)<<"]"<getOwner()) return; //already has owner! + + newowner=player->lastClan; + initial_metadata=new SignNodeMetadata("Property of "+m_env.clansManager.clanNameNoEx(player->lastClan)); + } // Reset build time counter getClient(peer_id)->m_time_from_building = 0.0; // Create node data - MaterialItem *mitem = (MaterialItem*)item; MapNode n; n.setContent(mitem->getMaterial()); @@ -2789,7 +2895,22 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) MapEditEventIgnorer ign(&m_ignore_map_edit_events); std::string p_name = std::string(player->getName()); - m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name); + m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name, initial_metadata); + if(initial_metadata) delete initial_metadata; + if(newowner) + { + MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(getNodeBlockPos(p_over)); + assert(block != NULL); + block->setOwner(newowner); + // force update for all clients + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd()==false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + if(client) client->SetBlockNotSent(getNodeBlockPos(p_over)); + } + } } /* Set blocks not sent to far players @@ -2995,25 +3116,30 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } u16 clan = (u16)i_clan;*/ - bool nullclan = false; - if(clanName == "#" || clanName == "" || clanName == "nobody") nullclan = true; +// bool nullclan = false; +// if(clanName == "#" || clanName == "" || clanName == "nobody") nullclan = true; u16 clan = 0; - if(!nullclan){ +// if(!nullclan){ clan = m_env.clansManager.clanId(clanName); if(!clan){ derr_server<<"Wrong clan name"<isClanMember(clan)) return; block->setOwner(clan); - if(clan) text = "Property of " + clanName; - else text = "Property of nobody"; + player->lastClan=clan; + text = "Property of " + m_env.clansManager.clanNameNoEx(clan); } else if( node.getContent() == CONTENT_TELEPORT ){ //j teleport @@ -4349,6 +4475,10 @@ void Server::SendBlocks(float dtime) /* Something random */ +void Server::KillPlayer(Player *player) +{ // to consider: in creative or if hp is disabled: just respawn, so inventory is not lost + HandlePlayerHP(player,32767); +} void Server::HandlePlayerHP(Player *player, s16 damage) { diff --git a/src/server.h b/src/server.h index 7b30d0a..1f83830 100644 --- a/src/server.h +++ b/src/server.h @@ -432,6 +432,7 @@ public: void BroadcastClanName(u16 clan, const std::string& name); //j void SendClanDeleted(u16 peer_id,u16 clan); //j void BroadcastClanDeleted(u16 clan); //j + void KillPlayer(Player *player); //p //void SendClanSpawn(u16 peer_id,u16 clan, v3f spawn); //j //void BroadcastClanSpawn(u16 clan, v3f spawn); //j diff --git a/src/servercommand.cpp b/src/servercommand.cpp index f953c18..f43ff75 100644 --- a/src/servercommand.cpp +++ b/src/servercommand.cpp @@ -338,6 +338,12 @@ void cmd_clanNew(std::wostringstream &os, } } +void cmd_die(std::wostringstream &os, + ServerCommandContext *ctx) +{ + ctx->server->KillPlayer(ctx->player); +} + //j void cmd_clanDelete(std::wostringstream &os, ServerCommandContext *ctx) @@ -610,6 +616,8 @@ std::wstring processServerCommand(ServerCommandContext *ctx) cmd_me(os, ctx); else if(ctx->parms[0] == L"clearobjects") cmd_clearobjects(os, ctx); + else if(ctx->parms[0] == L"die") + cmd_die(os, ctx); else if(ctx->parms[0] == L"clan-new") cmd_clanNew(os, ctx); else if(ctx->parms[0] == L"clan-delete")