Compare commits
No commits in common. "1f7dd1eafe9933be8023821ccb961b7e06b4a9e0" and "0f0adb05a3d3d8d53007166530983e8b83a15447" have entirely different histories.
1f7dd1eafe
...
0f0adb05a3
2388
Cargo.lock
generated
Normal file
2388
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -29,7 +29,7 @@ async-recursion= "1.0.0"
|
||||
lazy_static = "1.4.0"
|
||||
barrel = { version = "0.6.5", features = ["pg"] }
|
||||
refinery = { version = "0.5.0", features = ["postgres"] }
|
||||
sqlx = { version = "0.6.2", features = ["runtime-async-std-native-tls", "postgres", "json", "chrono"] }
|
||||
sqlx = { version = "0.5.10", features = ["runtime-async-std-native-tls", "postgres", "json", "chrono"] }
|
||||
strum = "0.19.5"
|
||||
strum_macros = "0.19"
|
||||
anyhow = { version = "1.0.68", features = ["backtrace"] }
|
||||
|
@ -4,10 +4,10 @@ use futures::future::{Future, BoxFuture};
|
||||
use crate::entity::account::*;
|
||||
use crate::entity::character::*;
|
||||
use crate::entity::item::*;
|
||||
use crate::entity::room::*;
|
||||
|
||||
|
||||
// TODO: better granularity?
|
||||
//#[derive(Error, Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum GatewayError {
|
||||
#[error("unknown error")]
|
||||
@ -147,14 +147,6 @@ pub trait EntityGateway: Send + Sync {
|
||||
async fn set_character_playtime(&mut self, _char_id: &CharacterEntityId, _playtime: u32) -> Result<(), GatewayError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
async fn create_room(&mut self, _room: NewRoomEntity) -> Result<RoomEntity, GatewayError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
async fn add_room_note(&mut self, _room_id: RoomEntityId, _note: RoomNote) -> Result<(), GatewayError> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -6,7 +6,6 @@ use crate::entity::account::*;
|
||||
use crate::entity::character::*;
|
||||
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError};
|
||||
use crate::entity::item::*;
|
||||
use crate::entity::room::*;
|
||||
|
||||
use async_std::sync::{Arc, Mutex};
|
||||
|
||||
@ -767,21 +766,4 @@ impl EntityGateway for InMemoryGateway {
|
||||
Err(GatewayError::Error)
|
||||
}
|
||||
}
|
||||
|
||||
// I do not care to replicate this in testing
|
||||
async fn create_room(&mut self, room: NewRoomEntity) -> Result<RoomEntity, GatewayError> {
|
||||
Ok(RoomEntity {
|
||||
id: RoomEntityId(0),
|
||||
name: room.name,
|
||||
section_id: room.section_id,
|
||||
episode: room.episode,
|
||||
difficulty: room.difficulty,
|
||||
mode: room.mode,
|
||||
})
|
||||
}
|
||||
|
||||
// I do not care to replicate this in testing
|
||||
async fn add_room_note(&mut self, _room_id: RoomEntityId, _note: RoomNote) -> Result<(), GatewayError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
create table room (
|
||||
id serial primary key not null,
|
||||
name varchar(32) not null,
|
||||
section_id char not null,
|
||||
mode char not null,
|
||||
episode char not null,
|
||||
difficulty char not null
|
||||
);
|
||||
|
||||
create table room_note (
|
||||
room integer references room (id) not null,
|
||||
note jsonb not null,
|
||||
created_at timestamptz default current_timestamp not null
|
||||
);
|
@ -1,17 +0,0 @@
|
||||
drop table room_note;
|
||||
drop table room;
|
||||
|
||||
create table room (
|
||||
id serial primary key not null,
|
||||
name varchar(32) not null,
|
||||
section_id "char" not null,
|
||||
mode "char" not null,
|
||||
episode "char" not null,
|
||||
difficulty "char" not null
|
||||
);
|
||||
|
||||
create table room_note (
|
||||
room integer references room (id) not null,
|
||||
note jsonb not null,
|
||||
created_at timestamptz default current_timestamp not null
|
||||
);
|
@ -7,10 +7,7 @@ use libpso::util::vec_to_array;
|
||||
use crate::entity::account::*;
|
||||
use crate::entity::character::*;
|
||||
use crate::entity::item::*;
|
||||
use crate::entity::room::*;
|
||||
use crate::ship::map::MapArea;
|
||||
use crate::ship::room::{Episode, Difficulty};
|
||||
use crate::ship::monster::MonsterType;
|
||||
|
||||
#[derive(Debug, sqlx::FromRow)]
|
||||
pub struct PgUserAccount {
|
||||
@ -580,16 +577,6 @@ pub enum PgItemNoteDetail {
|
||||
},
|
||||
EnemyDrop {
|
||||
character_id: u32,
|
||||
room_id: u32,
|
||||
monster_type: MonsterType,
|
||||
map_area: MapArea,
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
},
|
||||
BoxDrop {
|
||||
character_id: u32,
|
||||
room_id: u32,
|
||||
map_area: MapArea,
|
||||
x: f32,
|
||||
y: f32,
|
||||
@ -605,19 +592,14 @@ pub enum PgItemNoteDetail {
|
||||
y: f32,
|
||||
z: f32,
|
||||
},
|
||||
Consumed {
|
||||
character_id: u32,
|
||||
},
|
||||
Consumed,
|
||||
FedToMag {
|
||||
character_id: u32,
|
||||
mag: u32,
|
||||
},
|
||||
BoughtAtShop {
|
||||
character_id: u32,
|
||||
},
|
||||
SoldToShop {
|
||||
character_id: u32,
|
||||
},
|
||||
SoldToShop,
|
||||
Trade {
|
||||
trade_id: u32,
|
||||
character_to: u32,
|
||||
@ -642,16 +624,8 @@ impl From<ItemNote> for PgItemNoteDetail {
|
||||
ItemNote::CharacterCreation{character_id} => PgItemNoteDetail::CharacterCreation {
|
||||
character_id: character_id.0,
|
||||
},
|
||||
ItemNote::EnemyDrop{character_id, room_id, monster_type, map_area, x, y, z} => PgItemNoteDetail::EnemyDrop {
|
||||
ItemNote::EnemyDrop{character_id, map_area, x, y, z} => PgItemNoteDetail::EnemyDrop {
|
||||
character_id: character_id.0,
|
||||
room_id: room_id.0,
|
||||
monster_type,
|
||||
map_area,
|
||||
x,y,z,
|
||||
},
|
||||
ItemNote::BoxDrop{character_id, room_id, map_area, x, y, z} => PgItemNoteDetail::BoxDrop {
|
||||
character_id: character_id.0,
|
||||
room_id: room_id.0,
|
||||
map_area,
|
||||
x,y,z,
|
||||
},
|
||||
@ -663,19 +637,14 @@ impl From<ItemNote> for PgItemNoteDetail {
|
||||
map_area,
|
||||
x,y,z,
|
||||
},
|
||||
ItemNote::Consumed{character_id} => PgItemNoteDetail::Consumed {
|
||||
character_id: character_id.0,
|
||||
},
|
||||
ItemNote::FedToMag{character_id, mag} => PgItemNoteDetail::FedToMag{
|
||||
character_id: character_id.0,
|
||||
ItemNote::Consumed => PgItemNoteDetail::Consumed,
|
||||
ItemNote::FedToMag{mag} => PgItemNoteDetail::FedToMag{
|
||||
mag: mag.0
|
||||
},
|
||||
ItemNote::BoughtAtShop{character_id} => PgItemNoteDetail::BoughtAtShop {
|
||||
character_id: character_id.0,
|
||||
},
|
||||
ItemNote::SoldToShop{character_id} => PgItemNoteDetail::SoldToShop {
|
||||
character_id: character_id.0,
|
||||
},
|
||||
ItemNote::SoldToShop => PgItemNoteDetail::SoldToShop,
|
||||
ItemNote::Trade{trade_id, character_to, character_from} => PgItemNoteDetail::Trade {
|
||||
trade_id: trade_id.0,
|
||||
character_to: character_to.0,
|
||||
@ -708,16 +677,8 @@ impl From<PgItemNoteDetail> for ItemNote {
|
||||
PgItemNoteDetail::CharacterCreation{character_id} => ItemNote::CharacterCreation {
|
||||
character_id: CharacterEntityId(character_id),
|
||||
},
|
||||
PgItemNoteDetail::EnemyDrop{character_id, room_id, monster_type, map_area, x, y, z} => ItemNote::EnemyDrop {
|
||||
PgItemNoteDetail::EnemyDrop{character_id, map_area, x, y, z} => ItemNote::EnemyDrop {
|
||||
character_id: CharacterEntityId(character_id),
|
||||
room_id: RoomEntityId(room_id),
|
||||
monster_type,
|
||||
map_area,
|
||||
x,y,z,
|
||||
},
|
||||
PgItemNoteDetail::BoxDrop{character_id, room_id, map_area, x, y, z} => ItemNote::BoxDrop {
|
||||
character_id: CharacterEntityId(character_id),
|
||||
room_id: RoomEntityId(room_id),
|
||||
map_area,
|
||||
x,y,z,
|
||||
},
|
||||
@ -729,19 +690,14 @@ impl From<PgItemNoteDetail> for ItemNote {
|
||||
map_area,
|
||||
x,y,z,
|
||||
},
|
||||
PgItemNoteDetail::Consumed{character_id} => ItemNote::Consumed {
|
||||
character_id: CharacterEntityId(character_id),
|
||||
},
|
||||
PgItemNoteDetail::FedToMag{character_id, mag} => ItemNote::FedToMag{
|
||||
character_id: CharacterEntityId(character_id),
|
||||
PgItemNoteDetail::Consumed => ItemNote::Consumed,
|
||||
PgItemNoteDetail::FedToMag{mag} => ItemNote::FedToMag{
|
||||
mag: ItemEntityId(mag)
|
||||
},
|
||||
PgItemNoteDetail::BoughtAtShop{character_id} => ItemNote::BoughtAtShop {
|
||||
character_id: CharacterEntityId(character_id),
|
||||
},
|
||||
PgItemNoteDetail::SoldToShop{character_id} => ItemNote::SoldToShop {
|
||||
character_id: CharacterEntityId(character_id),
|
||||
},
|
||||
PgItemNoteDetail::SoldToShop => ItemNote::SoldToShop,
|
||||
PgItemNoteDetail::Trade {trade_id, character_to, character_from} => ItemNote::Trade {
|
||||
trade_id: TradeId(trade_id),
|
||||
character_to: CharacterEntityId(character_to),
|
||||
@ -924,27 +880,3 @@ impl From<PgTradeEntity> for TradeEntity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, sqlx::FromRow, Serialize)]
|
||||
pub struct PgRoomEntity {
|
||||
id: i32,
|
||||
name: String,
|
||||
section_id: i8,
|
||||
mode: i8,
|
||||
episode: i8,
|
||||
difficulty: i8,
|
||||
}
|
||||
|
||||
impl From<PgRoomEntity> for RoomEntity {
|
||||
fn from(other: PgRoomEntity) -> RoomEntity {
|
||||
RoomEntity {
|
||||
id: RoomEntityId(other.id as u32),
|
||||
name: other.name,
|
||||
section_id: SectionID::from(other.section_id as u8),
|
||||
mode: RoomEntityMode::from(other.mode as u8),
|
||||
episode: Episode::try_from(other.episode as u8).unwrap(),
|
||||
difficulty: Difficulty::try_from(other.difficulty as u8).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ use crate::entity::account::*;
|
||||
use crate::entity::character::*;
|
||||
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError};
|
||||
use crate::entity::item::*;
|
||||
use crate::entity::room::*;
|
||||
use super::models::*;
|
||||
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
@ -179,7 +178,7 @@ async fn create_user(conn: &mut sqlx::PgConnection, user: NewUserAccountEntity)
|
||||
async fn get_user_by_id(conn: &mut sqlx::PgConnection, id: UserAccountId) -> Result<UserAccountEntity, GatewayError>
|
||||
{
|
||||
let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where id = $1")
|
||||
.bind(id.0 as i32)
|
||||
.bind(id.0)
|
||||
.fetch_one(conn).await?;
|
||||
Ok(user.into())
|
||||
}
|
||||
@ -200,8 +199,8 @@ async fn save_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> R
|
||||
.bind(&user.password)
|
||||
.bind(user.banned_until)
|
||||
.bind(user.muted_until)
|
||||
.bind(user.flags as i32)
|
||||
.bind(user.id.0 as i32)
|
||||
.bind(user.flags)
|
||||
.bind(user.id.0)
|
||||
.execute(conn).await?;
|
||||
Ok(())
|
||||
}
|
||||
@ -210,7 +209,7 @@ async fn create_user_settings(conn: &mut sqlx::PgConnection, settings: NewUserSe
|
||||
{
|
||||
let new_settings = sqlx::query_as::<_, PgUserSettings>("insert into user_settings (user_account, blocked_users, key_config, joystick_config, option_flags, shortcuts, symbol_chats, team_name)
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8) returning *;")
|
||||
.bind(settings.user_id.0 as i32)
|
||||
.bind(settings.user_id.0)
|
||||
.bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
|
||||
.bind(settings.settings.keyboard_config.to_vec())
|
||||
.bind(settings.settings.gamepad_config.to_vec())
|
||||
@ -225,7 +224,7 @@ async fn create_user_settings(conn: &mut sqlx::PgConnection, settings: NewUserSe
|
||||
async fn get_user_settings_by_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError>
|
||||
{
|
||||
let settings = sqlx::query_as::<_, PgUserSettings>("select * from user_settings where user_account = $1")
|
||||
.bind(user.id.0 as i32)
|
||||
.bind(user.id.0)
|
||||
.fetch_one(conn).await?;
|
||||
Ok(settings.into())
|
||||
}
|
||||
@ -236,11 +235,11 @@ async fn save_user_settings(conn: &mut sqlx::PgConnection, settings: &UserSettin
|
||||
.bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
|
||||
.bind(&settings.settings.keyboard_config.to_vec())
|
||||
.bind(&settings.settings.gamepad_config.to_vec())
|
||||
.bind(settings.settings.option_flags as i32)
|
||||
.bind(settings.settings.option_flags)
|
||||
.bind(&settings.settings.shortcuts.to_vec())
|
||||
.bind(&settings.settings.symbol_chats.to_vec())
|
||||
.bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
|
||||
.bind(settings.id.0 as i32)
|
||||
.bind(settings.id.0)
|
||||
.execute(conn).await?;
|
||||
Ok(())
|
||||
}
|
||||
@ -263,7 +262,7 @@ async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntit
|
||||
$26, $27, $28, $29, $30)
|
||||
returning *;"#;
|
||||
let character = sqlx::query_as::<_, PgCharacter>(q)
|
||||
.bind(char.user_id.0 as i32)
|
||||
.bind(char.user_id.0)
|
||||
.bind(char.slot as i16)
|
||||
.bind(char.name)
|
||||
.bind(char.exp as i32)
|
||||
@ -301,7 +300,7 @@ async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntit
|
||||
async fn get_characters_by_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError>
|
||||
{
|
||||
let stream = sqlx::query_as::<_, PgCharacter>("select * from player_character where user_account = $1 and slot < 4 order by created_at;")
|
||||
.bind(user.id.0 as i32)
|
||||
.bind(user.id.0)
|
||||
.fetch(conn);
|
||||
|
||||
Ok(stream.fold(core::array::from_fn(|_| None), |mut acc, char| async move {
|
||||
@ -321,7 +320,7 @@ async fn save_character(conn: &mut sqlx::PgConnection, char: &CharacterEntity) -
|
||||
evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29, playtime=$30
|
||||
where id=$31;"#;
|
||||
sqlx::query(q)
|
||||
.bind(char.user_id.0 as i32) // $1
|
||||
.bind(char.user_id.0) // $1
|
||||
.bind(char.slot as i16) // $2
|
||||
.bind(&char.name) // $3
|
||||
.bind(char.exp as i32) // $4
|
||||
@ -371,7 +370,7 @@ async fn create_item(conn: &mut sqlx::PgConnection, item: NewItemEntity) -> Resu
|
||||
async fn add_item_note(conn: &mut sqlx::PgConnection, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError>
|
||||
{
|
||||
sqlx::query("insert into item_note(item, note) values ($1, $2)")
|
||||
.bind(item_id.0 as i32)
|
||||
.bind(item_id.0)
|
||||
.bind(sqlx::types::Json(PgItemNoteDetail::from(item_note)))
|
||||
.execute(conn).await?;
|
||||
Ok(())
|
||||
@ -380,7 +379,7 @@ async fn add_item_note(conn: &mut sqlx::PgConnection, item_id: &ItemEntityId, it
|
||||
async fn feed_mag(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError>
|
||||
{
|
||||
sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);")
|
||||
.bind(mag_item_id.0 as i32)
|
||||
.bind(mag_item_id.0)
|
||||
.bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::FeedMag{food: *tool_item_id})))
|
||||
.execute(conn).await?;
|
||||
Ok(())
|
||||
@ -389,7 +388,7 @@ async fn feed_mag(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, too
|
||||
async fn change_mag_owner(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError>
|
||||
{
|
||||
sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);")
|
||||
.bind(mag_item_id.0 as i32)
|
||||
.bind(mag_item_id.0)
|
||||
.bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::OwnerChange(character.char_class, character.section_id))))
|
||||
.execute(conn).await?;
|
||||
Ok(())
|
||||
@ -398,7 +397,7 @@ async fn change_mag_owner(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntit
|
||||
async fn use_mag_cell(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError>
|
||||
{
|
||||
sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);")
|
||||
.bind(mag_item_id.0 as i32)
|
||||
.bind(mag_item_id.0)
|
||||
.bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::MagCell(*mag_cell_id))))
|
||||
.execute(conn).await?;
|
||||
Ok(())
|
||||
@ -407,7 +406,7 @@ async fn use_mag_cell(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId,
|
||||
async fn add_weapon_modifier(conn: &mut sqlx::PgConnection, item_id: &ItemEntityId, modifier: &weapon::WeaponModifier) -> Result<(), GatewayError>
|
||||
{
|
||||
sqlx::query("insert into weapon_modifier (weapon, modifier) values ($1, $2);")
|
||||
.bind(item_id.0 as i32)
|
||||
.bind(item_id.0)
|
||||
.bind(sqlx::types::Json(modifier))
|
||||
.execute(conn).await?;
|
||||
Ok(())
|
||||
@ -417,7 +416,7 @@ async fn get_character_inventory(conn: &mut sqlx::PgConnection, char_id: &Charac
|
||||
{
|
||||
let conn = Arc::new(Mutex::new(conn.begin().await?)); // this is some degen shit
|
||||
let inventory = sqlx::query_as::<_, PgInventoryEntity>("select * from inventory where pchar = $1")
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.fetch_one(&mut **conn.lock().await).await?;
|
||||
|
||||
Ok(InventoryEntity::new(
|
||||
@ -442,14 +441,14 @@ async fn get_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEn
|
||||
let bank = match bank_identifier {
|
||||
BankIdentifier::Character => {
|
||||
sqlx::query_as::<_, PgInventoryEntity>("select * from bank where pchar = $1")
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.fetch_one(&mut **conn.lock().await).await?
|
||||
},
|
||||
BankIdentifier::Shared(bank_name) => {
|
||||
sqlx::query_as::<_, PgInventoryEntity>("select player_character.id as pchar, shared_bank.items as items from shared_bank
|
||||
join player_character on shared_bank.user_account = player_character.user_account
|
||||
where player_character.id = $1 and shared_bank.name = $2")
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.bind(&bank_name.0)
|
||||
.fetch_optional(&mut **conn.lock().await)
|
||||
.await?
|
||||
@ -492,7 +491,7 @@ async fn set_character_inventory(conn: &mut sqlx::PgConnection, char_id: &Charac
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2")
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.bind(sqlx::types::Json(inventory))
|
||||
.execute(conn)
|
||||
.await?;
|
||||
@ -517,7 +516,7 @@ async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEn
|
||||
match bank_identifier {
|
||||
BankIdentifier::Character => {
|
||||
sqlx::query("insert into bank (pchar, items, name) values ($1, $2, '') on conflict (pchar, name) do update set items = $2")
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.bind(sqlx::types::Json(bank))
|
||||
.execute(conn)
|
||||
.await?;
|
||||
@ -527,7 +526,7 @@ async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEn
|
||||
select player_character.user_account, $2, $3 from player_character
|
||||
where player_character.id = $1
|
||||
on conflict (user_account, name) do update set items = $2;")
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.bind(sqlx::types::Json(bank))
|
||||
.bind(&bank_name.0)
|
||||
.execute(conn)
|
||||
@ -540,7 +539,7 @@ async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEn
|
||||
async fn get_character_equips(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result<EquippedEntity, GatewayError>
|
||||
{
|
||||
let equips = sqlx::query_as::<_, PgEquipped>("select * from equipped where pchar = $1")
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.fetch_one(conn)
|
||||
.await?;
|
||||
|
||||
@ -551,7 +550,7 @@ async fn set_character_equips(conn: &mut sqlx::PgConnection, char_id: &Character
|
||||
{
|
||||
sqlx::query(r#"insert into equipped (pchar, weapon, armor, shield, unit0, unit1, unit2, unit3, mag) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
on conflict (pchar) do update set weapon=$2, armor=$3, shield=$4, unit0=$5, unit1=$6, unit2=$7, unit3=$8, mag=$9"#)
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.bind(equips.weapon.map(|i| i.0 as i32))
|
||||
.bind(equips.armor.map(|i| i.0 as i32))
|
||||
.bind(equips.shield.map(|i| i.0 as i32))
|
||||
@ -569,7 +568,7 @@ async fn set_character_equips(conn: &mut sqlx::PgConnection, char_id: &Character
|
||||
async fn set_character_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError>
|
||||
{
|
||||
sqlx::query("insert into character_meseta values ($1, $2) on conflict (pchar) do update set meseta = $2")
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.bind(meseta.0 as i32)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
@ -581,7 +580,7 @@ async fn get_character_meseta(conn: &mut sqlx::PgConnection, char_id: &Character
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct PgMeseta(i32);
|
||||
let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where pchar = $1"#)
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.fetch_one(conn)
|
||||
.await?;
|
||||
Ok(Meseta(meseta.0 as u32))
|
||||
@ -592,7 +591,7 @@ async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntit
|
||||
match bank_identifier {
|
||||
BankIdentifier::Character => {
|
||||
sqlx::query("insert into bank_meseta values ($1, '', $2) on conflict (pchar, bank) do update set meseta = $2")
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.bind(meseta.0 as i32)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
@ -602,7 +601,7 @@ async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntit
|
||||
select player_character.user_account, $2, $3 from player_character
|
||||
where player_character.id = $1
|
||||
on conflict (user_account, name) do update set meseta = $3")
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.bind(&bank_name.0)
|
||||
.bind(meseta.0 as i32)
|
||||
.execute(conn)
|
||||
@ -621,7 +620,7 @@ async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntit
|
||||
let meseta = match bank_identifier {
|
||||
BankIdentifier::Character => {
|
||||
sqlx::query_as::<_, PgMeseta>(r#"select meseta from bank_meseta where pchar = $1"#)
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.fetch_one(conn)
|
||||
.await?
|
||||
},
|
||||
@ -629,7 +628,7 @@ async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntit
|
||||
sqlx::query_as::<_, PgMeseta>(r#"select shared_bank_meseta.meseta from shared_bank_meseta
|
||||
join player_character on shared_bank_meseta.user_account = player_character.user_account
|
||||
where player_character.id = $1 and shared_bank_meseta.name = $2"#)
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(char_id.0)
|
||||
.bind(&bank_name.0)
|
||||
.fetch_optional(conn)
|
||||
.await?
|
||||
@ -642,8 +641,8 @@ async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntit
|
||||
async fn create_trade(conn: &mut sqlx::PgConnection, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result<TradeEntity, GatewayError>
|
||||
{
|
||||
let trade = sqlx::query_as::<_, PgTradeEntity>(r#"insert into trades (character1, character2) values ($1, $2) returning *;"#)
|
||||
.bind(char_id1.0 as i32)
|
||||
.bind(char_id2.0 as i32)
|
||||
.bind(char_id1.0)
|
||||
.bind(char_id2.0)
|
||||
.fetch_one(conn)
|
||||
.await?;
|
||||
Ok(trade.into())
|
||||
@ -652,30 +651,8 @@ async fn create_trade(conn: &mut sqlx::PgConnection, char_id1: &CharacterEntityI
|
||||
async fn set_character_playtime(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError>
|
||||
{
|
||||
sqlx::query(r#"update player_character set playtime=$2 where id=$1;"#)
|
||||
.bind(char_id.0 as i32)
|
||||
.bind(playtime as i32)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_room(conn: &mut sqlx::PgConnection, room: NewRoomEntity) -> Result<RoomEntity, GatewayError> {
|
||||
sqlx::query_as::<_, PgRoomEntity>("insert into room (name, section_id, mode, episode, difficulty) values ($1, $2, $3, $4, $5) returning *")
|
||||
.bind(room.name)
|
||||
.bind(u8::from(room.section_id) as i8)
|
||||
.bind(u8::from(room.mode) as i8)
|
||||
.bind(u8::from(room.episode) as i8)
|
||||
.bind(u8::from(room.difficulty) as i8)
|
||||
.fetch_one(conn)
|
||||
.await
|
||||
.map(|room| room.into())
|
||||
.map_err(|err| err.into())
|
||||
}
|
||||
|
||||
async fn add_room_note(conn: &mut sqlx::PgConnection, room_id: RoomEntityId, note: RoomNote) -> Result<(), GatewayError> {
|
||||
sqlx::query("insert into room_note (room, note) values ($1, $2)")
|
||||
.bind(room_id.0 as i32)
|
||||
.bind(sqlx::types::Json(note))
|
||||
.bind(char_id.0)
|
||||
.bind(playtime)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
Ok(())
|
||||
@ -820,14 +797,6 @@ impl EntityGateway for PostgresGateway {
|
||||
async fn set_character_playtime(&mut self, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError> {
|
||||
set_character_playtime(&mut *self.pool.acquire().await?, char_id, playtime).await
|
||||
}
|
||||
|
||||
async fn create_room(&mut self, room: NewRoomEntity) -> Result<RoomEntity, GatewayError> {
|
||||
create_room(&mut *self.pool.acquire().await?, room).await
|
||||
}
|
||||
|
||||
async fn add_room_note(&mut self, room_id: RoomEntityId, note: RoomNote) -> Result<(), GatewayError> {
|
||||
add_room_note(&mut *self.pool.acquire().await?, room_id, note).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -954,13 +923,5 @@ impl<'c> EntityGateway for PostgresTransaction<'c> {
|
||||
async fn set_character_playtime(&mut self, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError> {
|
||||
set_character_playtime(&mut *self.pgtransaction.lock().await, char_id, playtime).await
|
||||
}
|
||||
|
||||
async fn create_room(&mut self, room: NewRoomEntity) -> Result<RoomEntity, GatewayError> {
|
||||
create_room(&mut *self.pgtransaction.lock().await, room).await
|
||||
}
|
||||
|
||||
async fn add_room_note(&mut self, room_id: RoomEntityId, note: RoomNote) -> Result<(), GatewayError> {
|
||||
add_room_note(&mut *self.pgtransaction.lock().await, room_id, note).await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,7 @@ pub mod esweapon;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::entity::character::CharacterEntityId;
|
||||
use crate::entity::room::RoomEntityId;
|
||||
use crate::ship::map::MapArea;
|
||||
use crate::ship::monster::MonsterType;
|
||||
use crate::ship::drops::ItemDropType;
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
@ -37,16 +35,8 @@ pub enum ItemNote {
|
||||
},
|
||||
EnemyDrop {
|
||||
character_id: CharacterEntityId,
|
||||
room_id: RoomEntityId,
|
||||
monster_type: MonsterType,
|
||||
map_area: MapArea,
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
},
|
||||
BoxDrop {
|
||||
character_id: CharacterEntityId,
|
||||
room_id: RoomEntityId,
|
||||
//monster_type: MonsterType,
|
||||
//droprate: f32,
|
||||
map_area: MapArea,
|
||||
x: f32,
|
||||
y: f32,
|
||||
@ -62,19 +52,15 @@ pub enum ItemNote {
|
||||
y: f32,
|
||||
z: f32,
|
||||
},
|
||||
Consumed {
|
||||
character_id: CharacterEntityId,
|
||||
},
|
||||
Consumed, // TODO: character_id
|
||||
FedToMag {
|
||||
character_id: CharacterEntityId,
|
||||
//character_id: CharacterEntityId,
|
||||
mag: ItemEntityId,
|
||||
},
|
||||
BoughtAtShop {
|
||||
character_id: CharacterEntityId,
|
||||
},
|
||||
SoldToShop {
|
||||
character_id: CharacterEntityId,
|
||||
},
|
||||
SoldToShop,
|
||||
Trade {
|
||||
trade_id: TradeId,
|
||||
character_to: CharacterEntityId,
|
||||
|
@ -2,4 +2,3 @@ pub mod gateway;
|
||||
pub mod account;
|
||||
pub mod character;
|
||||
pub mod item;
|
||||
pub mod room;
|
||||
|
@ -1,83 +0,0 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
|
||||
use crate::entity::character::{CharacterEntityId, SectionID};
|
||||
use crate::ship::room::{Episode, Difficulty};
|
||||
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct RoomEntityId(pub u32);
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum RoomEntityMode {
|
||||
Multi,
|
||||
Single,
|
||||
Challenge,
|
||||
Battle,
|
||||
}
|
||||
|
||||
impl From<u8> for RoomEntityMode {
|
||||
fn from(other: u8) -> RoomEntityMode {
|
||||
match other {
|
||||
0 => RoomEntityMode::Multi,
|
||||
1 => RoomEntityMode::Single,
|
||||
2 => RoomEntityMode::Challenge,
|
||||
3 => RoomEntityMode::Battle,
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RoomEntityMode> for u8 {
|
||||
fn from(other: RoomEntityMode) -> u8 {
|
||||
match other {
|
||||
RoomEntityMode::Multi => 0,
|
||||
RoomEntityMode::Single => 1,
|
||||
RoomEntityMode::Challenge => 2,
|
||||
RoomEntityMode::Battle => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RoomEntity {
|
||||
pub id: RoomEntityId,
|
||||
pub name: String,
|
||||
pub section_id: SectionID,
|
||||
pub mode: RoomEntityMode,
|
||||
pub episode: Episode,
|
||||
pub difficulty: Difficulty,
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NewRoomEntity {
|
||||
pub name: String,
|
||||
pub section_id: SectionID,
|
||||
pub mode: RoomEntityMode,
|
||||
pub episode: Episode,
|
||||
pub difficulty: Difficulty,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize)]
|
||||
pub enum RoomNote {
|
||||
Create {
|
||||
character_id: CharacterEntityId,
|
||||
},
|
||||
PlayerJoin {
|
||||
character_id: CharacterEntityId,
|
||||
},
|
||||
PlayerLeave {
|
||||
character_id: CharacterEntityId,
|
||||
},
|
||||
QuestStart {
|
||||
// quest id
|
||||
},
|
||||
QuestComplete {
|
||||
// quest id
|
||||
},
|
||||
|
||||
}
|
@ -9,23 +9,22 @@ use std::iter::IntoIterator;
|
||||
use anyhow::Context;
|
||||
|
||||
use libpso::packet::{ship::Message, messages::GameMessage};
|
||||
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
||||
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction};
|
||||
use crate::entity::item::{ItemDetail, NewItemEntity, TradeId, ItemModifier};
|
||||
use crate::entity::item::tool::Tool;
|
||||
use crate::entity::room::RoomEntityId;
|
||||
use crate::ship::map::MapArea;
|
||||
use crate::ship::ship::SendShipPacket;
|
||||
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
||||
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction};
|
||||
use crate::ship::items::state::{ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail};
|
||||
use crate::ship::items::bank::{BankItem, BankItemDetail};
|
||||
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
|
||||
use crate::ship::items::floor::{FloorItem, FloorItemDetail};
|
||||
use crate::ship::items::apply_item::{apply_item, ApplyItemAction};
|
||||
use crate::entity::item::{ItemDetail, NewItemEntity, TradeId};
|
||||
use crate::entity::item::tool::Tool;
|
||||
use crate::entity::item::ItemModifier;
|
||||
use crate::ship::shops::ShopItem;
|
||||
use crate::ship::drops::{ItemDrop, ItemDropType};
|
||||
use crate::ship::packet::builder;
|
||||
use crate::ship::location::AreaClient;
|
||||
use crate::ship::monster::MonsterType;
|
||||
|
||||
pub enum TriggerCreateItem {
|
||||
Yes,
|
||||
@ -514,9 +513,7 @@ where
|
||||
Box::pin(async move {
|
||||
let mut transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
async move {
|
||||
transaction.gateway().add_item_note(&entity_id, ItemNote::Consumed {
|
||||
character_id: character.id,
|
||||
}).await?;
|
||||
transaction.gateway().add_item_note(&entity_id, ItemNote::Consumed).await?;
|
||||
Ok(transaction)
|
||||
}}).await?;
|
||||
|
||||
@ -551,7 +548,7 @@ where
|
||||
let mut transaction = tool.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
async move {
|
||||
transaction.gateway().add_item_note(&entity_id, ItemNote::FedToMag {
|
||||
character_id: character.id,
|
||||
//character_id: character.id,
|
||||
mag: mag_entity_id,
|
||||
}).await?;
|
||||
transaction.gateway().feed_mag(&mag_entity_id, &entity_id).await?;
|
||||
@ -663,9 +660,7 @@ where
|
||||
|
||||
let mut transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
async move {
|
||||
transaction.gateway().add_item_note(&entity_id, ItemNote::SoldToShop {
|
||||
character_id,
|
||||
}).await?;
|
||||
transaction.gateway().add_item_note(&entity_id, ItemNote::SoldToShop).await?;
|
||||
Ok(transaction)
|
||||
}}).await?;
|
||||
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||
@ -909,6 +904,7 @@ where
|
||||
|
||||
|
||||
pub(super) fn convert_item_drop_to_floor_item<'a, EG, TR>(
|
||||
character_id: CharacterEntityId,
|
||||
item_drop: ItemDrop,
|
||||
) -> impl Fn((ItemStateProxy, TR), ())
|
||||
-> BoxFuture<'a, Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>> + Clone
|
||||
@ -950,6 +946,13 @@ where
|
||||
let entity = transaction.gateway().create_item(NewItemEntity {
|
||||
item: item_detail.clone(),
|
||||
}).await?;
|
||||
transaction.gateway().add_item_note(&entity.id, ItemNote::EnemyDrop {
|
||||
character_id,
|
||||
map_area: item_drop.map_area,
|
||||
x: item_drop.x,
|
||||
y: item_drop.y,
|
||||
z: item_drop.z,
|
||||
}).await?;
|
||||
FloorItem {
|
||||
item_id,
|
||||
item: FloorItemDetail::Individual(IndividualItemDetail {
|
||||
@ -966,6 +969,13 @@ where
|
||||
let entity = transaction.gateway().create_item(NewItemEntity {
|
||||
item: ItemDetail::Tool(tool),
|
||||
}).await?;
|
||||
transaction.gateway().add_item_note(&entity.id, ItemNote::EnemyDrop {
|
||||
character_id,
|
||||
map_area: item_drop.map_area,
|
||||
x: item_drop.x,
|
||||
y: item_drop.y,
|
||||
z: item_drop.z,
|
||||
}).await?;
|
||||
FloorItem {
|
||||
item_id,
|
||||
item: FloorItemDetail::Stacked(StackedItemDetail{
|
||||
@ -995,88 +1005,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub(super) fn item_note_enemy_drop<'a, EG, TR>(
|
||||
character_id: CharacterEntityId,
|
||||
room_id: RoomEntityId,
|
||||
monster_type: MonsterType,
|
||||
) -> impl Fn((ItemStateProxy, TR), FloorItem)
|
||||
-> BoxFuture<'a, Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>> + Clone
|
||||
where
|
||||
EG: EntityGateway,
|
||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
|
||||
{
|
||||
move |(item_state, mut transaction), floor_item| {
|
||||
Box::pin(async move {
|
||||
match &floor_item.item {
|
||||
FloorItemDetail::Individual(individual) => {
|
||||
transaction.gateway().add_item_note(&individual.entity_id, ItemNote::EnemyDrop {
|
||||
character_id,
|
||||
room_id,
|
||||
monster_type,
|
||||
map_area: floor_item.map_area,
|
||||
x: floor_item.x,
|
||||
y: floor_item.y,
|
||||
z: floor_item.z,
|
||||
}).await?;
|
||||
},
|
||||
FloorItemDetail::Stacked(stacked) => {
|
||||
transaction.gateway().add_item_note(&stacked.entity_ids[0], ItemNote::EnemyDrop {
|
||||
character_id,
|
||||
room_id,
|
||||
monster_type,
|
||||
map_area: floor_item.map_area,
|
||||
x: floor_item.x,
|
||||
y: floor_item.y,
|
||||
z: floor_item.z,
|
||||
}).await?;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
Ok(((item_state, transaction), floor_item))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn item_note_box_drop<'a, EG, TR>(
|
||||
character_id: CharacterEntityId,
|
||||
room_id: RoomEntityId,
|
||||
) -> impl Fn((ItemStateProxy, TR), FloorItem)
|
||||
-> BoxFuture<'a, Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>> + Clone
|
||||
where
|
||||
EG: EntityGateway,
|
||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
|
||||
{
|
||||
move |(item_state, mut transaction), floor_item| {
|
||||
Box::pin(async move {
|
||||
match &floor_item.item {
|
||||
FloorItemDetail::Individual(individual) => {
|
||||
transaction.gateway().add_item_note(&individual.entity_id, ItemNote::BoxDrop {
|
||||
character_id,
|
||||
room_id,
|
||||
map_area: floor_item.map_area,
|
||||
x: floor_item.x,
|
||||
y: floor_item.y,
|
||||
z: floor_item.z,
|
||||
}).await?;
|
||||
},
|
||||
FloorItemDetail::Stacked(stacked) => {
|
||||
transaction.gateway().add_item_note(&stacked.entity_ids[0], ItemNote::BoxDrop {
|
||||
character_id,
|
||||
room_id,
|
||||
map_area: floor_item.map_area,
|
||||
x: floor_item.x,
|
||||
y: floor_item.y,
|
||||
z: floor_item.z,
|
||||
}).await?;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
Ok(((item_state, transaction), floor_item))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn add_item_to_local_floor<'a, EG, TR>(
|
||||
character_id: CharacterEntityId,
|
||||
) -> impl Fn((ItemStateProxy, TR), FloorItem)
|
||||
@ -1241,4 +1169,4 @@ where
|
||||
Ok(((item_state, transaction), ()))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -6,17 +6,15 @@ use crate::ship::ship::SendShipPacket;
|
||||
use crate::ship::map::MapArea;
|
||||
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
||||
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction};
|
||||
use crate::entity::item::ItemModifier;
|
||||
use crate::entity::room::RoomEntityId;
|
||||
use crate::ship::items::state::{ItemState, ItemStateProxy, IndividualItemDetail};
|
||||
use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction};
|
||||
use crate::ship::items::inventory::InventoryItem;
|
||||
use crate::ship::items::floor::FloorItem;
|
||||
use crate::entity::item::ItemModifier;
|
||||
use crate::ship::shops::ShopItem;
|
||||
use crate::ship::trade::TradeItem;
|
||||
use crate::ship::location::AreaClient;
|
||||
use crate::ship::drops::ItemDrop;
|
||||
use crate::ship::monster::MonsterType;
|
||||
|
||||
use crate::ship::items::actions;
|
||||
|
||||
@ -467,8 +465,6 @@ pub fn enemy_drops_item<'a, EG> (
|
||||
item_state: &'a mut ItemState,
|
||||
entity_gateway: &'a mut EG,
|
||||
character_id: CharacterEntityId,
|
||||
room_id: RoomEntityId,
|
||||
monster_type: MonsterType,
|
||||
item_drop: ItemDrop)
|
||||
-> BoxFuture<'a, Result<FloorItem, anyhow::Error>>
|
||||
where
|
||||
@ -477,32 +473,7 @@ where
|
||||
entity_gateway.with_transaction(move |transaction| async move {
|
||||
let item_state_proxy = ItemStateProxy::new(item_state.clone());
|
||||
let ((item_state_proxy, transaction), floor_item) = ItemStateAction::default()
|
||||
.act(actions::convert_item_drop_to_floor_item(item_drop))
|
||||
.act(actions::item_note_enemy_drop(character_id, room_id, monster_type))
|
||||
.act(actions::add_item_to_local_floor(character_id))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, floor_item))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn box_drops_item<'a, EG> (
|
||||
item_state: &'a mut ItemState,
|
||||
entity_gateway: &'a mut EG,
|
||||
character_id: CharacterEntityId,
|
||||
room_id: RoomEntityId,
|
||||
item_drop: ItemDrop)
|
||||
-> BoxFuture<'a, Result<FloorItem, anyhow::Error>>
|
||||
where
|
||||
EG: EntityGateway + 'static,
|
||||
{
|
||||
entity_gateway.with_transaction(move |transaction| async move {
|
||||
let item_state_proxy = ItemStateProxy::new(item_state.clone());
|
||||
let ((item_state_proxy, transaction), floor_item) = ItemStateAction::default()
|
||||
.act(actions::convert_item_drop_to_floor_item(item_drop))
|
||||
.act(actions::item_note_box_drop(character_id, room_id))
|
||||
.act(actions::convert_item_drop_to_floor_item(character_id, item_drop))
|
||||
.act(actions::add_item_to_local_floor(character_id))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
|
@ -18,7 +18,7 @@ use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem};
|
||||
use crate::ship::items::state::{ItemState, ItemStateError};
|
||||
use crate::ship::items::floor::{FloorType, FloorItemDetail};
|
||||
use crate::ship::items::actions::TriggerCreateItem;
|
||||
use crate::ship::items::tasks::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, box_drops_item, take_meseta, apply_modifier};
|
||||
use crate::ship::items::tasks::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, take_meseta, apply_modifier};
|
||||
|
||||
const BANK_ACTION_DEPOSIT: u8 = 0;
|
||||
const BANK_ACTION_WITHDRAW: u8 = 1;
|
||||
@ -89,8 +89,8 @@ where
|
||||
EG: EntityGateway + 'static,
|
||||
{
|
||||
let room_id = client_location.get_room(id).await?;
|
||||
let (room_entity_id, monster) = rooms.with(room_id, |room| Box::pin(async move {
|
||||
Ok::<_, anyhow::Error>((room.room_id, room.maps.enemy_by_id(request_item.enemy_id as usize)?))
|
||||
let monster = rooms.with(room_id, |room| Box::pin(async move {
|
||||
room.maps.enemy_by_id(request_item.enemy_id as usize)
|
||||
})).await??;
|
||||
|
||||
if monster.dropped_item {
|
||||
@ -121,7 +121,7 @@ where
|
||||
client.character.id
|
||||
})).await?;
|
||||
|
||||
let floor_item = enemy_drops_item(item_state, entity_gateway, character_id, room_entity_id, monster.monster, item_drop).await?;
|
||||
let floor_item = enemy_drops_item(item_state, entity_gateway, character_id, item_drop).await?;
|
||||
let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?;
|
||||
|
||||
item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))));
|
||||
@ -200,8 +200,8 @@ where
|
||||
EG: EntityGateway + Clone + 'static
|
||||
{
|
||||
let room_id = client_location.get_room(id).await?;
|
||||
let (room_entity_id, box_object) = rooms.with(room_id, |room| Box::pin(async move {
|
||||
Ok::<_, anyhow::Error>((room.room_id, room.maps.object_by_id(box_drop_request.object_id as usize)?))
|
||||
let box_object = rooms.with(room_id, |room| Box::pin(async move {
|
||||
room.maps.object_by_id(box_drop_request.object_id as usize)
|
||||
})).await??;
|
||||
|
||||
if box_object.dropped_item {
|
||||
@ -232,7 +232,7 @@ where
|
||||
let character_id = clients.with(area_client.client, |client| Box::pin(async move {
|
||||
client.character.id
|
||||
})).await?;
|
||||
let floor_item = box_drops_item(item_state, entity_gateway, character_id, room_entity_id, item_drop).await?;
|
||||
let floor_item = enemy_drops_item(item_state, entity_gateway, character_id, item_drop).await?;
|
||||
//let floor_item = enemy_drops_item(item_state, &mut entity_gateway, client.character.id, item_drop).await?;
|
||||
let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item)?;
|
||||
item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))))
|
||||
|
@ -8,7 +8,6 @@ use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationEr
|
||||
use crate::ship::packet;
|
||||
use crate::ship::items::state::ItemState;
|
||||
use crate::entity::gateway::EntityGateway;
|
||||
use crate::entity::room::RoomNote;
|
||||
use crate::ship::map::MapArea;
|
||||
use futures::future::join_all;
|
||||
|
||||
@ -90,25 +89,14 @@ where
|
||||
}
|
||||
},
|
||||
RoomLobby::Room(old_room) => {
|
||||
let room_entity_id = rooms.with(old_room, |room| Box::pin(async {
|
||||
room.room_id
|
||||
})).await?;
|
||||
if client_location.get_client_neighbors(id).await?.is_empty() {
|
||||
rooms.remove(old_room).await;
|
||||
}
|
||||
|
||||
let character_id = clients.with(id, |client| Box::pin(async {
|
||||
client.character.id
|
||||
})).await?;
|
||||
clients.with(id, |client| {
|
||||
let mut item_state = item_state.clone();
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
Box::pin(async move {
|
||||
item_state.remove_character_from_room(&client.character).await;
|
||||
entity_gateway.add_room_note(room_entity_id, RoomNote::PlayerLeave {
|
||||
character_id
|
||||
}).await
|
||||
})}).await??;
|
||||
})}).await?;
|
||||
},
|
||||
}
|
||||
let leave_lobby = packet::builder::lobby::remove_from_lobby(id, client_location).await?;
|
||||
|
@ -46,9 +46,8 @@ pub async fn send_quest_category_list(id: ClientId,
|
||||
let room_id = client_location.get_room(id).await?;
|
||||
let rql = rql.clone();
|
||||
rooms.with_mut(room_id, |room| Box::pin(async move {
|
||||
//let qcl = quest::quest_category_list(&room.quests[rql.flag.clamp(0, (room.quests.len() - 1) as u32) as usize]);
|
||||
room.quest_group = rql.flag.into();
|
||||
let qcl = quest::quest_category_list(room.quests());
|
||||
let qcl = quest::quest_category_list(&room.quests[rql.flag.clamp(0, (room.quests.len() - 1) as u32) as usize]);
|
||||
room.set_quest_group(rql.flag as usize);
|
||||
Ok(vec![(id, SendShipPacket::QuestCategoryList(qcl))])
|
||||
})).await?
|
||||
}
|
||||
@ -60,10 +59,10 @@ pub async fn select_quest_category(id: ClientId,
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
let room_id = client_location.get_room(id).await?;
|
||||
rooms.with(room_id, |room| Box::pin(async move {
|
||||
let (_, category_quests) = room.quests()
|
||||
.iter()
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(menuselect.item as usize)
|
||||
.ok_or_else(|| ShipError::InvalidQuestCategory(menuselect.item as u16))?;
|
||||
|
||||
let ql = quest::quest_list(menuselect.item, category_quests);
|
||||
Ok(vec![(id, SendShipPacket::QuestOptionList(ql))])
|
||||
})).await?
|
||||
@ -77,7 +76,7 @@ pub async fn quest_detail(id: ClientId,
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
let room_id = client_location.get_room(id).await?;
|
||||
rooms.with(room_id, |room| Box::pin(async move {
|
||||
let (_, category_quests) = room.quests().iter()
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(questdetailrequest.category as usize)
|
||||
.ok_or_else(|| ShipError::InvalidQuestCategory(questdetailrequest.category))?;
|
||||
|
||||
@ -106,7 +105,7 @@ pub async fn player_chose_quest(id: ClientId,
|
||||
rooms.with_mut(room_id, |room| {
|
||||
let clients = clients.clone();
|
||||
Box::pin(async move {
|
||||
let quest = room.quests().iter()
|
||||
let quest = room.quests[room.quest_group.value()].iter()
|
||||
.nth(questmenuselect.category as usize)
|
||||
.ok_or_else(|| ShipError::InvalidQuestCategory(questmenuselect.category))?
|
||||
.1
|
||||
@ -150,7 +149,7 @@ pub async fn quest_file_request(id: ClientId,
|
||||
let quest_file_request = quest_file_request.clone();
|
||||
rooms.with(room_id, |room| Box::pin(async move {
|
||||
let (category_id, quest_id, datatype) = parse_filename(&quest_file_request.filename)?;
|
||||
let (_, category_quests) = room.quests().iter()
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(category_id as usize)
|
||||
.ok_or_else(|| ShipError::InvalidQuestCategory(category_id))?;
|
||||
|
||||
@ -183,7 +182,7 @@ pub async fn quest_chunk_ack(id: ClientId,
|
||||
let quest_chunk_ack = quest_chunk_ack.clone();
|
||||
rooms.with(room_id, |room| Box::pin(async move {
|
||||
let (category_id, quest_id, datatype) = parse_filename(&quest_chunk_ack.filename)?;
|
||||
let (_, category_quests) = room.quests().iter()
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(category_id as usize)
|
||||
.ok_or_else(|| ShipError::InvalidQuestCategory(category_id))?;
|
||||
|
||||
|
@ -7,9 +7,7 @@ use libpso::packet::ship::*;
|
||||
use libpso::packet::messages::*;
|
||||
use crate::common::serverstate::ClientId;
|
||||
use crate::common::leveltable::LEVEL_TABLE;
|
||||
use crate::entity::gateway::EntityGateway;
|
||||
use crate::entity::character::SectionID;
|
||||
use crate::entity::room::{NewRoomEntity, RoomEntityMode, RoomNote};
|
||||
use crate::ship::drops::DropTable;
|
||||
use crate::ship::ship::{SendShipPacket, Clients, ShipEvent};
|
||||
use crate::ship::room::{Rooms, Episode, Difficulty, RoomState, RoomMode};
|
||||
@ -19,25 +17,20 @@ use crate::ship::packet::builder;
|
||||
use crate::ship::items::state::ItemState;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn create_room<EG>(id: ClientId,
|
||||
create_room: CreateRoom,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
rooms: &Rooms,
|
||||
map_builder: Arc<Box<dyn Fn(RoomMode, ShipEvent) -> Maps + Send + Sync>>,
|
||||
drop_table_builder: Arc<Box<dyn Fn(Episode, Difficulty, SectionID) -> DropTable + Send + Sync>>,
|
||||
event: ShipEvent)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||
where
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
pub async fn create_room(id: ClientId,
|
||||
create_room: CreateRoom,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
rooms: &Rooms,
|
||||
map_builder: Arc<Box<dyn Fn(RoomMode, ShipEvent) -> Maps + Send + Sync>>,
|
||||
drop_table_builder: Arc<Box<dyn Fn(Episode, Difficulty, SectionID) -> DropTable + Send + Sync>>,
|
||||
event: ShipEvent)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
let level = clients.with(id, |client| Box::pin(async move {
|
||||
LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp)
|
||||
})).await?;
|
||||
let difficulty = Difficulty::try_from(create_room.difficulty)?;
|
||||
match difficulty {
|
||||
match Difficulty::try_from(create_room.difficulty)? {
|
||||
Difficulty::Ultimate if level < 80 => {
|
||||
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 80 \nto create Ultimate rooms.".into())))])
|
||||
},
|
||||
@ -51,42 +44,15 @@ where
|
||||
};
|
||||
|
||||
let area = client_location.get_area(id).await?;
|
||||
let old_area_client = client_location.get_local_client(id).await?;
|
||||
let area_client = client_location.get_local_client(id).await?;
|
||||
let lobby_neighbors = client_location.get_client_neighbors(id).await?;
|
||||
|
||||
let room_id = client_location.create_new_room(id).await?;
|
||||
let new_area_client = client_location.get_local_client(id).await?;
|
||||
|
||||
let name = String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).to_string();
|
||||
let mode = match (create_room.battle, create_room.challenge, create_room.single_player) {
|
||||
(1, 0, 0) => RoomEntityMode::Battle,
|
||||
(0, 1, 0) => RoomEntityMode::Challenge,
|
||||
(0, 0, 1) => RoomEntityMode::Single,
|
||||
_ => RoomEntityMode::Multi,
|
||||
};
|
||||
let episode = create_room.episode.try_into()?;
|
||||
let difficulty = create_room.difficulty.try_into()?;
|
||||
|
||||
let room = clients.with(id, |client| {
|
||||
let mut item_state = item_state.clone();
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
Box::pin(async move {
|
||||
item_state.add_character_to_room(room_id, &client.character, new_area_client).await;
|
||||
let room_entity = entity_gateway.create_room(NewRoomEntity {
|
||||
name: name.clone(),
|
||||
section_id: client.character.section_id,
|
||||
mode,
|
||||
episode,
|
||||
difficulty,
|
||||
}).await?;
|
||||
|
||||
entity_gateway.add_room_note(room_entity.id, RoomNote::Create {
|
||||
character_id: client.character.id,
|
||||
}).await?;
|
||||
|
||||
let mut room = RoomState::new(room_entity.id, mode, episode, difficulty,
|
||||
client.character.section_id, name, create_room.password, event,
|
||||
map_builder, drop_table_builder)?;
|
||||
item_state.add_character_to_room(room_id, &client.character, area_client).await;
|
||||
let mut room = RoomState::from_create_room(&create_room, map_builder, drop_table_builder, client.character.section_id, event)?;
|
||||
room.bursting = true;
|
||||
Ok::<_, anyhow::Error>(room)
|
||||
})}).await??;
|
||||
@ -96,7 +62,7 @@ where
|
||||
|
||||
let mut result = vec![(id, SendShipPacket::JoinRoom(join_room))];
|
||||
if let Ok(leader) = client_location.get_area_leader(area).await {
|
||||
let leave_lobby = SendShipPacket::LeaveLobby(LeaveLobby::new(old_area_client.local_client.id(), leader.local_client.id()));
|
||||
let leave_lobby = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()));
|
||||
result.extend(lobby_neighbors
|
||||
.into_iter()
|
||||
.map(move |c| {
|
||||
@ -124,19 +90,14 @@ pub async fn room_name_request(id: ClientId,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn join_room<EG>(id: ClientId,
|
||||
pkt: MenuSelect,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
rooms: &Rooms,
|
||||
event: ShipEvent)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||
where
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
pub async fn join_room(id: ClientId,
|
||||
pkt: MenuSelect,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
rooms: &Rooms,
|
||||
event: ShipEvent)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
let room_id = RoomId(pkt.item as usize);
|
||||
if !rooms.exists(room_id).await {
|
||||
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("This room no longer exists!".into())))])
|
||||
@ -144,8 +105,8 @@ where
|
||||
let level = clients.with(id, |client| Box::pin(async move {
|
||||
LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp)
|
||||
})).await?;
|
||||
let (difficulty, bursting, room_entity_id) = rooms.with(room_id, |room| Box::pin(async move {
|
||||
(room.mode.difficulty(), room.bursting, room.room_id)
|
||||
let (difficulty, bursting) = rooms.with(room_id, |room| Box::pin(async move {
|
||||
(room.mode.difficulty(), room.bursting)
|
||||
})).await?;
|
||||
|
||||
match difficulty {
|
||||
@ -174,14 +135,9 @@ where
|
||||
|
||||
clients.with(id, |client| {
|
||||
let mut item_state = item_state.clone();
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
Box::pin(async move {
|
||||
entity_gateway.add_room_note(room_entity_id, RoomNote::PlayerJoin {
|
||||
character_id: client.character.id,
|
||||
}).await?;
|
||||
item_state.add_character_to_room(room_id, &client.character, area_client).await;
|
||||
Ok::<_, anyhow::Error>(())
|
||||
})}).await??;
|
||||
})}).await?;
|
||||
|
||||
let join_room = rooms.with(room_id, |room| {
|
||||
let clients = clients.clone();
|
||||
@ -198,7 +154,7 @@ where
|
||||
rooms.with_mut(room_id, |room| Box::pin(async move {
|
||||
room.bursting = true;
|
||||
})).await?;
|
||||
|
||||
|
||||
Ok(vec![(id, SendShipPacket::JoinRoom(join_room))]
|
||||
.into_iter()
|
||||
.chain(original_room_clients.into_iter()
|
||||
|
@ -11,7 +11,7 @@ use ages_prs::{LegacyPrsDecoder, LegacyPrsEncoder};
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use libpso::util::array_to_utf16;
|
||||
use crate::ship::map::{MapArea, MapAreaError, MapObject, MapEnemy, enemy_data_from_stream, objects_from_stream};
|
||||
use crate::ship::room::{Episode, RoomMode};
|
||||
use crate::ship::room::Episode;
|
||||
use crate::ship::map::area::{MapAreaLookup, MapAreaLookupBuilder};
|
||||
|
||||
|
||||
@ -152,14 +152,11 @@ fn parse_dat(dat: &[u8], episode: &Episode, map_areas: &MapAreaLookup) -> Result
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("")]
|
||||
pub enum QuestLoadError {
|
||||
#[error("io error {0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
#[error("parse dat error {0}")]
|
||||
ParseDatError(#[from] ParseDatError),
|
||||
#[error("could not read metadata")]
|
||||
CouldNotReadMetadata,
|
||||
#[error("could not load config file")]
|
||||
CouldNotLoadConfigFile,
|
||||
}
|
||||
|
||||
@ -236,7 +233,7 @@ pub fn load_quest(bin_path: PathBuf, dat_path: PathBuf, quest_path: PathBuf) ->
|
||||
}
|
||||
|
||||
|
||||
pub fn load_quests_path(mut quest_path: PathBuf) -> Result<QuestList, QuestLoadError> {
|
||||
pub fn load_quests(mut quest_path: PathBuf) -> Result<QuestList, QuestLoadError> {
|
||||
let mut f = File::open(quest_path.clone()).map_err(|_| QuestLoadError::CouldNotLoadConfigFile)?;
|
||||
let mut s = String::new();
|
||||
f.read_to_string(&mut s)?;
|
||||
@ -245,59 +242,29 @@ pub fn load_quests_path(mut quest_path: PathBuf) -> Result<QuestList, QuestLoadE
|
||||
let ql: BTreeMap<String, QuestListCategory> = toml::from_str(s.as_str()).map_err(|_| QuestLoadError::CouldNotLoadConfigFile)?;
|
||||
|
||||
Ok(ql.into_iter().map(|(category, category_details)| {
|
||||
(
|
||||
QuestCategory {
|
||||
index: category_details.list_order,
|
||||
name: category,
|
||||
description: category_details.description,
|
||||
},
|
||||
category_details.quests
|
||||
.into_iter()
|
||||
.filter_map(|quest| {
|
||||
load_quest(quest.bin.into(), quest.dat.into(), quest_path.to_path_buf())
|
||||
.and_then(|quest | {
|
||||
if used_quest_ids.contains(&quest.id) {
|
||||
warn!("quest id already exists: {}", quest.id);
|
||||
return None;
|
||||
}
|
||||
used_quest_ids.insert(quest.id);
|
||||
Some(quest)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
)
|
||||
let quests = category_details.quests
|
||||
.into_iter()
|
||||
.filter_map(|quest| {
|
||||
load_quest(quest.bin.into(), quest.dat.into(), quest_path.to_path_buf())
|
||||
.and_then(|quest | {
|
||||
if used_quest_ids.contains(&quest.id) {
|
||||
warn!("quest id already exists: {}", quest.id);
|
||||
return None;
|
||||
}
|
||||
used_quest_ids.insert(quest.id);
|
||||
Some(quest)
|
||||
})
|
||||
});
|
||||
(QuestCategory{
|
||||
index: category_details.list_order,
|
||||
name: category,
|
||||
description: category_details.description,
|
||||
}, quests.collect())
|
||||
}).collect())
|
||||
}
|
||||
|
||||
pub fn load_standard_quests(mode: RoomMode) -> Result<QuestList, QuestLoadError> {
|
||||
match mode {
|
||||
RoomMode::Single {episode, .. } => {
|
||||
load_quests_path(PathBuf::from_iter(["data", "quests", "bb", &episode.to_string(), "single", "quests.toml"]))
|
||||
},
|
||||
RoomMode::Multi {episode, .. } => {
|
||||
load_quests_path(PathBuf::from_iter(["data", "quests", "bb", &episode.to_string(), "multi", "quests.toml"]))
|
||||
},
|
||||
_ => {
|
||||
Ok(BTreeMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn load_government_quests(mode: RoomMode) -> Result<QuestList, QuestLoadError> {
|
||||
match mode {
|
||||
RoomMode::Single {episode, .. } => {
|
||||
load_quests_path(PathBuf::from_iter(["data", "quests", "bb", &episode.to_string(), "government", "quests.toml"]))
|
||||
},
|
||||
RoomMode::Multi {episode, .. } => {
|
||||
load_quests_path(PathBuf::from_iter(["data", "quests", "bb", &episode.to_string(), "government", "quests.toml"]))
|
||||
},
|
||||
_ => {
|
||||
Ok(BTreeMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
152
src/ship/room.rs
152
src/ship/room.rs
@ -1,5 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::convert::{From, Into, TryFrom};
|
||||
use std::convert::{From, Into, TryFrom, TryInto};
|
||||
use std::path::PathBuf;
|
||||
use async_std::sync::{Arc, RwLock, RwLockReadGuard};
|
||||
use futures::future::BoxFuture;
|
||||
use futures::stream::{FuturesOrdered, Stream};
|
||||
@ -10,7 +11,6 @@ use rand::Rng;
|
||||
use crate::ship::map::Maps;
|
||||
use crate::ship::drops::DropTable;
|
||||
use crate::entity::character::SectionID;
|
||||
use crate::entity::room::{RoomEntityId, RoomEntityMode};
|
||||
use crate::ship::monster::{load_monster_stats_table, MonsterType, MonsterStats};
|
||||
use crate::ship::map::area::MapAreaLookup;
|
||||
use crate::ship::quests;
|
||||
@ -55,7 +55,7 @@ impl Rooms {
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub async fn with<'a, T, F>(&'a self, room_id: RoomId, func: F) -> Result<T, anyhow::Error>
|
||||
where
|
||||
T: Send,
|
||||
@ -92,7 +92,7 @@ impl Rooms {
|
||||
Err(ShipError::InvalidRoom(room_id.0 as u32).into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub async fn get(&self, room_id: RoomId) -> RwLockReadGuard<Option<RoomState>> {
|
||||
self.0
|
||||
.get(room_id.0)
|
||||
@ -282,15 +282,8 @@ impl From<usize> for QuestCategoryType {
|
||||
fn from(f: usize) -> QuestCategoryType {
|
||||
match f {
|
||||
0 => QuestCategoryType::Standard,
|
||||
_ => QuestCategoryType::Government,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<u32> for QuestCategoryType {
|
||||
fn from(f: u32) -> QuestCategoryType {
|
||||
match f {
|
||||
0 => QuestCategoryType::Standard,
|
||||
_ => QuestCategoryType::Government,
|
||||
1 => QuestCategoryType::Government,
|
||||
_ => QuestCategoryType::Standard, // TODO: panic?
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -305,7 +298,6 @@ impl QuestCategoryType {
|
||||
}
|
||||
|
||||
pub struct RoomState {
|
||||
pub room_id: RoomEntityId,
|
||||
pub mode: RoomMode,
|
||||
pub name: String,
|
||||
pub password: [u16; 16],
|
||||
@ -317,22 +309,22 @@ pub struct RoomState {
|
||||
pub monster_stats: Box<HashMap<MonsterType, MonsterStats>>,
|
||||
pub map_areas: MapAreaLookup,
|
||||
pub quest_group: QuestCategoryType,
|
||||
pub standard_quests: quests::QuestList,
|
||||
pub government_quests: quests::QuestList,
|
||||
pub quests: Vec<quests::QuestList>,
|
||||
// items on ground
|
||||
// enemy info
|
||||
}
|
||||
|
||||
impl RoomState {
|
||||
pub fn get_flags_for_room_list(&self) -> u8 {
|
||||
let mut flags = 0u8;
|
||||
|
||||
|
||||
match self.mode {
|
||||
RoomMode::Single {..} => {flags += 0x04}
|
||||
RoomMode::Battle {..} => {flags += 0x10},
|
||||
RoomMode::Challenge {..} => {flags += 0x20},
|
||||
_ => {flags += 0x40},
|
||||
};
|
||||
|
||||
|
||||
if self.password[0] > 0 {
|
||||
flags += 0x02;
|
||||
}
|
||||
@ -353,59 +345,85 @@ impl RoomState {
|
||||
difficulty + 0x22
|
||||
}
|
||||
|
||||
pub fn quests(&self) -> &quests::QuestList {
|
||||
match self.quest_group {
|
||||
QuestCategoryType::Standard => &self.standard_quests,
|
||||
QuestCategoryType::Government => &self.government_quests,
|
||||
}
|
||||
pub fn set_quest_group(&mut self, group: usize) {
|
||||
self.quest_group = QuestCategoryType::from(group);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new (room_id: RoomEntityId,
|
||||
mode: RoomEntityMode,
|
||||
episode: Episode,
|
||||
difficulty: Difficulty,
|
||||
section_id: SectionID,
|
||||
name: String,
|
||||
password: [u16; 16],
|
||||
event: ShipEvent,
|
||||
map_builder: Arc<Box<dyn Fn(RoomMode, ShipEvent) -> Maps + Send + Sync>>,
|
||||
drop_table_builder: Arc<Box<dyn Fn(Episode, Difficulty, SectionID) -> DropTable + Send + Sync>>,
|
||||
) -> Result<RoomState, anyhow::Error> {
|
||||
let mode = match mode {
|
||||
RoomEntityMode::Single => RoomMode::Single {
|
||||
episode,
|
||||
difficulty,
|
||||
},
|
||||
RoomEntityMode::Multi => RoomMode::Multi {
|
||||
episode,
|
||||
difficulty,
|
||||
},
|
||||
RoomEntityMode::Challenge => RoomMode::Challenge {
|
||||
episode,
|
||||
},
|
||||
RoomEntityMode::Battle => RoomMode::Battle {
|
||||
episode,
|
||||
difficulty,
|
||||
},
|
||||
pub fn from_create_room(create_room: &libpso::packet::ship::CreateRoom,
|
||||
map_builder: Arc<Box<dyn Fn(RoomMode, ShipEvent) -> Maps + Send + Sync>>,
|
||||
drop_table_builder: Arc<Box<dyn Fn(Episode, Difficulty, SectionID) -> DropTable + Send + Sync>>,
|
||||
section_id: SectionID,
|
||||
event: ShipEvent)
|
||||
-> Result<RoomState, RoomCreationError> {
|
||||
if [create_room.battle, create_room.challenge, create_room.single_player].iter().sum::<u8>() > 1 {
|
||||
return Err(RoomCreationError::InvalidMode)
|
||||
}
|
||||
|
||||
let room_mode = if create_room.battle == 1 {
|
||||
RoomMode::Battle {
|
||||
episode: create_room.episode.try_into()?,
|
||||
difficulty: create_room.difficulty.try_into()?,
|
||||
}
|
||||
}
|
||||
else if create_room.challenge == 1 {
|
||||
RoomMode::Challenge {
|
||||
episode: create_room.episode.try_into()?,
|
||||
}
|
||||
}
|
||||
else if create_room.single_player == 1 {
|
||||
RoomMode::Single {
|
||||
episode: create_room.episode.try_into()?,
|
||||
difficulty: create_room.difficulty.try_into()?,
|
||||
}
|
||||
}
|
||||
else { // normal multimode
|
||||
RoomMode::Multi {
|
||||
episode: create_room.episode.try_into()?,
|
||||
difficulty: create_room.difficulty.try_into()?,
|
||||
}
|
||||
};
|
||||
|
||||
Ok(RoomState {
|
||||
room_id,
|
||||
monster_stats: Box::new(load_monster_stats_table(&mode).map_err(|_| RoomCreationError::CouldNotLoadMonsterStats(mode))?),
|
||||
mode,
|
||||
random_seed: rand::thread_rng().gen(),
|
||||
name,
|
||||
password,
|
||||
maps: map_builder(mode, event),
|
||||
section_id,
|
||||
drop_table: Box::new(drop_table_builder(episode, difficulty, section_id)),
|
||||
bursting: false,
|
||||
map_areas: MapAreaLookup::new(&episode),
|
||||
quest_group: QuestCategoryType::Standard,
|
||||
standard_quests: quests::load_standard_quests(mode)?,
|
||||
government_quests: quests::load_government_quests(mode)?,
|
||||
})
|
||||
|
||||
// push the usual set of quests for the selected mode
|
||||
let mut qpath = PathBuf::from("data/quests/bb");
|
||||
qpath.push(room_mode.episode().to_string());
|
||||
qpath.push(room_mode.to_string());
|
||||
qpath.push("quests.toml");
|
||||
let mut room_quests = Vec::new();
|
||||
let quest_list = match quests::load_quests(qpath) {
|
||||
Ok(qlist) => qlist,
|
||||
Err(_) => return Err(RoomCreationError::CouldNotLoadQuests),
|
||||
};
|
||||
|
||||
room_quests.push(quest_list);
|
||||
|
||||
// if multiplayer also push the government quests
|
||||
if let RoomMode::Multi {..} = room_mode {
|
||||
qpath = PathBuf::from("data/quests/bb/");
|
||||
qpath.push(room_mode.episode().to_string());
|
||||
qpath.push("government/quests.toml");
|
||||
|
||||
let quest_list = match quests::load_quests(qpath) {
|
||||
Ok(qlist) => qlist,
|
||||
Err(_) => return Err(RoomCreationError::CouldNotLoadQuests),
|
||||
};
|
||||
|
||||
room_quests.push(quest_list);
|
||||
}
|
||||
|
||||
Ok(RoomState {
|
||||
monster_stats: Box::new(load_monster_stats_table(&room_mode).map_err(|_| RoomCreationError::CouldNotLoadMonsterStats(room_mode))?),
|
||||
mode: room_mode,
|
||||
random_seed: rand::thread_rng().gen(),
|
||||
name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(),
|
||||
password: create_room.password,
|
||||
maps: map_builder(room_mode, event),
|
||||
section_id,
|
||||
drop_table: Box::new(drop_table_builder(room_mode.episode(), room_mode.difficulty(), section_id)),
|
||||
bursting: false,
|
||||
map_areas: MapAreaLookup::new(&room_mode.episode()),
|
||||
quest_group: QuestCategoryType::Standard,
|
||||
quests: room_quests,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ use crate::common::interserver::{AuthToken, Ship, ServerId, InterserverActor, Lo
|
||||
use crate::login::character::SHIP_MENU_ID;
|
||||
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||
use crate::entity::character::SectionID;
|
||||
use crate::entity::room::RoomNote;
|
||||
use crate::ship::location::{ClientLocation, RoomLobby, ClientLocationError, RoomId};
|
||||
use crate::ship::drops::DropTable;
|
||||
use crate::ship::items;
|
||||
@ -699,7 +698,7 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
|
||||
let select_block = handler::lobby::block_selected(id, menuselect, &self.clients, &self.item_state).await?.into_iter();
|
||||
leave_lobby.chain(select_block).collect()
|
||||
}
|
||||
ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut self.entity_gateway, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms, self.event).await?,
|
||||
ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms, self.event).await?,
|
||||
QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &block.client_location, &block.rooms).await?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@ -724,7 +723,7 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
|
||||
menu: room_password_req.menu,
|
||||
item: room_password_req.item,
|
||||
};
|
||||
handler::room::join_room(id, menuselect, &mut self.entity_gateway, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms, self.event).await?
|
||||
handler::room::join_room(id, menuselect, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms, self.event).await?
|
||||
}
|
||||
else {
|
||||
vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))]
|
||||
@ -756,8 +755,7 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
|
||||
},
|
||||
RecvShipPacket::CreateRoom(create_room) => {
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::room::create_room(id, create_room, &mut self.entity_gateway, &mut block.client_location, &self.clients, &mut self.item_state,
|
||||
&block.rooms, self.map_builder.clone(), self.drop_table_builder.clone(), self.event).await?
|
||||
handler::room::create_room(id, create_room, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms, self.map_builder.clone(), self.drop_table_builder.clone(), self.event).await?
|
||||
},
|
||||
RecvShipPacket::RoomNameRequest(_req) => {
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
@ -852,16 +850,6 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
|
||||
|
||||
let pkt = match block.client_location.get_area(id).await? {
|
||||
RoomLobby::Room(room) => {
|
||||
let character_id = self.clients.with(id, |client| Box::pin(async {
|
||||
client.character.id
|
||||
})).await?;
|
||||
block.rooms.with(room, |room| {
|
||||
let mut entity_gateway = self.entity_gateway.clone();
|
||||
Box::pin(async move {
|
||||
entity_gateway.add_room_note(room.room_id, RoomNote::PlayerJoin {
|
||||
character_id,
|
||||
}).await
|
||||
})}).await;
|
||||
if neighbors.is_empty() {
|
||||
block.rooms.remove(room).await;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user