Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core/src/common/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,8 @@ pub enum LogMessageType {
UnableToAcceptAttachmentInventoryFull = 0x17B, // Unable to accept attachment. Inventory is full.
FurnitureMovedToStoreroom = 0xD9F, // The <item name> was moved to your storeroom.
FurnitureMovedToInventory = 0xDA0, // The <item name> was moved to your inventory.
UnableToJoinMoreFellowships = 0x15FE, // You cannot join any more Fellowships.
UnableToCreateMoreFellowships = 0x15E3, // You cannot create any more Fellowshiops.
}

/// Names for rows in the Excel sheet of the same name.
Expand Down
11 changes: 11 additions & 0 deletions core/src/ipc/zone/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,17 @@ pub enum ClientZoneIpcData {
PerformanceNote {
unk1: [u8; 24],
},
CreateFellowship {
#[brw(pad_size_to = 60)]
#[br(count = 60)]
#[br(map = read_string)]
#[bw(map = write_string)]
#[brw(pad_after = 4)] // Seems to just be padding/garbage
name: String,
},
SearchFellowshipsJoin {
community_id: u64,
},
}

#[cfg(test)]
Expand Down
31 changes: 31 additions & 0 deletions core/src/ipc/zone/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,37 @@ pub struct StrategyBoardUpdate {
data: Vec<u8>,
}

/// Represents circle/Fellowship activity tags that players set for Fellowships they've created, or tags that can be used to search for Fellowships to join.
#[binrw]
#[brw(repr = u16)]
#[derive(Clone, Debug, Default)]
pub enum FellowshipActivityTag {
#[default]
None = 0,
RolePlaying = 1,
PlayerEvents = 2,
MakingFriends = 3,
NoviceSupport = 4,
Casual = 5,
Hardcore = 6,
Leveling = 7,
Battle = 8,
Crafting = 9,
Gathering = 10,
Housing = 11,
Hunts = 12,
TreasureHunt = 13,
PvP = 14,
Fishing = 15,
DomanMahjong = 16,
Performance = 17,
Glamours = 18,
GroupPose = 19,
TripleTriad = 20,
Collectables = 21,
Chatting = 22,
}

/// Represents housing appeal tags that players can set for their owned housing.
#[binrw]
#[brw(repr = u8)]
Expand Down
68 changes: 68 additions & 0 deletions core/src/ipc/zone/server/fellowships.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use binrw::binrw;
use bstr::BString;

use crate::{
common::{
CHAR_NAME_MAX_LENGTH, ClientLanguage, read_sestring, read_string, write_sestring,
write_string,
},
ipc::zone::{FellowshipActivityTag, SocialListUILanguages},
};

#[binrw]
#[derive(Clone, Default, Debug)]
pub struct FellowshipSearchInfo {
/// The Fellowship's community id (assumed).
pub community_id: u64,
/// The master or recruiter's content id (assumed).
pub master_content_id: u64, // TODO: Figure out which content id is which
/// The recruiter or master's content id (assumed).
pub recruiter_content_id: u64,
/// A 32-bit UNIX timestamp indicating when this recruitment listing will expire.
pub recruitment_deadline: u32,
/// The languages this Fellowship is seeking.
pub languages: SocialListUILanguages,
#[brw(pad_before = 3, pad_after = 3)] // Probably just padding
/// The primary/underlined language of this Fellowship. This seems to be set to the recruiter's client language at the time of Fellowship's creation, or during the time of starting a recruitment listing.
pub primary_language: ClientLanguage,
/// The home world of the Fellowship's recruiter.
pub recruiter_world_id: u16,
/// The home world of the Fellowship's master.
pub master_world_id: u16,
/// The current number of members in this Fellowship, out of 1000 (but the client will display numbers higher than 1000).
pub current_member_count: u16,
/// The number of members the recruiter is attempting to reach (up to 1000, but the client can display up to 65,535 properly).
pub target_member_count: u16,
/// The Fellowship's first activity icon. References the CircleActivity excel sheet.
pub activity1: FellowshipActivityTag,
pub unk1: [u8; 4], // Has values in it, but it's unclear what they control yet. Changing them doesn't seem to affect what the client shows in the Finder window.
/// The Fellowship's second activity icon. References the CircleActivity excel sheet.
pub activity2: FellowshipActivityTag,
/// The fellowship's third activity icon. References the CircleActivity excel sheet.
pub activity3: FellowshipActivityTag,
/// The name of the Fellowship. It can be up to 60 characters in length (fewer if using Unicode glyphs).
#[brw(pad_size_to = 61)]
#[br(count = 60)]
#[br(map = read_string)]
#[bw(map = write_string)]
pub fellowship_name: String,
/// The Fellowship recruiter's name. The recruiter can be the same person as the master.
#[brw(pad_size_to = CHAR_NAME_MAX_LENGTH)]
#[br(count = CHAR_NAME_MAX_LENGTH)]
#[br(map = read_string)]
#[bw(map = write_string)]
pub recruiter_name: String,
/// The Fellowship master's name.
#[brw(pad_size_to = CHAR_NAME_MAX_LENGTH)]
#[br(count = CHAR_NAME_MAX_LENGTH)]
#[br(map = read_string)]
#[bw(map = write_string)]
pub master_name: String,
/// The Fellowship's text description. This can be up to 192 characters in length (fewer if using Unicode glyphs).
#[brw(pad_after = 5)]
#[brw(pad_size_to = 192)]
#[br(count = 192)]
#[br(map = read_sestring)]
#[bw(map = write_sestring)]
pub fellowship_description: BString, // NOTE: This is a BString due to the fact that SEString macros can appear in its contents.
}
18 changes: 18 additions & 0 deletions core/src/ipc/zone/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ pub use marketboard::{MarketBoardHistory, MarketBoardHistoryEntry, MarketBoardIt
mod linkshell;
pub use linkshell::*;

mod fellowships;
pub use fellowships::*;

mod spawn_treasure;
pub use spawn_treasure::SpawnTreasure;

Expand Down Expand Up @@ -1506,6 +1509,21 @@ pub enum ServerZoneIpcData {
hits: u32,
},
ZoneDiceRollResult(ZoneDiceRollResult),
SearchFellowshipsNoResults {
#[brw(pad_before = 4, pad_after = 16)]
err_code: u32, // Name is assumed, observed as 0x01240C89. Changing this to an incorrect value seems to make the client wait for more results (the UI window acts as if it's waiting with a spinner)?
},
SearchFellowshipsResults {
#[brw(pad_before = 4)] // Seems to always be zeroes/padding
unk1: u32, // Seems to always be 0xFFFF_FFFF
sequence_end: u32, // Is typically 0xFFFF_FFFF, but seems to change to 0x28 as the final value when a number of FellowshipFinderResults have been sent. If only one is sent, it stays at 0xFFFF_FFFF and sequence_current will be 0. More research is needed for this.
/// The current sequence value, so the client knows which packet this is so far. If it's set to 0, the client won't expect more to be coming (the appropriate page arrows in the Finder window will be disabled).
sequence_current: u32,
/// The actual Fellowship result items.
#[br(count = 4)]
#[bw(pad_size_to = 4 * 376)]
fellowships: Vec<FellowshipSearchInfo>,
},
}

