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]
# Framework
tokio = { version = "1.36.0", features = ["full"] }
axum = "0.8.1"
axum-server = "0.7.1"
tower-http = "0.6.2"
reqwest = { version = "0.12", default-features = false, features = [
tokio = { version = "1.52.1", features = ["full"] }
axum = "0.8.9"
axum-server = "0.8.0"
tower-http = "0.6.8"
reqwest = { version = "0.13.3", default-features = false, features = [
"json",
"multipart",
"cookies",
"gzip",
"brotli",
"deflate",
"rustls-tls",
"rustls",
"charset",
"http2",
"macos-system-configuration",
"system-proxy",
] }
# JSON
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.114"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"
# Logging
tracing = "0.1.40"
tracing = "0.1.44"
ansi_term = "0.12.1"
env_logger = "0.11.3"
env_logger = "0.11.10"
# Cryptography
rsa = { version = "0.9.6", features = [
rsa = { version = "0.9.10", features = [
"sha1",
"nightly",
"pkcs5",
"serde",
"sha2",
] }
rand = "0.9.0"
rand = "0.10.1"
# Serialization
prost = "0.13.5"
prost-types = "0.13.5"
prost-build = "0.13.5"
prost = "0.14.3"
prost-types = "0.14.3"
prost-build = "0.14.3"
rbase64 = "2.0.3"
# Utilities
paste = "1.0.14"
notify = "8.0.0"
notify-debouncer-mini = "0.6.0"
anyhow = "1.0.81"
paste = "1.0.15"
notify = "8.2.0"
notify-debouncer-mini = "0.7.0"
anyhow = "1.0.102"
# Local
proto = { path = "proto/" }
+1
View File
@@ -9,3 +9,4 @@ serde.workspace = true
serde_json.workspace = true
tokio.workspace = true
tracing.workspace = true
anyhow.workspace = true
+21 -23
View File
@@ -56,14 +56,14 @@ 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")
.await
.map(|v| {
.map_err(|e| anyhow::anyhow!("failed to read freesr-data.json: {e}"))
.and_then(|v| {
serde_json::from_str::<FreesrData>(&v)
.expect("freesr-data.json is broken, pls redownload")
})
.expect("failed to read freesr-data.json");
.map_err(|e| anyhow::anyhow!("freesr-data.json is broken: {e}, pls redownload"))
})?;
let persistent: Persistent = serde_json::from_str(
&tokio::fs::read_to_string("persistent")
@@ -96,11 +96,12 @@ impl FreesrData {
freesr_data.march_type = MultiPathAvatar::MarchHunt;
}
freesr_data
Ok(freesr_data)
}
pub async fn update(&mut self) {
*self = Self::load().await
pub async fn update(&mut self) -> anyhow::Result<()> {
*self = Self::load().await?;
Ok(())
}
async fn verify_lineup(&mut self) {
@@ -115,20 +116,17 @@ impl FreesrData {
}
pub async fn save_persistent(&self) {
let _ = tokio::fs::write(
"persistent",
serde_json::to_string_pretty(&Persistent {
lineups: self.lineups.clone(),
main_character: self.main_character,
position: self.position.clone(),
scene: self.scene.clone(),
march_type: self.march_type,
// game_language: self.game_language,
// voice_language: self.voice_langauge,
})
.unwrap(),
)
.await;
tracing::info!("persistent saved");
if let Ok(data) = serde_json::to_string_pretty(&Persistent {
lineups: self.lineups.clone(),
main_character: self.main_character,
position: self.position.clone(),
scene: self.scene.clone(),
march_type: self.march_type,
// game_language: self.game_language,
// voice_language: self.voice_langauge,
}) {
let _ = tokio::fs::write("persistent", data).await;
tracing::info!("persistent saved");
}
}
}
+27 -16
View File
@@ -12,7 +12,7 @@ use std::{
use anyhow::Result;
use common::sr_tools::FreesrData;
use rand::RngCore;
use rand::Rng;
use crate::net::PlayerSession;
@@ -85,12 +85,12 @@ impl Gateway {
session_token,
)));
// Init the json to session
let _ = session
.write()
.await
.json_data
.set(FreesrData::load().await);
// Init the json to session
if let Ok(data) = FreesrData::load().await {
let _ = session.write().await.json_data.set(data);
} else {
tracing::error!("Failed to load initial freesr-data.json");
}
let session_ref = session.clone();
@@ -98,17 +98,25 @@ impl Gateway {
tokio::spawn(async move {
let (tx, mut rx) = mpsc::channel(100);
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);
})
.unwrap();
}) {
Ok(d) => d,
Err(e) => {
tracing::error!("debouncer init err: {e}");
return;
}
};
let path = Path::new("freesr-data.json");
debouncer
if let Err(e) = debouncer
.watcher()
.watch(path, notify::RecursiveMode::NonRecursive)
.unwrap();
{
tracing::error!("failed to watch {path:?}: {e}");
return;
}
tracing::info!("watching freesr-data.json changes");
@@ -136,13 +144,16 @@ impl Gateway {
let mut session = session.write().await;
if let Some(json) = session.json_data.get_mut() {
let _ = json.update().await;
session.sync_player().await;
tracing::info!("json updated");
if let Err(e) = json.update().await {
tracing::error!("Failed to update json: {e}");
} else {
let _ = session.sync_player().await;
tracing::info!("json updated");
}
}
}
}
Err(e) => eprintln!("json watcher error: {e:?}"),
Err(e) => tracing::error!("json watcher error: {e:?}"),
}
}
_ = shutdown_rx.changed() => {
+97 -15
View File
@@ -1,6 +1,6 @@
use std::collections::HashMap;
use rand::Rng;
use rand::RngExt;
use common::{
resources::GAME_RES,
@@ -98,7 +98,10 @@ async fn create_battle_info(
..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_idx = 9999;
let lineup_uwu = if let Some(custom) = &player.battle_config.custom_battle_lineup {
custom.iter()
@@ -110,6 +113,10 @@ async fn create_battle_info(
for (avatar_index, avatar_id) in lineup_uwu {
if first_avatar_id == 0 {
first_avatar_id = *avatar_id;
first_avatar_idx = *avatar_index;
}
if !has_dahlia {
has_dahlia = *avatar_id == 1321;
}
let is_trailblazer = *avatar_id == 8001;
@@ -136,9 +143,8 @@ async fn create_battle_info(
.filter(|v| v.equip_avatar == avatar.avatar_id)
.collect::<Vec<_>>(),
);
for tech in techs {
battle_info.buff_list.push(tech);
}
battle_info.buff_list.extend(techs);
if caster_id > 0
&& *avatar_index == (caster_id - 1)
@@ -162,7 +168,7 @@ async fn create_battle_info(
// hardcoded march
if avatar.avatar_id == 1224 {
let buffs = BattleBuff {
battle_info.buff_list.push(BattleBuff {
id: 122401,
level: 3,
wave_flag: 0xffffffff,
@@ -172,20 +178,67 @@ async fn create_battle_info(
(String::from("#ADF_2"), 3f32),
]),
target_index_list: vec![0],
};
battle_info.buff_list.push(buffs);
});
}
};
}
// Hardcoded Cerydra & Danheng PT technique
if first_avatar_id != 0 {
for buff in battle_info.buff_list.iter_mut() {
if buff.id == 141202 || buff.id == 141403 {
buff.owner_index = first_avatar_id
}
// and hardcode dahlia dance partner to 1st in lineup
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 {
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
@@ -380,8 +433,18 @@ async fn create_battle_info(
// });
}
// Global Buff
if !battle_info.buff_list.iter().any(|b| b.id == 140703) {
// Global buffs
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 {
id: 140703,
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
}
+2 -2
View File
@@ -110,7 +110,7 @@ pub async fn on_send_msg_cs_req(
if let Some((cmd, args)) = parse_command(msg) {
match cmd {
"sync" => {
session.sync_player().await;
let _ = session.sync_player().await;
session
.send(create_send_message(
25,
@@ -144,7 +144,7 @@ pub async fn on_send_msg_cs_req(
.await
.unwrap();
session.sync_player().await;
let _ = session.sync_player().await;
session
.send(create_send_message(
+5 -9
View File
@@ -116,10 +116,10 @@ impl PlayerSession {
Ok(())
}
pub async fn sync_player(&self) {
pub async fn sync_player(&self) -> Result<()> {
let Some(json) = self.json_data.get() else {
tracing::error!("data is not init!");
return;
return Ok(());
};
// clear relics & lightcones
@@ -128,8 +128,7 @@ impl PlayerSession {
del_equipment_list: (3001..=3500).collect(),
..Default::default()
})
.await
.unwrap();
.await?;
// clear all avatars equip item
self.send(PlayerSyncScNotify {
@@ -157,8 +156,7 @@ impl PlayerSession {
..Default::default()
})
.await
.unwrap();
.await?;
// Sync new relics & lightcones
self.send(PlayerSyncScNotify {
@@ -166,8 +164,7 @@ impl PlayerSession {
equipment_list: json.lightcones.iter().map(|v| v.into()).collect(),
..Default::default()
})
.await
.unwrap();
.await?;
// Sync new avatars equip item
self.send(PlayerSyncScNotify {
@@ -203,7 +200,6 @@ impl PlayerSession {
..Default::default()
})
.await
.unwrap()
}
fn session_time(&self) -> u64 {
+1 -1
View File
@@ -16,7 +16,7 @@ use bytes::{Buf, BufMut, BytesMut};
#[cfg(feature = "tokio")]
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_MIN: u32 = 100; // normal min rto
+2 -2
View File
@@ -7,11 +7,11 @@ mod kcp;
/// The `KCP` 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 kcp::{get_conv, get_sn, set_conv, Kcp, KCP_OVERHEAD};
pub use kcp::{KCP_OVERHEAD, Kcp, get_conv, get_sn, set_conv};
/// KCP result
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);
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}");
server.serve(router.into_make_service()).await?;