use std::time::Duration;
use tokio::sync::mpsc;
use encoding_rs::SHIFT_JIS;
use crate::proxy::{send_once, url_encode, BoxError, ProxyMode};
use crate::realtime_map_view::{
parse_map, parse_players, parse_room_name, MapViewResult,
};
#[derive(Debug, Clone, Default)]
pub struct PollOptions {
pub proxy_uri: Option<String>,
}
const SERVER_CHECK_URL: &str =
"http://www7019ug.sakura.ne.jp/CHaserOnline003/Server/UserCheck";
const MAP_VIEW_URL: &str =
"http://www7019ug.sakura.ne.jp/CHaserOnline003/Server/MapView.jsp";
async fn authenticate(
user: &str,
pass: &str,
proxy_mode: &ProxyMode,
) -> Result<String, BoxError> {
let url = format!(
"{}?user={}&pass={}&select=mapview",
SERVER_CHECK_URL,
url_encode(user),
url_encode(pass),
);
let (_, headers, _) = send_once(&url, &[], proxy_mode).await?;
for val in headers.get_all("set-cookie").iter() {
for part in val.to_str().unwrap_or("").split(';') {
if let Some(id) = part.trim().strip_prefix("JSESSIONID=") {
return Ok(id.to_string());
}
}
}
Err("JSESSIONID not found in authentication response".into())
}
async fn fetch_map(
jsessionid: &str,
proxy_mode: &ProxyMode,
) -> Result<Option<MapViewResult>, BoxError> {
let cookie = format!("JSESSIONID={}", jsessionid);
let (status, _, body) =
send_once(MAP_VIEW_URL, &[("Cookie", cookie)], proxy_mode).await?;
if status >= 300 {
return Ok(None);
}
let (html, _, _) = SHIFT_JIS.decode(&body);
if html.contains("UserCheck") && html.contains(r#"type="text" name="user""#) {
return Ok(None);
}
let room_name = parse_room_name(&html);
let (map, turn, next_player) = parse_map(&html);
let dom = tl::parse(&html, tl::ParserOptions::default())?;
let players = parse_players(&dom);
Ok(Some(MapViewResult { room_name, turn, next_player, map, players }))
}
pub fn poll_map_view(
user: impl Into<String>,
pass: impl Into<String>,
interval: Duration,
opts: PollOptions,
) -> mpsc::Receiver<MapViewResult> {
let user = user.into();
let pass = pass.into();
let (tx, rx) = mpsc::channel(32);
tokio::spawn(async move {
let proxy_mode = ProxyMode::from_option(opts.proxy_uri.as_deref());
let mut jsessionid = loop {
match authenticate(&user, &pass, &proxy_mode).await {
Ok(id) => break id,
Err(e) => {
eprintln!("[poll_map_view] authentication failed: {e}; retrying in {interval:?}");
tokio::time::sleep(interval).await;
}
}
};
loop {
match fetch_map(&jsessionid, &proxy_mode).await {
Ok(Some(result)) => {
if tx.send(result).await.is_err() {
break;
}
}
Ok(None) => {
eprintln!("[poll_map_view] session expired, re-authenticating…");
loop {
match authenticate(&user, &pass, &proxy_mode).await {
Ok(id) => {
jsessionid = id;
break;
}
Err(e) => {
eprintln!("[poll_map_view] re-authentication failed: {e}; retrying in {interval:?}");
tokio::time::sleep(interval).await;
}
}
}
}
Err(e) => {
eprintln!("[poll_map_view] fetch error: {e}; skipping tick");
}
}
tokio::time::sleep(interval).await;
}
});
rx
}