#[cfg(test)]
Expand Down
3 changes: 2 additions & 1 deletion docs/src/update_ingame_checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,6 @@ The following tests should also be visible to others in the zone upon completion
- [ ] Trading: attempt to trade with another player, and ensure the client tells you that the other player is unable trade.
- [ ] Run the `/playtime` command and ensure it displays your character's total play time.
For the following, ensure the following items display their correct unimplemented messages when trying to do them.
- [ ] Fellowships & Fellowship Finder
- [ ] Fellowships: ensure that creating a Fellowship displays "You cannot create any more Fellowships."
- [ ] Fellowship Finder: ensure that the window displays the sample Fellowship, and that attempting to join it displays "You cannot join any more Fellowships."
- [ ] Run the `/dice` and `/random` commands and ensure they display both for you and another player
18 changes: 17 additions & 1 deletion resources/data/opcodes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ ServerZoneIpcType:
size: 104
- name: Fellowships
comment: The fellowships you have joined.
opcode: 108
opcode: 439
size: 808
- name: DailyQuests
comment: Sent shortly after the server sends InitZone, this with UnkZoneLoad2 it makes everything load faster.
Expand Down Expand Up @@ -791,6 +791,14 @@ ServerZoneIpcType:
comment: Sent to the client and nearby players after the client sends a dice roll.
opcode: 801
size: 64
- name: SearchFellowshipsResults
comment: Sent to the client when their Fellowship Finder search yielded some results.
opcode: 682
size: 1520
- name: SearchFellowshipsNoResults
comment: Sent to the client when their Fellowship Finder search yielded no results.
opcode: 218
size: 24
ClientZoneIpcType:
- name: InitRequest
comment: Sent by the client when they successfully initialize with the server, and they need several bits of information (e.g. what zone to load.)
Expand Down Expand Up @@ -1192,6 +1200,14 @@ ClientZoneIpcType:
comment: Sent when playing an instrument.
opcode: 266
size: 24
- name: CreateFellowship
comment: Sent when creating a new Fellowship.
opcode: 243
size: 64
- name: SearchFellowshipsJoin
comment: Sent when joining a Fellowship from the Fellowship Finder.
opcode: 237
size: 8
ServerLobbyIpcType:
- name: NackReply
comment: Sent by the server to indicate an lobby error occured.
Expand Down
80 changes: 72 additions & 8 deletions servers/world/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ use std::time::{Instant, SystemTime};
use axum::Router;
use axum::routing::get;
use kawari::common::{
BasicServiceAccountData, ContainerType, DEBUG_COMMAND_TRIGGER, FestivalId, HandlerId,
HandlerType, InstanceContentType, ItemOperationKind, LogMessageType, ObjectId, ObjectTypeId,
ObjectTypeKind, PlayerStateFlags1, PlayerStateFlags2, PlayerStateFlags3, Position,
QuestSpecialFlags, TAB_SHARED_FATE_COUNT, WarpType, calculate_max_level,
BasicServiceAccountData, ClientLanguage, ContainerType, DEBUG_COMMAND_TRIGGER, FestivalId,
HandlerId, HandlerType, InstanceContentType, ItemOperationKind, LogMessageType, ObjectId,
ObjectTypeId, ObjectTypeKind, PlayerStateFlags1, PlayerStateFlags2, PlayerStateFlags3,
Position, QuestSpecialFlags, TAB_SHARED_FATE_COUNT, WarpType, calculate_max_level,
};
use kawari::config::get_config;
use kawari_world::inventory::{Item, MAX_LARGE_STORAGE, Storage, get_next_free_slot};
Expand All @@ -26,8 +26,9 @@ use kawari::ipc::zone::{
};

