Skip to content
Open
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
35 changes: 34 additions & 1 deletion message/src/eldenring/bloodstain.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use serde::{Deserialize, Serialize};
use serde::{de, Deserialize, Serialize};

use super::*;

Expand All @@ -10,6 +10,30 @@ pub struct RequestCreateBloodstainParams {
pub group_passwords: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct BloodstainAdvertisementData {
pub magic: [u8; 8],
pub version: u32,
pub unkc: u32,
pub unk10: u32,
pub unk14: u32,
pub unk18: u32,
pub location: Location,
pub yaw: f32,
pub ghost_spawn_position: Position,
pub unk3c: u32,
pub unk40: u16,
pub unk42: u16,
pub unk44: u16,
pub unk46: u8,
pub unk47: u8,
pub unk48: u64,
pub play_region: u32,
pub character_name: [u16; 18],
pub unk70: u32,
pub unk74: u32,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ResponseCreateBloodstainParams {
pub identifier: ObjectIdentifier,
Expand Down Expand Up @@ -66,6 +90,15 @@ mod test {
assert_eq!(deserialized.replay_data.len(), 1165);
assert_eq!(deserialized.group_passwords.len(), 1);
assert_eq!(deserialized.group_passwords[0], "schlong");

let adv_data: super::BloodstainAdvertisementData =
deserialize(&deserialized.advertisement_data).unwrap();
assert_eq!(&adv_data.magic, &[7, 0, 0, 0, 120, 0, 0, 0]);
assert_eq!(adv_data.version, 7);
assert_eq!(adv_data.play_region, 1400001);
assert_eq!(adv_data.location.position.x, 150.72684);
assert_eq!(adv_data.location.position.y, 112.589775);
assert_eq!(adv_data.location.position.z, -180.64731);
}

#[test]
Expand Down
6 changes: 3 additions & 3 deletions message/src/eldenring/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ mod test {
assert_eq!(deserialized.used_items[0].times_used, 1);
assert_eq!(deserialized.used_items[0].new_count, 1);
assert_eq!(deserialized.location.map, 60423600);
assert_eq!(deserialized.location.x, -45.939575);
assert_eq!(deserialized.location.y, 92.36392);
assert_eq!(deserialized.location.z, 79.65545);
assert_eq!(deserialized.location.position.x, -45.939575);
assert_eq!(deserialized.location.position.y, 92.36392);
assert_eq!(deserialized.location.position.z, 79.65545);
}
}
9 changes: 7 additions & 2 deletions message/src/eldenring/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,14 @@ pub struct MatchingParameters {
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Location {
pub map: u32,
pub struct Position {
pub x: f32,
pub y: f32,
pub z: f32,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Location {
pub map: u32,
pub position: Position,
}
40 changes: 36 additions & 4 deletions server/src/handler/eldenring/bloodstain.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use message::eldenring::{
ObjectIdentifier, PlayRegionArea, RequestCreateBloodstainParams,
RequestGetBloodstainListParams, RequestGetDeadingGhostParams, ResponseCreateBloodstainParams,
ResponseGetBloodstainListParams, ResponseGetBloodstainListParamsEntry,
ResponseGetDeadingGhostParams,
BloodstainAdvertisementData, ObjectIdentifier, PlayRegionArea, Position,
RequestCreateBloodstainParams, RequestGetBloodstainListParams, RequestGetDeadingGhostParams,
ResponseCreateBloodstainParams, ResponseGetBloodstainListParams,
ResponseGetBloodstainListParamsEntry, ResponseGetDeadingGhostParams,
};
use sqlx::Row;
use wire::deserialize;

use crate::handler::HandleRequest;

Expand Down Expand Up @@ -57,13 +58,40 @@ const SELECT_BY_ID_QUERY: &str = "
FROM bloodstains
WHERE bloodstain_id = $1";

const ROUNDTABLE_PLAY_REGION_ID: i32 = 1110000;
/// BBOX defining the PVP-enabled area within the Roundtable Hold
/// (lower part where player can be invaded by npc invader)
const ROUNDTABLE_PVP_ENABLED_AREA_BBOX: [(f32, f32, f32); 2] =
[(-325.0, -35.0, -325.0), (-245.0, -25.0, -245.0)];

fn is_in_bbox(pos: Position, bbox: [(f32, f32, f32); 2]) -> bool {
let min = bbox[0];
let max = bbox[1];
pos.x >= min.0
&& pos.x <= max.0
&& pos.y >= min.1
&& pos.y <= max.1
&& pos.z >= min.2
&& pos.z <= max.2
}

impl HandleRequest<Box<RequestCreateBloodstainParams>, ResponseCreateBloodstainParams>
for DefaultClientHandler<'_>
{
async fn handle(
&mut self,
request: &Box<RequestCreateBloodstainParams>,
) -> Result<ResponseCreateBloodstainParams, Box<dyn std::error::Error>> {
if request.area.play_region as i32 == ROUNDTABLE_PLAY_REGION_ID {
// check that location is in the pvp-enabled area of the roundtable
let adv_data: BloodstainAdvertisementData = deserialize(&request.advertisement_data)?;
let pos = adv_data.location.position;
if !is_in_bbox(pos, ROUNDTABLE_PVP_ENABLED_AREA_BBOX) {
return Ok(ResponseCreateBloodstainParams {
identifier: ObjectIdentifier(0),
});
}
}
let bloodstain_id = sqlx::query(INSERT_QUERY)
.bind(self.session.player_id)
.bind(self.session.session_id)
Expand Down Expand Up @@ -95,6 +123,10 @@ impl HandleRequest<Box<RequestGetBloodstainListParams>, ResponseGetBloodstainLis
.map(|a| a.play_region as i32)
.collect::<Vec<i32>>();

if play_regions.is_empty() {
return Ok(ResponseGetBloodstainListParams { entries: vec![] });
}

let entries: Vec<ResponseGetBloodstainListParamsEntry> =
sqlx::query_as::<_, BloodstainRecord>(SELECT_QUERY)
.bind(play_regions)
Expand Down