Live map: plugin/mod chunk+player sync, admin panel Live Map section, launcher transport
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DjMbLQujBHunCCu5GpsHaT
This commit is contained in:
14 files changed
+769
-8
No files matched your search
@@ -637,7 +637,6 @@ pub struct MapResource {
|
||||
/// Only paths under a map URL supplied by the panel can be fetched.
|
||||
#[tauri::command]
|
||||
pub async fn fetch_vantage_resource(state: State<'_, AppState>, server_id: i64, url: String, etag: Option<String>) -> Res<MapResource> {
|
||||
use base64::Engine;
|
||||
let configured = state.map_urls.lock().unwrap().get(&server_id).cloned().ok_or("map not configured for this server")?;
|
||||
let base = reqwest::Url::parse(&configured).map_err(err)?;
|
||||
let target = reqwest::Url::parse(&url).map_err(err)?;
|
||||
@@ -651,15 +650,55 @@ pub async fn fetch_vantage_resource(state: State<'_, AppState>, server_id: i64,
|
||||
{
|
||||
return Err("map asset is outside the configured Vantage world".into());
|
||||
}
|
||||
fetch_map_asset(target, etag, None).await
|
||||
}
|
||||
|
||||
/// What the launcher needs to offer a Live Map button for one game server.
|
||||
#[tauri::command]
|
||||
pub async fn get_livemap_info(state: State<'_, AppState>, server_id: i64) -> Res<serde_json::Value> {
|
||||
let req = account_api(&state, reqwest::Method::GET, &format!("/servers/{server_id}/livemap")).await?;
|
||||
let resp = req.send().await.map_err(err)?;
|
||||
if !resp.status().is_success() {
|
||||
let body: serde_json::Value = resp.json().await.unwrap_or_default();
|
||||
return Err(body["error"].as_str().unwrap_or("unable to load the live map").to_string());
|
||||
}
|
||||
resp.json().await.map_err(err)
|
||||
}
|
||||
|
||||
/// Native transport for the panel-hosted live map. The session token is
|
||||
/// attached here, so it never reaches page scripts, and only URLs under
|
||||
/// this panel's `/api/livemap/<server>/` can be fetched.
|
||||
#[tauri::command]
|
||||
pub async fn fetch_livemap_resource(state: State<'_, AppState>, server_id: i64, url: String, etag: Option<String>) -> Res<MapResource> {
|
||||
let panel = state.panel_url().ok_or("no panel configured")?;
|
||||
let token = accounts::panel_token(&state).ok_or("sign in to view the live map")?;
|
||||
let base = reqwest::Url::parse(&format!("{}/api/livemap/{server_id}/", panel.trim_end_matches('/'))).map_err(err)?;
|
||||
let target = reqwest::Url::parse(&url).map_err(err)?;
|
||||
if target.origin() != base.origin()
|
||||
|| !target.path().starts_with(base.path())
|
||||
|| target.username() != ""
|
||||
|| target.password().is_some()
|
||||
|| target.fragment().is_some()
|
||||
{
|
||||
return Err("map asset is outside this server's live map".into());
|
||||
}
|
||||
fetch_map_asset(target, etag, Some(token)).await
|
||||
}
|
||||
|
||||
async fn fetch_map_asset(target: reqwest::Url, etag: Option<String>, bearer: Option<String>) -> Res<MapResource> {
|
||||
use base64::Engine;
|
||||
if target.as_str().len() > 2048 {
|
||||
return Err("map asset URL is too long".into());
|
||||
}
|
||||
let client = reqwest::Client::builder()
|
||||
.redirect(reqwest::redirect::Policy::none())
|
||||
.timeout(std::time::Duration::from_secs(30))
|
||||
.timeout(std::time::Duration::from_secs(60))
|
||||
.build()
|
||||
.map_err(err)?;
|
||||
let mut request = client.get(target);
|
||||
if let Some(token) = bearer {
|
||||
request = request.bearer_auth(token);
|
||||
}
|
||||
if let Some(etag) = etag.filter(|e| e.len() < 256 && !e.contains('\n') && !e.contains('\r')) {
|
||||
request = request.header(reqwest::header::IF_NONE_MATCH, etag);
|
||||
}
|
||||
|
||||
@@ -92,6 +92,8 @@ pub fn run() {
|
||||
commands::get_leaderboard,
|
||||
commands::get_public_servers,
|
||||
commands::fetch_vantage_resource,
|
||||
commands::fetch_livemap_resource,
|
||||
commands::get_livemap_info,
|
||||
commands::get_my_level,
|
||||
commands::get_my_quests,
|
||||
commands::claim_quest,
|
||||
|
||||
Reference in new issue
Block a user