bump deps ver, use result instead of panic for fs watcher, hardcode dahlia tech to 1st unit

This commit is contained in:
yuvlian
2026-04-30 20:31:00 +07:00
parent 5e0f2a9673
commit 676bd63dac
12 changed files with 3151 additions and 3060 deletions
+20 -20
View File
@@ -7,53 +7,53 @@ version = "0.1.0"
[workspace.dependencies] [workspace.dependencies]
# Framework # Framework
tokio = { version = "1.36.0", features = ["full"] } tokio = { version = "1.52.1", features = ["full"] }
axum = "0.8.1" axum = "0.8.9"
axum-server = "0.7.1" axum-server = "0.8.0"
tower-http = "0.6.2" tower-http = "0.6.8"
reqwest = { version = "0.12", default-features = false, features = [ reqwest = { version = "0.13.3", default-features = false, features = [
"json", "json",
"multipart", "multipart",
"cookies", "cookies",
"gzip", "gzip",
"brotli", "brotli",
"deflate", "deflate",
"rustls-tls", "rustls",
"charset", "charset",
"http2", "http2",
"macos-system-configuration", "system-proxy",
] } ] }
# JSON # JSON
serde = { version = "1.0.197", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.114" serde_json = "1.0.149"
# Logging # Logging
tracing = "0.1.40" tracing = "0.1.44"
ansi_term = "0.12.1" ansi_term = "0.12.1"
env_logger = "0.11.3" env_logger = "0.11.10"
# Cryptography # Cryptography
rsa = { version = "0.9.6", features = [ rsa = { version = "0.9.10", features = [
"sha1", "sha1",
"nightly", "nightly",
"pkcs5", "pkcs5",
"serde", "serde",
"sha2", "sha2",
] } ] }
rand = "0.9.0" rand = "0.10.1"
# Serialization # Serialization
prost = "0.13.5" prost = "0.14.3"
prost-types = "0.13.5" prost-types = "0.14.3"
prost-build = "0.13.5" prost-build = "0.14.3"
rbase64 = "2.0.3" rbase64 = "2.0.3"
# Utilities # Utilities
paste = "1.0.14" paste = "1.0.15"
notify = "8.0.0" notify = "8.2.0"
notify-debouncer-mini = "0.6.0" notify-debouncer-mini = "0.7.0"
anyhow = "1.0.81" anyhow = "1.0.102"
# Local # Local
proto = { path = "proto/" } proto = { path = "proto/" }
+1
View File
@@ -9,3 +9,4 @@ serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
tokio.workspace = true tokio.workspace = true
tracing.workspace = true tracing.workspace = true
anyhow.workspace = true
+13 -15
View File
@@ -56,14 +56,14 @@ impl FreesrData {
} }
impl FreesrData { impl FreesrData {
pub async fn load() -> Self { pub async fn load() -> anyhow::Result<Self> {
let mut freesr_data: FreesrData = tokio::fs::read_to_string("freesr-data.json") let mut freesr_data: FreesrData = tokio::fs::read_to_string("freesr-data.json")
.await .await
.map(|v| { .map_err(|e| anyhow::anyhow!("failed to read freesr-data.json: {e}"))
.and_then(|v| {
serde_json::from_str::<FreesrData>(&v) serde_json::from_str::<FreesrData>(&v)
.expect("freesr-data.json is broken, pls redownload") .map_err(|e| anyhow::anyhow!("freesr-data.json is broken: {e}, pls redownload"))
}) })?;
.expect("failed to read freesr-data.json");
let persistent: Persistent = serde_json::from_str( let persistent: Persistent = serde_json::from_str(
&tokio::fs::read_to_string("persistent") &tokio::fs::read_to_string("persistent")
@@ -96,11 +96,12 @@ impl FreesrData {
freesr_data.march_type = MultiPathAvatar::MarchHunt; freesr_data.march_type = MultiPathAvatar::MarchHunt;
} }
freesr_data Ok(freesr_data)
} }
pub async fn update(&mut self) { pub async fn update(&mut self) -> anyhow::Result<()> {
*self = Self::load().await *self = Self::load().await?;
Ok(())
} }
async fn verify_lineup(&mut self) { async fn verify_lineup(&mut self) {
@@ -115,9 +116,7 @@ impl FreesrData {
} }
pub async fn save_persistent(&self) { pub async fn save_persistent(&self) {
let _ = tokio::fs::write( if let Ok(data) = serde_json::to_string_pretty(&Persistent {
"persistent",
serde_json::to_string_pretty(&Persistent {
lineups: self.lineups.clone(), lineups: self.lineups.clone(),
main_character: self.main_character, main_character: self.main_character,
position: self.position.clone(), position: self.position.clone(),
@@ -125,10 +124,9 @@ impl FreesrData {
march_type: self.march_type, march_type: self.march_type,
// game_language: self.game_language, // game_language: self.game_language,
// voice_language: self.voice_langauge, // voice_language: self.voice_langauge,
}) }) {
.unwrap(), let _ = tokio::fs::write("persistent", data).await;
)
.await;
tracing::info!("persistent saved"); tracing::info!("persistent saved");
} }
} }
}
+25 -14
View File
@@ -12,7 +12,7 @@ use std::{
use anyhow::Result; use anyhow::Result;
use common::sr_tools::FreesrData; use common::sr_tools::FreesrData;
use rand::RngCore; use rand::Rng;
use crate::net::PlayerSession; use crate::net::PlayerSession;
@@ -86,11 +86,11 @@ impl Gateway {
))); )));
// Init the json to session // Init the json to session
let _ = session if let Ok(data) = FreesrData::load().await {
.write() let _ = session.write().await.json_data.set(data);
.await } else {
.json_data tracing::error!("Failed to load initial freesr-data.json");
.set(FreesrData::load().await); }
let session_ref = session.clone(); let session_ref = session.clone();
@@ -98,17 +98,25 @@ impl Gateway {
tokio::spawn(async move { tokio::spawn(async move {
let (tx, mut rx) = mpsc::channel(100); let (tx, mut rx) = mpsc::channel(100);
let mut debouncer = let mut debouncer =
notify_debouncer_mini::new_debouncer(Duration::from_millis(1000), move |ev| { match notify_debouncer_mini::new_debouncer(Duration::from_millis(1000), move |ev| {
let _ = tx.blocking_send(ev); let _ = tx.blocking_send(ev);
}) }) {
.unwrap(); Ok(d) => d,
Err(e) => {
tracing::error!("debouncer init err: {e}");
return;
}
};
let path = Path::new("freesr-data.json"); let path = Path::new("freesr-data.json");
debouncer if let Err(e) = debouncer
.watcher() .watcher()
.watch(path, notify::RecursiveMode::NonRecursive) .watch(path, notify::RecursiveMode::NonRecursive)
.unwrap(); {
tracing::error!("failed to watch {path:?}: {e}");
return;
}
tracing::info!("watching freesr-data.json changes"); tracing::info!("watching freesr-data.json changes");
@@ -136,13 +144,16 @@ impl Gateway {
let mut session = session.write().await; let mut session = session.write().await;
if let Some(json) = session.json_data.get_mut() { if let Some(json) = session.json_data.get_mut() {
let _ = json.update().await; if let Err(e) = json.update().await {
session.sync_player().await; tracing::error!("Failed to update json: {e}");
} else {
let _ = session.sync_player().await;
tracing::info!("json updated"); tracing::info!("json updated");
} }
} }
} }
Err(e) => eprintln!("json watcher error: {e:?}"), }
Err(e) => tracing::error!("json watcher error: {e:?}"),
} }
} }
_ = shutdown_rx.changed() => { _ = shutdown_rx.changed() => {
+95 -13
View File
@@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use rand::Rng; use rand::RngExt;
use common::{ use common::{
resources::GAME_RES, resources::GAME_RES,
@@ -98,7 +98,10 @@ async fn create_battle_info(
..Default::default() ..Default::default()
}; };
let is_calyx = caster_id == 0 && skill_index == 0;
let mut has_dahlia = false;
let mut first_avatar_id = 0; let mut first_avatar_id = 0;
let mut first_avatar_idx = 9999;
let lineup_uwu = if let Some(custom) = &player.battle_config.custom_battle_lineup { let lineup_uwu = if let Some(custom) = &player.battle_config.custom_battle_lineup {
custom.iter() custom.iter()
@@ -110,6 +113,10 @@ async fn create_battle_info(
for (avatar_index, avatar_id) in lineup_uwu { for (avatar_index, avatar_id) in lineup_uwu {
if first_avatar_id == 0 { if first_avatar_id == 0 {
first_avatar_id = *avatar_id; first_avatar_id = *avatar_id;
first_avatar_idx = *avatar_index;
}
if !has_dahlia {
has_dahlia = *avatar_id == 1321;
} }
let is_trailblazer = *avatar_id == 8001; let is_trailblazer = *avatar_id == 8001;
@@ -136,9 +143,8 @@ async fn create_battle_info(
.filter(|v| v.equip_avatar == avatar.avatar_id) .filter(|v| v.equip_avatar == avatar.avatar_id)
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
for tech in techs {
battle_info.buff_list.push(tech); battle_info.buff_list.extend(techs);
}
if caster_id > 0 if caster_id > 0
&& *avatar_index == (caster_id - 1) && *avatar_index == (caster_id - 1)
@@ -162,7 +168,7 @@ async fn create_battle_info(
// hardcoded march // hardcoded march
if avatar.avatar_id == 1224 { if avatar.avatar_id == 1224 {
let buffs = BattleBuff { battle_info.buff_list.push(BattleBuff {
id: 122401, id: 122401,
level: 3, level: 3,
wave_flag: 0xffffffff, wave_flag: 0xffffffff,
@@ -172,20 +178,67 @@ async fn create_battle_info(
(String::from("#ADF_2"), 3f32), (String::from("#ADF_2"), 3f32),
]), ]),
target_index_list: vec![0], target_index_list: vec![0],
}; });
battle_info.buff_list.push(buffs);
} }
}; };
} }
// Hardcoded Cerydra & Danheng PT technique // Hardcoded Cerydra & Danheng PT technique
if first_avatar_id != 0 { // and hardcode dahlia dance partner to 1st in lineup
for buff in battle_info.buff_list.iter_mut() { let first_avatar_attack_id = if let Some(c) = GAME_RES.avatar_configs.get(&first_avatar_id) {
c.weakness_buff_id
} else {
0
};
let len = battle_info.buff_list.len();
let mut has_replaced = false;
let mut dahlia_buffs = Vec::new();
for (i, buff) in battle_info.buff_list.iter_mut().enumerate() {
let is_last = i == len - 1;
if buff.id == 141202 || buff.id == 141403 { if buff.id == 141202 || buff.id == 141403 {
buff.owner_index = first_avatar_id buff.owner_index = first_avatar_id;
continue;
}
// this is actually useless because there is 0 attack buff id in calyx but idc
if has_dahlia
&& is_calyx
&& let 1000111..=1000117 = buff.id
&& !has_replaced
&& first_avatar_attack_id != 0
{
buff.id = first_avatar_attack_id;
buff.owner_index = first_avatar_id;
has_replaced = true;
}
if has_dahlia && is_calyx && !has_replaced && first_avatar_attack_id != 0 && is_last {
dahlia_buffs.push(BattleBuff {
id: 1000121,
level: 1,
wave_flag: u32::MAX,
target_index_list: vec![first_avatar_idx],
..Default::default()
});
dahlia_buffs.push(BattleBuff {
id: first_avatar_attack_id,
level: 1,
wave_flag: u32::MAX,
target_index_list: vec![first_avatar_idx],
dynamic_values: HashMap::from([(
String::from("SkillIndex"),
first_avatar_idx as f32,
)]),
..Default::default()
});
} }
} }
if !dahlia_buffs.is_empty() {
battle_info.buff_list.extend(dahlia_buffs);
} }
// custom stats for avatars // custom stats for avatars
@@ -380,8 +433,18 @@ async fn create_battle_info(
// }); // });
} }
// Global Buff // Global buffs
if !battle_info.buff_list.iter().any(|b| b.id == 140703) { let (mut has_castorice_global_buff, mut has_silver_wolf_global_buff) = (false, false);
for buff in &battle_info.buff_list {
if buff.id == 140703 {
has_castorice_global_buff = true;
}
if buff.id == 150602 {
has_silver_wolf_global_buff = true;
}
}
if !has_castorice_global_buff {
battle_info.buff_list.push(BattleBuff { battle_info.buff_list.push(BattleBuff {
id: 140703, id: 140703,
level: 1, level: 1,
@@ -392,5 +455,24 @@ async fn create_battle_info(
}); });
} }
if !has_silver_wolf_global_buff {
battle_info.buff_list.push(BattleBuff {
id: 150602,
level: 1,
owner_index: u32::MAX,
wave_flag: u32::MAX,
target_index_list: Vec::with_capacity(0),
dynamic_values: HashMap::with_capacity(0),
});
}
if has_dahlia {
tracing::info!("DAHLIA FOUND!!!");
tracing::info!("BUFF LIST----------");
for buff in &battle_info.buff_list {
tracing::info!("{}", buff.id);
}
}
battle_info battle_info
} }
+2 -2
View File
@@ -110,7 +110,7 @@ pub async fn on_send_msg_cs_req(
if let Some((cmd, args)) = parse_command(msg) { if let Some((cmd, args)) = parse_command(msg) {
match cmd { match cmd {
"sync" => { "sync" => {
session.sync_player().await; let _ = session.sync_player().await;
session session
.send(create_send_message( .send(create_send_message(
25, 25,
@@ -144,7 +144,7 @@ pub async fn on_send_msg_cs_req(
.await .await
.unwrap(); .unwrap();
session.sync_player().await; let _ = session.sync_player().await;
session session
.send(create_send_message( .send(create_send_message(
+5 -9
View File
@@ -116,10 +116,10 @@ impl PlayerSession {
Ok(()) Ok(())
} }
pub async fn sync_player(&self) { pub async fn sync_player(&self) -> Result<()> {
let Some(json) = self.json_data.get() else { let Some(json) = self.json_data.get() else {
tracing::error!("data is not init!"); tracing::error!("data is not init!");
return; return Ok(());
}; };
// clear relics & lightcones // clear relics & lightcones
@@ -128,8 +128,7 @@ impl PlayerSession {
del_equipment_list: (3001..=3500).collect(), del_equipment_list: (3001..=3500).collect(),
..Default::default() ..Default::default()
}) })
.await .await?;
.unwrap();
// clear all avatars equip item // clear all avatars equip item
self.send(PlayerSyncScNotify { self.send(PlayerSyncScNotify {
@@ -157,8 +156,7 @@ impl PlayerSession {
..Default::default() ..Default::default()
}) })
.await .await?;
.unwrap();
// Sync new relics & lightcones // Sync new relics & lightcones
self.send(PlayerSyncScNotify { self.send(PlayerSyncScNotify {
@@ -166,8 +164,7 @@ impl PlayerSession {
equipment_list: json.lightcones.iter().map(|v| v.into()).collect(), equipment_list: json.lightcones.iter().map(|v| v.into()).collect(),
..Default::default() ..Default::default()
}) })
.await .await?;
.unwrap();
// Sync new avatars equip item // Sync new avatars equip item
self.send(PlayerSyncScNotify { self.send(PlayerSyncScNotify {
@@ -203,7 +200,6 @@ impl PlayerSession {
..Default::default() ..Default::default()
}) })
.await .await
.unwrap()
} }
fn session_time(&self) -> u64 { fn session_time(&self) -> u64 {
+1 -1
View File
@@ -16,7 +16,7 @@ use bytes::{Buf, BufMut, BytesMut};
#[cfg(feature = "tokio")] #[cfg(feature = "tokio")]
use tokio::io::{AsyncWrite, AsyncWriteExt}; use tokio::io::{AsyncWrite, AsyncWriteExt};
use crate::{error::Error, KcpResult}; use crate::{KcpResult, error::Error};
const KCP_RTO_NDL: u32 = 20; // no delay min rto const KCP_RTO_NDL: u32 = 20; // no delay min rto
const KCP_RTO_MIN: u32 = 100; // normal min rto const KCP_RTO_MIN: u32 = 100; // normal min rto
+2 -2
View File
@@ -7,11 +7,11 @@ mod kcp;
/// The `KCP` prelude /// The `KCP` prelude
pub mod prelude { pub mod prelude {
pub use super::{get_conv, Kcp, KCP_OVERHEAD}; pub use super::{KCP_OVERHEAD, Kcp, get_conv};
} }
pub use error::Error; pub use error::Error;
pub use kcp::{get_conv, get_sn, set_conv, Kcp, KCP_OVERHEAD}; pub use kcp::{KCP_OVERHEAD, Kcp, get_conv, get_sn, set_conv};
/// KCP result /// KCP result
pub type KcpResult<T> = Result<T, Error>; pub type KcpResult<T> = Result<T, Error>;
+2971 -2971
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
ignore = [
"proto/"
]
+1 -1
View File
@@ -105,7 +105,7 @@ pub async fn start_sdkserver() -> Result<()> {
.fallback(errors::not_found); .fallback(errors::not_found);
let addr = format!("0.0.0.0:{PORT}"); let addr = format!("0.0.0.0:{PORT}");
let server = axum_server::bind(addr.parse()?); let server = axum_server::bind(addr.parse::<std::net::SocketAddr>()?);
tracing::info!("sdkserver is listening at {addr}"); tracing::info!("sdkserver is listening at {addr}");
server.serve(router.into_make_service()).await?; server.serve(router.into_make_service()).await?;