diff --git a/core/src/ipc/zone/client/mod.rs b/core/src/ipc/zone/client/mod.rs index 7c40a6ce..0f4775b5 100644 --- a/core/src/ipc/zone/client/mod.rs +++ b/core/src/ipc/zone/client/mod.rs @@ -588,6 +588,20 @@ pub enum ClientZoneIpcData { #[brw(pad_after = 6)] // Seems to just be padding/garbage name: String, }, + Dive { + rotation: f32, + target_position: Position, + #[brw(pad_after = 4)] + original_position: Position, + }, + WorldInteraction { + action: u32, + param1: u32, + param2: u32, + param3: u32, + param4: u32, + position: Position, + }, } #[cfg(test)] diff --git a/core/src/ipc/zone/server/actor_set_pos.rs b/core/src/ipc/zone/server/actor_set_pos.rs index 6e6645f4..e62758f0 100644 --- a/core/src/ipc/zone/server/actor_set_pos.rs +++ b/core/src/ipc/zone/server/actor_set_pos.rs @@ -21,6 +21,8 @@ pub enum WarpType { /// Seen during Mt Gulg, assuming it applies to all instanced content. #[brw(magic = 25u8)] InstanceContent, + #[brw(magic = 26u8)] + Dive, #[brw(magic = 30u8)] Unk4, Unknown(u8), diff --git a/core/src/ipc/zone/server/mod.rs b/core/src/ipc/zone/server/mod.rs index 7ef411b3..afa50875 100644 --- a/core/src/ipc/zone/server/mod.rs +++ b/core/src/ipc/zone/server/mod.rs @@ -213,6 +213,7 @@ pub enum ServerZoneIpcData { log_message: u32, target_zone: u16, animation: u16, + /// This, in conjunction with unk1, seem to influence visual effects displayed during the zoning transition. For example, when diving, param4 is 218, and unk1 is 6 (with hide_character set to 1). When surfacing, param4 is 227, unk1 6, and hide_character 1. When going through an underwater portal, param4 is 15, unk1 is 4, and hide_character is 2. param4: u8, hide_character: u8, /// Must match what is used in ActorSetPos (if applicable) otherwise weird stuff like EnterTerritoryEvent is sent by the client again. diff --git a/resources/data/opcodes.yml b/resources/data/opcodes.yml index 49b00ee5..1663d004 100644 --- a/resources/data/opcodes.yml +++ b/resources/data/opcodes.yml @@ -1068,6 +1068,14 @@ ClientZoneIpcType: comment: The client requests that any attachments in this letter be given to them. opcode: 590 size: 64 + - name: Dive + comment: The client dives underwater. + opcode: 631 + size: 32 + - name: WorldInteraction + comment: The client interacts with the world in some way. + opcode: 296 + size: 32 ServerLobbyIpcType: - name: NackReply comment: Sent by the server to indicate an lobby error occured. diff --git a/servers/world/src/common.rs b/servers/world/src/common.rs index 8bbaa413..2006314b 100644 --- a/servers/world/src/common.rs +++ b/servers/world/src/common.rs @@ -21,7 +21,7 @@ use kawari::{ ClientTrigger, Conditions, Config, CrossworldLinkshellInvite, InviteReply, InviteType, OnlineStatus, PartyMemberEntry, PartyMemberPositions, PartyUpdateStatus, ReadyCheckReply, ServerZoneIpcSegment, SpawnNpc, SpawnObject, SpawnPlayer, - SpawnTreasure, StrategyBoard, StrategyBoardUpdate, WaymarkPlacementMode, + SpawnTreasure, StrategyBoard, StrategyBoardUpdate, WarpType, WaymarkPlacementMode, WaymarkPosition, WaymarkPreset, }, }, @@ -278,8 +278,15 @@ pub enum ToServer { // TODO: the connection should not be in charge and telling the global server what zone they just loaded in! but this will work for now ZoneLoaded(ClientId, ObjectId, SpawnPlayer), /// The connection wants to enter a new zone. - // TODO: temporary as this is only used for commands and those aren't run on global server state yet - ChangeZone(ClientId, ObjectId, u16, Option, Option), + // TODO: temporary as this is only used for commands (and diving/surfacing due to this being the least intrusive way to do this currently) and those aren't run on global server state yet + ChangeZone( + ClientId, + ObjectId, + u16, + Option, + Option, + Option<(WarpType, u8, u8, u8)>, + ), /// The player walks through a zone change line. EnterZoneJump(ClientId, ObjectId, u32), /// The connection disconnected. diff --git a/servers/world/src/main.rs b/servers/world/src/main.rs index 521852bf..975dd07d 100644 --- a/servers/world/src/main.rs +++ b/servers/world/src/main.rs @@ -18,7 +18,7 @@ use kawari::ipc::zone::{ ActorControlCategory, CWLSLeaveReason, Conditions, ContentFinderUserAction, CrossRealmListing, CrossRealmListings, EventType, LinkshellInviteResponse, MapEffects, MarketBoardItem, OnlineStatus, OnlineStatusMask, PlayerSetup, SceneFlags, SearchInfo, SocialListRequestType, - TrustContent, TrustInformation, + TrustContent, TrustInformation, WarpType, }; use kawari::ipc::zone::{ @@ -2942,6 +2942,51 @@ async fn process_packet( .remove_from_friend_list(*content_id, name.clone()) .await; } + ClientZoneIpcData::Dive { + target_position, + rotation, + .. + } => { + connection + .change_zone( + connection.player_data.volatile.zone_id as u16, + Some(*target_position), + Some(*rotation), + Some((WarpType::Dive, 218, 1, 6)), + ) + .await; + } + ClientZoneIpcData::WorldInteraction { + action, + param1, + param2, + param3, + param4, + position, + } => { + match action { + 0xD1 => { // Underwater portal + // TODO: This uses param1 somehow. It doesn't appear to be a poprange or exit box. If it's an index into a sheet, I have no idea which. + // TODO: The ActorSetPos for underwater portals uses warp_type: WarpType::InstanceContent, param4 = 15, hide_character = 2, unk1 = 4. + } + 0x25F => { + // Surfacing from diving + connection + .change_zone( + connection.player_data.volatile.zone_id as u16, + Some(*position), + Some(connection.player_data.volatile.rotation as f32), + Some((WarpType::Dive, 227, 1, 6)), + ) + .await; + } + _ => { + tracing::info!( + "Client executed uninplemented world interaction {action} with params {param1} {param2} {param3} {param4}" + ); + } + } + } ClientZoneIpcData::Unknown { unk } => { tracing::warn!( "Unknown Zone packet {:?} recieved ({} bytes), this should be handled!", diff --git a/servers/world/src/server/zone.rs b/servers/world/src/server/zone.rs index 4479b35b..4ef3f6a4 100644 --- a/servers/world/src/server/zone.rs +++ b/servers/world/src/server/zone.rs @@ -776,6 +776,9 @@ fn begin_change_zone<'a>( destination_zone_id: u16, actor_id: ObjectId, warp_type: WarpType, + param4: u8, + hide_character: u8, + unk1: u8, ) -> Option<(&'a mut Instance, bool)> { let mut needs_init_zone = false; @@ -785,10 +788,10 @@ fn begin_change_zone<'a>( fade_out_time: 1, log_message: 0, animation: 0, - param4: 0, - hide_character: 0, + param4, + hide_character, param_7: 0, - unk1: 0, + unk1, unk2: 0, }); @@ -837,6 +840,9 @@ pub fn change_zone_warp_to_pop_range( destination_zone_id, actor_id, warp_type, + 0, + 0, + 0, ) .unwrap(); @@ -928,6 +934,9 @@ pub fn change_zone_to_player( destination_zone_id, from_actor_id, WarpType::Normal, + 0, + 0, + 0, ) .unwrap(); @@ -1031,20 +1040,37 @@ pub fn handle_zone_messages( true } - ToServer::ChangeZone(from_id, actor_id, zone_id, new_position, new_rotation) => { + ToServer::ChangeZone( + from_id, + actor_id, + zone_id, + new_position, + new_rotation, + warp_type_info, + ) => { tracing::info!("{from_id:?} is requesting to go to zone {zone_id}"); let mut data = data.lock(); let mut network = network.lock(); let mut game_data = game_data.lock(); + let (warp_type, param4, hide_character, unk1) = + if let Some((w_type, param, hide, unk)) = warp_type_info { + (*w_type, *param, *hide, *unk) + } else { + (WarpType::Normal, 0, 0, 0) + }; + let (target_instance, needs_init_zone) = begin_change_zone( &mut data, &mut network, &mut game_data, *zone_id, *actor_id, - WarpType::Normal, + warp_type, + param4, + hide_character, + unk1, ) .unwrap(); do_change_zone( @@ -1054,7 +1080,7 @@ pub fn handle_zone_messages( *new_position, *new_rotation, *from_id, - WarpType::Normal, + warp_type, ); true diff --git a/servers/world/src/zone_connection/lua.rs b/servers/world/src/zone_connection/lua.rs index f46b0b7d..8293b8af 100644 --- a/servers/world/src/zone_connection/lua.rs +++ b/servers/world/src/zone_connection/lua.rs @@ -50,7 +50,7 @@ impl ZoneConnection { exit_position, exit_rotation, } => { - self.change_zone(*zone_id, *exit_position, *exit_rotation) + self.change_zone(*zone_id, *exit_position, *exit_rotation, None) .await } LuaTask::SetRemakeMode(remake_mode) => { diff --git a/servers/world/src/zone_connection/zone.rs b/servers/world/src/zone_connection/zone.rs index c9e91902..9cd97976 100644 --- a/servers/world/src/zone_connection/zone.rs +++ b/servers/world/src/zone_connection/zone.rs @@ -11,7 +11,7 @@ use kawari::{ constants::OBFUSCATION_ENABLED_MODE, ipc::zone::{ ActorControlCategory, Condition, ContentRegistrationFlags, House, HouseList, InitZone, - InitZoneFlags, ServerZoneIpcData, ServerZoneIpcSegment, WeatherChange, + InitZoneFlags, ServerZoneIpcData, ServerZoneIpcSegment, WarpType, WeatherChange, }, packet::{ConnectionState, PacketSegment, ScramblerKeyGenerator, SegmentData, SegmentType}, }; @@ -24,6 +24,7 @@ impl ZoneConnection { new_zone_id: u16, new_position: Option, new_rotation: Option, + warp_type_info: Option<(WarpType, u8, u8, u8)>, ) { self.teleport_reason = TeleportReason::NotSpecified; self.handle @@ -33,6 +34,7 @@ impl ZoneConnection { new_zone_id, new_position, new_rotation, + warp_type_info, )) .await; }