From 53a3d5d6a4ff76ac94a8758e1e0d9f7791e6aca6 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sat, 21 Jun 2025 14:55:17 -0400 Subject: [PATCH] Load the actual weather rates for zones instead of using Sunny This doesn't match up with retail yet (I don't know why.) But this fixes the appearance of lots of dungeons and other instanced content that have their own special weather. --- Cargo.toml | 2 +- src/common/gamedata.rs | 63 +++++++++++++++++++++++++++++++++++++++++ src/world/connection.rs | 10 ++++++- 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 365c802..6840d29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,4 +104,4 @@ rkon = { version = "0.1" } tower-http = { version = "0.6", features = ["fs", "cors"] } # excel sheet data -icarus = { git = "https://github.com/redstrate/Icarus", branch = "ver/2025.04.16.0000.0000", features = ["Warp", "Tribe", "ClassJob", "World", "TerritoryType", "Race", "Aetheryte", "EquipSlotCategory", "Action"], default-features = false } +icarus = { git = "https://github.com/redstrate/Icarus", branch = "ver/2025.04.16.0000.0000", features = ["Warp", "Tribe", "ClassJob", "World", "TerritoryType", "Race", "Aetheryte", "EquipSlotCategory", "Action", "WeatherRate"], default-features = false } diff --git a/src/common/gamedata.rs b/src/common/gamedata.rs index e8020df..a6e2a9e 100644 --- a/src/common/gamedata.rs +++ b/src/common/gamedata.rs @@ -2,6 +2,8 @@ use icarus::Action::ActionSheet; use icarus::Aetheryte::AetheryteSheet; use icarus::ClassJob::ClassJobSheet; use icarus::EquipSlotCategory::EquipSlotCategorySheet; +use icarus::TerritoryType::TerritoryTypeSheet; +use icarus::WeatherRate::WeatherRateSheet; use icarus::World::WorldSheet; use icarus::{Tribe::TribeSheet, Warp::WarpSheet}; use physis::common::{Language, Platform}; @@ -10,6 +12,8 @@ use physis::exh::EXH; use crate::{common::Attributes, config::get_config}; +use super::timestamp_secs; + /// Convenient methods built on top of Physis to access data relevant to the server pub struct GameData { pub game_data: physis::gamedata::GameData, @@ -227,4 +231,63 @@ impl GameData { row.Cast100ms().into_u16().copied() } + + /// Calculates the current weather at the current time + // TODO: instead allow targetting a specific time to calculate forcecasts + pub fn get_weather_rate(&mut self, weather_rate_id: u32) -> Option { + let sheet = WeatherRateSheet::read_from(&mut self.game_data, Language::None)?; + let row = sheet.get_row(weather_rate_id)?; + + let target = Self::calculate_target(); + let weather_and_rates: Vec<(i32, i32)> = row + .Weather() + .iter() + .cloned() + .zip(row.Rate().clone()) + .map(|(x, y)| (*x.into_i32().unwrap(), *y.into_u8().unwrap() as i32)) + .collect(); + + Some( + weather_and_rates + .iter() + .filter(|(_, rate)| target < *rate) + .take(1) + .collect::>() + .first()? + .0, + ) + } + + /// Calculate target window for weather calculations + fn calculate_target() -> i32 { + // Based off of https://github.com/Rogueadyn/SaintCoinach/blob/master/SaintCoinach/Xiv/WeatherRate.cs + // TODO: this isn't correct still and doesn't seem to match up with the retail server + + let real_to_eorzean_factor = (60.0 * 24.0) / 70.0; + let unix = (timestamp_secs() as f32 / real_to_eorzean_factor) as u64; + // Get Eorzea hour for weather start + let bell = unix / 175; + // Do the magic 'cause for calculations 16:00 is 0, 00:00 is 8 and 08:00 is 16 + let increment = ((bell + 8 - (bell % 8)) as u32) % 24; + + // Take Eorzea days since unix epoch + let total_days = (unix / 4200) as u32; + + let calc_base = (total_days * 0x64) + increment; + + let step1 = (calc_base << 0xB) ^ calc_base; + let step2 = (step1 >> 8) ^ step1; + + (step2 % 0x64) as i32 + } + + /// Gets the current weather for the given zone id + pub fn get_weather(&mut self, zone_id: u32) -> Option { + let sheet = TerritoryTypeSheet::read_from(&mut self.game_data, Language::None)?; + let row = sheet.get_row(zone_id)?; + + let weather_rate_id = row.WeatherRate().into_u8()?; + + self.get_weather_rate(*weather_rate_id as u32) + } } diff --git a/src/world/connection.rs b/src/world/connection.rs index eb6229f..67a0fc7 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -346,12 +346,20 @@ impl ZoneConnection { { let config = get_config(); + let weather_id; + { + let mut game_data = self.gamedata.lock().unwrap(); + weather_id = game_data + .get_weather(self.zone.as_ref().unwrap().id.into()) + .unwrap_or(1) as u16; + } + let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::InitZone, timestamp: timestamp_secs(), data: ServerZoneIpcData::InitZone(InitZone { territory_type: self.zone.as_ref().unwrap().id, - weather_id: 1, + weather_id, obsfucation_mode: if config.world.enable_packet_obsfucation { OBFUSCATION_ENABLED_MODE } else {