mirror of
https://github.com/redstrate/Kawari.git
synced 2025-07-23 13:07:45 +00:00
Implement more of the Content Finder
Now it can put you in the zone properly (just Satasha, really.) The "checking member status" window doesn't go away yet.
This commit is contained in:
parent
b608d32590
commit
60fcac80c2
6 changed files with 237 additions and 67 deletions
|
@ -83,7 +83,7 @@ physis = { git = "https://github.com/redstrate/physis", default-features = false
|
|||
bitflags = { version = "2.9", default-features = false }
|
||||
|
||||
# excel sheet data
|
||||
icarus = { git = "https://github.com/redstrate/Icarus", branch = "ver/2025.06.28.0000.0000", features = ["Warp", "Tribe", "ClassJob", "World", "TerritoryType", "Race", "Aetheryte", "EquipSlotCategory", "Action", "WeatherRate", "PlaceName", "GilShopItem"], default-features = false }
|
||||
icarus = { git = "https://github.com/redstrate/Icarus", branch = "ver/2025.06.28.0000.0000", features = ["Warp", "Tribe", "ClassJob", "World", "TerritoryType", "Race", "Aetheryte", "EquipSlotCategory", "Action", "WeatherRate", "PlaceName", "GilShopItem", "InstanceContent", "ContentFinderCondition"], default-features = false }
|
||||
|
||||
# navimesh visualization
|
||||
bevy = { version = "0.16", features = ["std",
|
||||
|
|
|
@ -251,14 +251,14 @@
|
|||
"size": 16
|
||||
},
|
||||
{
|
||||
"name": "ContentFinderFound",
|
||||
"name": "ContentFinderUpdate",
|
||||
"opcode": 619,
|
||||
"size": 40
|
||||
},
|
||||
{
|
||||
"name": "ContentFinderFound2",
|
||||
"opcode": 619,
|
||||
"size": 812
|
||||
"name": "ContentFinderFound",
|
||||
"opcode": 730,
|
||||
"size": 40
|
||||
},
|
||||
{
|
||||
"name": "ObjectSpawn",
|
||||
|
@ -314,6 +314,11 @@
|
|||
"name": "ItemObtainedLogMessage",
|
||||
"opcode": 456,
|
||||
"size": 22
|
||||
},
|
||||
{
|
||||
"name": "ContentFinderCommencing",
|
||||
"opcode": 485,
|
||||
"size": 24
|
||||
}
|
||||
],
|
||||
"ClientZoneIpcType": [
|
||||
|
@ -471,6 +476,11 @@
|
|||
"name": "ContentFinderRegister",
|
||||
"opcode": 991,
|
||||
"size": 40
|
||||
},
|
||||
{
|
||||
"name": "ContentFinderAction",
|
||||
"opcode": 409,
|
||||
"size": 8
|
||||
}
|
||||
],
|
||||
"ServerLobbyIpcType": [
|
||||
|
|
|
@ -1147,64 +1147,197 @@ async fn client_loop(
|
|||
}
|
||||
ClientZoneIpcData::ContentFinderRegister { content_ids, .. } => {
|
||||
tracing::info!("Searching for {content_ids:?}");
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
op_code: ServerZoneIpcType::ContentFinderFound,
|
||||
timestamp: timestamp_secs(),
|
||||
data: ServerZoneIpcData::ContentFinderFound {
|
||||
state1: 2,
|
||||
classjob_id: 1,
|
||||
unk1: [
|
||||
5,
|
||||
2,
|
||||
5,
|
||||
2,
|
||||
5,
|
||||
2,
|
||||
96,
|
||||
4,
|
||||
5,
|
||||
64,
|
||||
2,
|
||||
5,
|
||||
2,
|
||||
5,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
4,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
connection
|
||||
.send_segment(PacketSegment {
|
||||
source_actor: connection.player_data.actor_id,
|
||||
target_actor: connection.player_data.actor_id,
|
||||
segment_type: SegmentType::Ipc,
|
||||
data: SegmentData::Ipc { data: ipc },
|
||||
})
|
||||
.await;
|
||||
connection.queued_content = Some(content_ids[0]);
|
||||
|
||||
// update
|
||||
{
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
op_code: ServerZoneIpcType::ContentFinderUpdate,
|
||||
timestamp: timestamp_secs(),
|
||||
data: ServerZoneIpcData::ContentFinderUpdate {
|
||||
state1: 1,
|
||||
classjob_id: connection.player_data.classjob_id, // TODO: store what they registered with, because it can change
|
||||
unk1: [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
96,
|
||||
4,
|
||||
2,
|
||||
64,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
],
|
||||
content_ids: [
|
||||
4,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
unk2: [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
connection
|
||||
.send_segment(PacketSegment {
|
||||
source_actor: connection.player_data.actor_id,
|
||||
target_actor: connection.player_data.actor_id,
|
||||
segment_type: SegmentType::Ipc,
|
||||
data: SegmentData::Ipc { data: ipc },
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
// found
|
||||
{
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
op_code: ServerZoneIpcType::ContentFinderFound,
|
||||
timestamp: timestamp_secs(),
|
||||
data: ServerZoneIpcData::ContentFinderFound {
|
||||
unk1: [
|
||||
3,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
96,
|
||||
4,
|
||||
2,
|
||||
64,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
4,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
],
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
connection
|
||||
.send_segment(PacketSegment {
|
||||
source_actor: connection.player_data.actor_id,
|
||||
target_actor: connection.player_data.actor_id,
|
||||
segment_type: SegmentType::Ipc,
|
||||
data: SegmentData::Ipc { data: ipc },
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
ClientZoneIpcData::ContentFinderAction { unk1 } => {
|
||||
dbg!(unk1);
|
||||
|
||||
|
||||
// commencing
|
||||
{
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
op_code: ServerZoneIpcType::ContentFinderCommencing,
|
||||
timestamp: timestamp_secs(),
|
||||
data: ServerZoneIpcData::ContentFinderCommencing {
|
||||
unk1: [
|
||||
4,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
4,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
connection
|
||||
.send_segment(PacketSegment {
|
||||
source_actor: connection.player_data.actor_id,
|
||||
target_actor: connection.player_data.actor_id,
|
||||
segment_type: SegmentType::Ipc,
|
||||
data: SegmentData::Ipc { data: ipc },
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
// TODO: content finder should be moved to global state
|
||||
// For now, just send them to do the zone if they do anything
|
||||
let zone_id;
|
||||
{
|
||||
let mut game_data = game_data.lock().unwrap();
|
||||
zone_id = game_data.find_zone_for_content(connection.queued_content.unwrap());
|
||||
}
|
||||
|
||||
if let Some(zone_id) = zone_id {
|
||||
connection.change_zone(zone_id).await;
|
||||
} else {
|
||||
tracing::warn!("Failed to find zone id for content?!");
|
||||
}
|
||||
|
||||
connection.queued_content = None;
|
||||
}
|
||||
ClientZoneIpcData::Unknown { .. } => {
|
||||
tracing::warn!("Unknown packet {:?} recieved, this should be handled!", data.op_code);
|
||||
|
@ -1376,6 +1509,7 @@ async fn main() {
|
|||
gracefully_logged_out: false,
|
||||
weather_id: 0,
|
||||
obsfucation_data: ObsfucationData::default(),
|
||||
queued_content: None,
|
||||
});
|
||||
}
|
||||
Some((mut socket, _)) = handle_rcon(&rcon_listener) => {
|
||||
|
|
|
@ -3,8 +3,10 @@ use std::path::PathBuf;
|
|||
use icarus::Action::ActionSheet;
|
||||
use icarus::Aetheryte::AetheryteSheet;
|
||||
use icarus::ClassJob::ClassJobSheet;
|
||||
use icarus::ContentFinderCondition::ContentFinderConditionSheet;
|
||||
use icarus::EquipSlotCategory::EquipSlotCategorySheet;
|
||||
use icarus::GilShopItem::GilShopItemSheet;
|
||||
use icarus::InstanceContent::InstanceContentSheet;
|
||||
use icarus::PlaceName::PlaceNameSheet;
|
||||
use icarus::TerritoryType::TerritoryTypeSheet;
|
||||
use icarus::WeatherRate::WeatherRateSheet;
|
||||
|
@ -437,6 +439,20 @@ impl GameData {
|
|||
|
||||
self.get_item_info(ItemInfoQuery::ById(*item_id as u32))
|
||||
}
|
||||
|
||||
/// Gets the zone id for the given InstanceContent.
|
||||
pub fn find_zone_for_content(&mut self, content_id: u16) -> Option<u16> {
|
||||
let instance_content_sheet =
|
||||
InstanceContentSheet::read_from(&mut self.resource, Language::None).unwrap();
|
||||
let instance_content_row = instance_content_sheet.get_row(content_id as u32)?;
|
||||
|
||||
let content_finder_row_id = instance_content_row.ContentFinderCondition().into_u16()?;
|
||||
let content_finder_sheet =
|
||||
ContentFinderConditionSheet::read_from(&mut self.resource, Language::English).unwrap();
|
||||
let content_finder_row = content_finder_sheet.get_row(*content_finder_row_id as u32)?;
|
||||
|
||||
content_finder_row.TerritoryType().into_u16().copied()
|
||||
}
|
||||
}
|
||||
|
||||
// Simple enum for GameData::get_territory_name
|
||||
|
|
|
@ -459,8 +459,8 @@ pub enum ServerZoneIpcData {
|
|||
/// Unknown, seems to always be 0x00000200.
|
||||
unk2: u32,
|
||||
},
|
||||
#[br(pre_assert(*magic == ServerZoneIpcType::ContentFinderFound))]
|
||||
ContentFinderFound {
|
||||
#[br(pre_assert(*magic == ServerZoneIpcType::ContentFinderUpdate))]
|
||||
ContentFinderUpdate {
|
||||
/// 0 = Nothing happens
|
||||
/// 1 = Reserving server
|
||||
/// 2 = again? ^
|
||||
|
@ -469,10 +469,12 @@ pub enum ServerZoneIpcData {
|
|||
/// nothing appears to happen above 5
|
||||
state1: u8,
|
||||
classjob_id: u8,
|
||||
unk1: [u8; 38],
|
||||
unk1: [u8; 18],
|
||||
content_ids: [u16; 5],
|
||||
unk2: [u8; 10],
|
||||
},
|
||||
#[br(pre_assert(*magic == ServerZoneIpcType::ContentFinderFound2))]
|
||||
ContentFinderFound2 { unk1: [u8; 8] },
|
||||
#[br(pre_assert(*magic == ServerZoneIpcType::ContentFinderFound))]
|
||||
ContentFinderFound { unk1: [u8; 40] },
|
||||
#[br(pre_assert(*magic == ServerZoneIpcType::ObjectSpawn))]
|
||||
ObjectSpawn(ObjectSpawn),
|
||||
#[br(pre_assert(*magic == ServerZoneIpcType::ActorGauge))]
|
||||
|
@ -554,6 +556,9 @@ pub enum ServerZoneIpcData {
|
|||
},
|
||||
#[br(pre_assert(*magic == ServerZoneIpcType::EffectResult))]
|
||||
EffectResult(EffectResult),
|
||||
/// Sent to give you the green checkmark before entering a CF zone.
|
||||
#[br(pre_assert(*magic == ServerZoneIpcType::ContentFinderCommencing))]
|
||||
ContentFinderCommencing { unk1: [u8; 24] },
|
||||
Unknown {
|
||||
#[br(count = size - 32)]
|
||||
unk: Vec<u8>,
|
||||
|
@ -750,6 +755,8 @@ pub enum ClientZoneIpcData {
|
|||
#[brw(pad_after = 4)] // seems to empty
|
||||
content_ids: [u16; 5],
|
||||
},
|
||||
#[br(pre_assert(*magic == ClientZoneIpcType::ContentFinderAction))]
|
||||
ContentFinderAction { unk1: [u8; 8] },
|
||||
Unknown {
|
||||
#[br(count = size - 32)]
|
||||
unk: Vec<u8>,
|
||||
|
|
|
@ -139,6 +139,9 @@ pub struct ZoneConnection {
|
|||
pub weather_id: u16,
|
||||
|
||||
pub obsfucation_data: ObsfucationData,
|
||||
|
||||
// TODO: support more than one content in the queue
|
||||
pub queued_content: Option<u16>,
|
||||
}
|
||||
|
||||
impl ZoneConnection {
|
||||
|
|
Loading…
Add table
Reference in a new issue