1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-07-09 15:37:45 +00:00

Attempt to improve the accuracy of a couple things (#105)

Attempt to improve the accuracy of:
-ItemOperation: Now sends back ItemAcknowledgeAck as well as InventorySlotDiscard and *Fin when discarding items
-ClientTrigger::EventRelatedUnk -> send back commonly observed ActorControlSelf responses
This commit is contained in:
thedax 2025-07-05 19:05:43 -04:00 committed by GitHub
parent c292033960
commit 17967c9d6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 160 additions and 0 deletions

View file

@ -239,6 +239,16 @@
"name": "UnkResponse2",
"opcode": 772,
"size": 8
},
{
"name": "InventorySlotDiscard",
"opcode": 851,
"size": 48
},
{
"name": "InventorySlotDiscardFin",
"opcode": 854,
"size": 16
}
],
"ClientZoneIpcType": [

View file

@ -36,6 +36,10 @@ use tokio::sync::mpsc::{Receiver, UnboundedReceiver, UnboundedSender, channel, u
use tokio::sync::oneshot;
use tokio::task::JoinHandle;
use kawari::{
INVENTORY_ACTION_ACK_GENERAL, /*INVENTORY_ACTION_ACK_SHOP,*/ INVENTORY_ACTION_DISCARD,
};
fn spawn_main_loop() -> (ServerHandle, JoinHandle<()>) {
let (send, recv) = channel(64);
@ -789,7 +793,80 @@ async fn client_loop(
ClientZoneIpcData::ItemOperation(action) => {
tracing::info!("Client is modifying inventory! {action:#?}");
let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::InventoryActionAck,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::InventoryActionAck {
sequence: action.context_id,
action_type: INVENTORY_ACTION_ACK_GENERAL as u16,
},
..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;
if action.operation_type == INVENTORY_ACTION_DISCARD {
tracing::info!("Player is discarding from their inventory!");
let sequence = 0; // TODO: How is this decided? It seems to be a sequence value but it's not sent by the client! Perhaps it's a 'lifetime-of-the-character' value that simply gets increased for every inventory action ever taken?
let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::InventorySlotDiscard,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::InventorySlotDiscard {
unk1: sequence,
operation_type: action.operation_type,
src_actor_id: action.src_actor_id,
src_storage_id: action.src_storage_id,
src_container_index: action.src_container_index,
src_stack: action.src_stack,
src_catalog_id: action.src_catalog_id,
dst_actor_id: 0xE0000000,
dst_storage_id: 0xFFFF,
dst_container_index: 0xFFFF,
dst_catalog_id: 0x0000FFFF,
},
..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;
let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::InventorySlotDiscard,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::InventorySlotDiscardFin {
unk1: sequence,
unk2: sequence, // yes, this repeats, it's not a copy paste error
unk3: 0x90,
unk4: 0x200,
},
..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.player_data.inventory.process_action(action);
// TODO: This seems incorrect, the server wasn't observed to send updates here, but if we don't then the client doesn't realize the items have been modified
connection.send_inventory(true).await;
}
// TODO: Likely rename this opcode if non-gil shops also use this same opcode

View file

@ -109,6 +109,16 @@ pub enum ActorControlCategory {
#[brw(pad_before = 2)] // padding
emote: u32,
},
#[brw(magic = 0x1ffu16)]
EventRelatedUnk1 {
#[brw(pad_before = 2)] // padding
unk1: u32,
},
#[brw(magic = 0x200u16)]
EventRelatedUnk2 {
#[brw(pad_before = 2)] // padding
unk1: u32,
},
Unknown {
category: u16,
#[brw(pad_before = 2)] // padding

View file

@ -92,6 +92,7 @@ use crate::common::ObjectTypeId;
use crate::common::Position;
use crate::common::read_string;
use crate::common::write_string;
use crate::inventory::ContainerType;
use crate::opcodes::ClientZoneIpcType;
use crate::opcodes::ServerZoneIpcType;
use crate::packet::IPC_HEADER_SIZE;
@ -347,6 +348,42 @@ pub enum ServerZoneIpcData {
#[brw(pad_after = 7)]
unk1: u8,
},
#[br(pre_assert(*magic == ServerZoneIpcType::InventorySlotDiscard))]
InventorySlotDiscard {
/// This is later reused in InventorySlotDiscardFin, so it might be some sort of sequence or context id, but it's not the one sent by the client
unk1: u32,
/// Same as the one sent by the client, not the one that the server responds with in inventoryactionack!
operation_type: u8,
#[br(pad_before = 3)]
src_actor_id: u32,
src_storage_id: ContainerType,
src_container_index: u16,
#[br(pad_before = 2)]
src_stack: u32,
src_catalog_id: u32,
/// This is all static as far as I can tell, across two captures and a bunch of discards these never changed
/// seems to always be 3758096384 / E0 00 00 00
dst_actor_id: u32,
/// seems to always be 65535/0xFFFF
dst_storage_id: u16,
/// seems to always be 65535/0xFFFF
dst_container_index: u16,
/// seems to always be 0x0000FFFF
#[br(pad_after = 8)]
dst_catalog_id: u32,
},
#[br(pre_assert(*magic == ServerZoneIpcType::InventorySlotDiscardFin))]
InventorySlotDiscardFin {
/// Same value as unk1 in InventorySlotDiscard
unk1: u32,
/// Repeated unk1 value?
unk2: u32,
/// Unknown, seems to always be 0x00000090
unk3: u32,
/// Unknown, seems to always be 0x00000200
unk4: u32,
},
Unknown {
#[br(count = size - 32)]
unk: Vec<u8>,

View file

@ -87,3 +87,11 @@ pub const INVENTORY_ACTION_MOVE: u8 = 146;
/// The operation opcode/type when moving an item to a slot occupied by another in the inventory.
pub const INVENTORY_ACTION_EXCHANGE: u8 = 147;
/// The server's acknowledgement of a shop item being purchased.
pub const INVENTORY_ACTION_ACK_SHOP: u8 = 6;
/// The server's acknowledgement of the client modifying their inventory.
/// In the past, many more values were used according to Sapphire:
/// https://github.com/SapphireServer/Sapphire/blob/044bff026c01b4cc3a37cbc9b0881fadca3fc477/src/common/Common.h#L83
pub const INVENTORY_ACTION_ACK_GENERAL: u8 = 7;

View file

@ -323,6 +323,24 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
to_remove.push(id);
}
}
if let ClientTriggerCommand::EventRelatedUnk { .. } = &trigger.trigger
{
let msg = FromServer::ActorControlSelf(ActorControlSelf {
category: ActorControlCategory::EventRelatedUnk1 { unk1: 1 },
});
if handle.send(msg).is_err() {
to_remove.push(id);
}
let msg = FromServer::ActorControlSelf(ActorControlSelf {
category: ActorControlCategory::EventRelatedUnk2 { unk1: 0 },
});
if handle.send(msg).is_err() {
to_remove.push(id);
}
}
continue;
}