1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-04-27 00:47:45 +00:00

More CommonSpawn, PlayerSpawn and NPCSpawn struct improvements

This is just a barrage of random stuff I needed to fix, along with finally
figuring out what subkinds are.
This commit is contained in:
Joshua Goins 2025-03-23 12:16:15 -04:00
parent 45f92e9e54
commit 37dbef99db
6 changed files with 134 additions and 69 deletions

View file

@ -12,7 +12,7 @@ use kawari::packet::{
};
use kawari::world::ipc::{
ClientZoneIpcData, CommonSpawn, GameMasterCommandType, GameMasterRank, ObjectKind,
OnlineStatus, ServerZoneIpcData, ServerZoneIpcSegment, ServerZoneIpcType,
OnlineStatus, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment, ServerZoneIpcType,
SocialListRequestType, StatusEffect,
};
use kawari::world::{
@ -326,22 +326,24 @@ async fn main() {
op_code: ServerZoneIpcType::PlayerSpawn,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::PlayerSpawn(PlayerSpawn {
account_id: connection.player_data.account_id,
content_id: connection.player_data.content_id,
current_world_id: config.world.world_id,
home_world_id: config.world.world_id,
gm_rank: GameMasterRank::Debug,
online_status: OnlineStatus::GameMasterBlue,
common: CommonSpawn {
current_world_id: config.world.world_id,
home_world_id: config.world.world_id,
class_job: 35,
name: chara_details.name,
hp_curr: 100,
hp_max: 100,
mp_curr: 100,
mp_max: 100,
object_kind: ObjectKind::Player,
gm_rank: GameMasterRank::Debug,
online_status: OnlineStatus::GameMasterBlue,
object_kind: ObjectKind::Player(
PlayerSubKind::Player,
),
look: chara_details.chara_make.customize,
fc_tag: "LOCAL".to_string(),
subtype: 4,
models: [
0, // head
89, // body

View file

@ -3,8 +3,9 @@ use crate::{
config::get_config,
packet::{PacketSegment, SegmentType},
world::ipc::{
ActorControl, ActorControlCategory, CommonSpawn, NpcSpawn, ObjectKind, PlayerSpawn,
ServerZoneIpcData, ServerZoneIpcSegment, ServerZoneIpcType, StatusEffectList,
ActorControl, ActorControlCategory, BattleNpcSubKind, CommonSpawn, NpcSpawn, ObjectKind,
PlayerSpawn, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment, ServerZoneIpcType,
StatusEffectList,
},
};
@ -103,22 +104,21 @@ impl ChatHandler {
server_id: 0,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::PlayerSpawn(PlayerSpawn {
some_unique_id: 1,
account_id: 1,
content_id: 1,
current_world_id: config.world.world_id,
home_world_id: config.world.world_id,
common: CommonSpawn {
current_world_id: config.world.world_id,
home_world_id: config.world.world_id,
class_job: 35,
name: "Test Actor".to_string(),
hp_curr: 100,
hp_max: 100,
mp_curr: 100,
mp_max: 100,
object_kind: ObjectKind::Player,
object_kind: ObjectKind::Player(PlayerSubKind::Player),
spawn_index: connection.get_free_spawn_index(),
look: CUSTOMIZE_DATA,
fc_tag: "LOCAL".to_string(),
subtype: 4,
models: [
0, // head
89, // body
@ -189,7 +189,7 @@ impl ChatHandler {
spawn_index: connection.get_free_spawn_index(),
bnpc_base: 13498,
bnpc_name: 10261,
object_kind: ObjectKind::BattleNpc,
object_kind: ObjectKind::BattleNpc(BattleNpcSubKind::Enemy),
target_id: ObjectTypeId {
object_id: ObjectId(connection.player_data.actor_id),
object_type: 0,
@ -233,6 +233,7 @@ impl ChatHandler {
server_id: 0,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::NpcSpawn(NpcSpawn {
aggression_mode: 1,
common: CommonSpawn {
hp_curr: 91,
hp_max: 91,
@ -241,7 +242,7 @@ impl ChatHandler {
spawn_index: connection.get_free_spawn_index(),
bnpc_base: 13498, // TODO: changing this prevents it from spawning...
bnpc_name: 405,
object_kind: ObjectKind::BattleNpc,
object_kind: ObjectKind::BattleNpc(BattleNpcSubKind::Enemy),
level: 1,
battalion: 4,
model_chara: 297,

View file

@ -10,25 +10,63 @@ use super::StatusEffect;
#[binrw]
#[brw(repr = u8)]
#[derive(Clone, PartialEq, Debug, Default)]
pub enum ObjectKind {
pub enum PlayerSubKind {
#[default]
Player = 4,
}
// See https://github.com/Caraxi/Dalamud/blob/e6017f96c09b8cde20e02371914ec25cfa989ef7/Dalamud/Game/ClientState/Objects/Enums/BattleNpcSubKind.cs#L6
#[binrw]
#[brw(repr = u8)]
#[derive(Clone, PartialEq, Debug, Default)]
pub enum BattleNpcSubKind {
#[default]
None = 0,
Player = 1,
BattleNpc = 2,
EventNpc = 3,
Treasure = 4,
Aetheryte = 5,
GatheringPoint = 6,
EventObj = 7,
Mount = 8,
Companion = 9,
Retainer = 10,
AreaObject = 11,
HousingEventObject = 12,
Cutscene = 13,
MjiObject = 14,
Ornament = 15,
CardStand = 16,
Part = 1,
Pet = 2,
Chocobo = 3,
Enemy = 5,
NpcPartyMember = 9,
}
#[binrw]
#[derive(Clone, PartialEq, Debug, Default)]
pub enum ObjectKind {
#[default]
#[brw(magic = 0u8)]
None,
#[brw(magic = 1u8)]
Player(PlayerSubKind),
#[brw(magic = 2u8)]
BattleNpc(BattleNpcSubKind),
#[brw(magic = 3u8)]
EventNpc,
#[brw(magic = 4u8)]
Treasure,
#[brw(magic = 5u8)]
Aetheryte,
#[brw(magic = 6u8)]
GatheringPoint,
#[brw(magic = 7u8)]
EventObj,
#[brw(magic = 8u8)]
Mount,
#[brw(magic = 9u8)]
Companion,
#[brw(magic = 10u8)]
Retainer,
#[brw(magic = 11u8)]
AreaObject,
#[brw(magic = 12u8)]
HousingEventObject,
#[brw(magic = 13u8)]
Cutscene,
#[brw(magic = 14u8)]
MjiObject,
#[brw(magic = 15u8)]
Ornament,
#[brw(magic = 16u8)]
CardStand,
}
#[binrw]
@ -52,7 +90,7 @@ pub enum OnlineStatus {
GameQA = 1,
GameMaster = 2,
GameMasterBlue = 3,
EventParticipant = 4, // used by NPCs?
EventParticipant = 4,
#[default]
Online = 47,
}
@ -78,22 +116,6 @@ pub enum GameMasterRank {
#[brw(little)]
#[derive(Debug, Clone, Default)]
pub struct CommonSpawn {
/// See Title Excel sheet
pub title_id: u16,
pub u1b: u16,
pub current_world_id: u16,
pub home_world_id: u16,
pub gm_rank: GameMasterRank,
pub u3c: u8,
pub u4: u8,
pub online_status: OnlineStatus,
pub pose: u8,
pub u5a: u8,
pub u5b: u8,
pub u5c: u8,
pub target_id: ObjectTypeId,
pub u6: u32,
pub u7: u32,
@ -133,8 +155,8 @@ pub struct CommonSpawn {
/// Argument used in CharacterMode.
// TODO: move to enum
pub persistent_emote: u8,
#[brw(pad_size_to = 2)] // for kinds that don't have a param
pub object_kind: ObjectKind,
pub subtype: u8,
pub voice: u8,
pub unk27: u8,
/// See Battalion Excel sheet. Used for determing whether it's friendy or an enemy.

View file

@ -36,7 +36,10 @@ mod npc_spawn;
pub use npc_spawn::NpcSpawn;
mod common_spawn;
pub use common_spawn::{CharacterMode, CommonSpawn, GameMasterRank, ObjectKind, OnlineStatus};
pub use common_spawn::{
BattleNpcSubKind, CharacterMode, CommonSpawn, GameMasterRank, ObjectKind, OnlineStatus,
PlayerSubKind,
};
mod status_effect_list;
pub use status_effect_list::StatusEffectList;

View file

@ -1,11 +1,24 @@
use binrw::binrw;
use super::CommonSpawn;
use super::{CommonSpawn, GameMasterRank, OnlineStatus};
#[binrw]
#[brw(little)]
#[derive(Debug, Clone, Default)]
pub struct NpcSpawn {
pub gimmick_id: u32,
pub u1b: u8,
pub u2b: u8,
pub gm_rank: GameMasterRank, // lol really? what does an NPC need GM rank privileges for?
pub u3b: u8,
pub aggression_mode: u8,
pub online_status: OnlineStatus,
pub u5a: u8,
pub pose: u8,
pub u5b: u32,
pub common: CommonSpawn,
pub padding: [u8; 10],
}
@ -18,7 +31,7 @@ mod tests {
use crate::{
common::INVALID_OBJECT_ID,
world::ipc::{CharacterMode, ObjectKind, OnlineStatus},
world::ipc::{BattleNpcSubKind, CharacterMode, ObjectKind, OnlineStatus},
};
use super::*;
@ -45,10 +58,13 @@ mod tests {
assert_eq!(npc_spawn.common.bnpc_name, 10261);
assert_eq!(npc_spawn.common.spawn_index, 56);
assert_eq!(npc_spawn.common.mode, CharacterMode::Normal);
assert_eq!(npc_spawn.common.object_kind, ObjectKind::BattleNpc);
assert_eq!(npc_spawn.common.subtype, 2);
assert_eq!(
npc_spawn.common.object_kind,
ObjectKind::BattleNpc(BattleNpcSubKind::Pet)
);
assert_eq!(npc_spawn.common.battalion, 0);
assert_eq!(npc_spawn.common.online_status, OnlineStatus::Offline); // TODO: why is this guy offline?
assert_eq!(npc_spawn.aggression_mode, 1); // passive
assert_eq!(npc_spawn.online_status, OnlineStatus::Offline);
}
#[test]
@ -73,11 +89,14 @@ mod tests {
assert_eq!(npc_spawn.common.bnpc_name, 405);
assert_eq!(npc_spawn.common.spawn_index, 14);
assert_eq!(npc_spawn.common.mode, CharacterMode::Normal);
assert_eq!(npc_spawn.common.object_kind, ObjectKind::BattleNpc);
assert_eq!(npc_spawn.common.subtype, 5);
assert_eq!(
npc_spawn.common.object_kind,
ObjectKind::BattleNpc(BattleNpcSubKind::Enemy)
);
assert_eq!(npc_spawn.common.battalion, 4);
assert_eq!(npc_spawn.common.parent_actor_id, INVALID_OBJECT_ID);
assert_eq!(npc_spawn.common.spawner_id, INVALID_OBJECT_ID);
assert_eq!(npc_spawn.common.online_status, OnlineStatus::EventParticipant);
assert_eq!(npc_spawn.aggression_mode, 1); // passive
assert_eq!(npc_spawn.online_status, OnlineStatus::Offline);
}
}

View file

@ -1,17 +1,33 @@
use binrw::binrw;
use super::CommonSpawn;
use super::{CommonSpawn, GameMasterRank, OnlineStatus};
#[binrw]
#[brw(little)]
#[derive(Debug, Clone, Default)]
pub struct PlayerSpawn {
// also shows up in the friends list.
pub some_unique_id: u32,
// yes, really.
pub account_id: u32,
#[brw(pad_before = 4)] // always empty?
pub content_id: u64,
/// See Title Excel sheet
pub title_id: u16,
pub u1b: u16,
pub current_world_id: u16,
pub home_world_id: u16,
pub gm_rank: GameMasterRank,
pub u3c: u8,
pub u4: u8,
pub online_status: OnlineStatus,
pub pose: u8,
pub u5a: u8,
pub u5b: u8,
pub u5c: u8,
pub common: CommonSpawn,
pub padding: [u8; 2],
@ -23,7 +39,7 @@ mod tests {
use binrw::BinRead;
use crate::world::ipc::{CharacterMode, ObjectKind, OnlineStatus};
use crate::world::ipc::{CharacterMode, ObjectKind, OnlineStatus, PlayerSubKind};
use super::*;
@ -36,8 +52,8 @@ mod tests {
let mut buffer = Cursor::new(&buffer);
let player_spawn = PlayerSpawn::read_le(&mut buffer).unwrap();
assert_eq!(player_spawn.common.current_world_id, 0x4F);
assert_eq!(player_spawn.common.home_world_id, 0x4F);
assert_eq!(player_spawn.current_world_id, 0x4F);
assert_eq!(player_spawn.home_world_id, 0x4F);
assert_eq!(player_spawn.common.hp_curr, 159);
assert_eq!(player_spawn.common.hp_max, 159);
assert_eq!(player_spawn.common.mp_curr, 10000);
@ -55,10 +71,12 @@ mod tests {
assert_eq!(player_spawn.common.look.gender, 1);
assert_eq!(player_spawn.common.look.bust, 100);
assert_eq!(player_spawn.common.fc_tag, "");
assert_eq!(player_spawn.common.subtype, 4);
assert_eq!(player_spawn.common.model_chara, 0);
assert_eq!(player_spawn.common.object_kind, ObjectKind::Player);
assert_eq!(
player_spawn.common.object_kind,
ObjectKind::Player(PlayerSubKind::Player)
);
assert_eq!(player_spawn.common.display_flags, 262144);
assert_eq!(player_spawn.common.online_status, OnlineStatus::Offline);
assert_eq!(player_spawn.online_status, OnlineStatus::Offline);
}
}