use kawari::ipc::zone::{
Blacklist, BlacklistedCharacter, ClientTriggerCommand, ClientZoneIpcData, ReadyCheckReply,
ServerZoneIpcData, ServerZoneIpcSegment,
Blacklist, BlacklistedCharacter, ClientTriggerCommand, ClientZoneIpcData,
FellowshipActivityTag, FellowshipSearchInfo, ReadyCheckReply, ServerZoneIpcData,
ServerZoneIpcSegment, SocialListUILanguages,
};

use kawari::common::{CharacterMode, NETWORK_TIMEOUT, RECEIVE_BUFFER_SIZE};
Expand Down Expand Up @@ -2644,13 +2645,57 @@ async fn process_packet(
connection.send_ipc_self(ipc).await;
}
ClientZoneIpcData::RequestFellowships { .. } => {
tracing::info!("Fellowships is unimplemented");
// Report back that they're not in a fellowship
// TODO: Implement this for real!
let ipc = ServerZoneIpcSegment::new(ServerZoneIpcData::Fellowships {
unk1: [0; 808],
});
connection.send_ipc_self(ipc).await;
}
ClientZoneIpcData::RequestCrossworldLinkshells { .. } => {
connection.send_crossworld_linkshells(true).await;
}
ClientZoneIpcData::SearchFellowships { .. } => {
tracing::info!("Fellowship Finder is unimplemented");
// TODO: Actually implement Fellowship searches, for now none of the player's search criteria are used.
let fellowships = vec![
FellowshipSearchInfo {
community_id: 1,
master_content_id: 1,
recruiter_content_id: 1,
recruitment_deadline: 0xFFFF_FFFF,
languages: SocialListUILanguages::JAPANESE
| SocialListUILanguages::ENGLISH
| SocialListUILanguages::GERMAN
| SocialListUILanguages::FRENCH,
primary_language: ClientLanguage::English,
recruiter_world_id: connection.config.world_id,
master_world_id: connection.config.world_id,
current_member_count: 999,
target_member_count: 1000,
activity1: FellowshipActivityTag::Chatting,
unk1: [0; 4],
activity2: FellowshipActivityTag::DomanMahjong,
activity3: FellowshipActivityTag::Glamours,
fellowship_name: "Kawari Fellowship Test".to_string(),
recruiter_name: "Kawari Player".to_string(),
master_name: "Kawari Player".to_string(),
fellowship_description: b"The Fellowship's description.".into(),
},
FellowshipSearchInfo::default(),
FellowshipSearchInfo::default(),
FellowshipSearchInfo::default(),
];

let ipc = ServerZoneIpcSegment::new(
ServerZoneIpcData::SearchFellowshipsResults {
unk1: 0xFFFF_FFFF,
sequence_end: 0xFFFF_FFFF,
sequence_current: 0,
fellowships,
},
);

connection.send_ipc_self(ipc).await;
}
ClientZoneIpcData::StartCountdown {
starter_actor_id,
Expand Down Expand Up @@ -3752,6 +3797,25 @@ async fn process_packet(
ClientZoneIpcData::PerformanceNote { .. } => {
// TODO: what to do with these?
}
ClientZoneIpcData::CreateFellowship { .. } => {
let ipc =
ServerZoneIpcSegment::new(ServerZoneIpcData::ShowLinkshellError {
log_message: LogMessageType::UnableToCreateMoreFellowships
as u16,
unk: 0, // I don't have a capture of this yet, but zero works fine.
});

connection.send_ipc_self(ipc).await;
}
ClientZoneIpcData::SearchFellowshipsJoin { .. } => {
let ipc =
ServerZoneIpcSegment::new(ServerZoneIpcData::ShowLinkshellError {
log_message: LogMessageType::UnableToJoinMoreFellowships as u16,
unk: 0x0124_0E82, // Observed in a capture, but it doesn't seem to matter what we set it to, zero works too.
});

connection.send_ipc_self(ipc).await;
}
ClientZoneIpcData::Unknown { unk } => {
tracing::warn!(
"Unknown Zone packet {:?} recieved ({} bytes)",
Expand Down
Loading