Browse Source
Merge pull request 'cratesplitting' (#141) from cratesplitting into master
Merge pull request 'cratesplitting' (#141) from cratesplitting into master
Reviewed-on: #141pull/143/head
jake
1 year ago
145 changed files with 2487 additions and 2110 deletions
-
16.drone.yml
-
73Cargo.toml
-
20client/Cargo.toml
-
27client/src/lib.rs
-
17drops/Cargo.toml
-
29drops/src/box_drop_table.rs
-
22drops/src/generic_armor.rs
-
18drops/src/generic_shield.rs
-
14drops/src/generic_unit.rs
-
18drops/src/generic_weapon.rs
-
94drops/src/lib.rs
-
28drops/src/rare_drop_table.rs
-
10drops/src/tech_table.rs
-
16drops/src/tool_table.rs
-
23entity/Cargo.toml
-
0entity/src/account.rs
-
4entity/src/character.rs
-
8entity/src/gateway/entitygateway.rs
-
10entity/src/gateway/inmemory.rs
-
0entity/src/gateway/mod.rs
-
0entity/src/gateway/postgres/migrations/V0001__initial.sql
-
0entity/src/gateway/postgres/migrations/V0002__equips.sql
-
0entity/src/gateway/postgres/migrations/V0003__item_notes.sql
-
0entity/src/gateway/postgres/migrations/V0004__meseta.sql
-
0entity/src/gateway/postgres/migrations/V0005__trade.sql
-
0entity/src/gateway/postgres/migrations/V0006__playtime.sql
-
0entity/src/gateway/postgres/migrations/V0007__player_keyconfig.sql
-
0entity/src/gateway/postgres/migrations/V0008__playtime_not_null.sql
-
0entity/src/gateway/postgres/migrations/V0009__no_player_keyconfig.sql
-
0entity/src/gateway/postgres/migrations/V0010__char_create_timestamp.sql
-
0entity/src/gateway/postgres/migrations/V0011__shared_bank.sql
-
0entity/src/gateway/postgres/migrations/V0012__room.sql
-
0entity/src/gateway/postgres/migrations/V0013__room2.sql
-
3entity/src/gateway/postgres/migrations/mod.rs
-
0entity/src/gateway/postgres/mod.rs
-
14entity/src/gateway/postgres/models.rs
-
14entity/src/gateway/postgres/postgres.rs
-
2entity/src/item/armor.rs
-
0entity/src/item/esweapon.rs
-
8entity/src/item/mag.rs
-
30entity/src/item/mod.rs
-
0entity/src/item/shield.rs
-
0entity/src/item/tech.rs
-
0entity/src/item/tool.rs
-
0entity/src/item/unit.rs
-
2entity/src/item/weapon.rs
-
0entity/src/lib.rs
-
4entity/src/room.rs
-
31entity/src/team.rs
-
25items/Cargo.toml
-
52items/src/actions.rs
-
20items/src/apply_item.rs
-
30items/src/bank.rs
-
20items/src/floor.rs
-
20items/src/inventory.rs
-
0items/src/itemstateaction.rs
-
3items/src/lib.rs
-
18items/src/manager.rs
-
26items/src/state.rs
-
43items/src/tasks.rs
-
38items/src/trade.rs
-
12location/Cargo.toml
-
2location/src/lib.rs
-
14maps/Cargo.toml
-
2maps/src/area.rs
-
37maps/src/enemy.rs
-
59maps/src/lib.rs
-
28maps/src/maps.rs
-
4maps/src/monster.rs
-
8maps/src/object.rs
-
150maps/src/room.rs
-
3maps/src/variant.rs
-
18networking/Cargo.toml
-
0networking/src/cipherkeys.rs
-
4networking/src/interserver.rs
-
1networking/src/lib.rs
-
6networking/src/mainloop/client.rs
-
20networking/src/mainloop/interserver.rs
-
0networking/src/mainloop/mod.rs
-
0networking/src/serverstate.rs
-
22pktbuilder/Cargo.toml
-
11pktbuilder/src/character.rs
-
11pktbuilder/src/lib.rs
-
15pktbuilder/src/lobby.rs
-
106pktbuilder/src/message.rs
-
5pktbuilder/src/quest.rs
-
22pktbuilder/src/room.rs
-
5pktbuilder/src/ship.rs
-
107pktbuilder/src/team.rs
-
0pktbuilder/src/trade.rs
-
18quests/Cargo.toml
-
11quests/src/lib.rs
-
17room/Cargo.toml
-
213room/src/lib.rs
-
17shops/Cargo.toml
-
16shops/src/armor.rs
-
95shops/src/lib.rs
-
23shops/src/tool.rs
-
16shops/src/weapon.rs
-
10src/bin/login.rs
@ -0,0 +1,20 @@ |
|||||
|
[package] |
||||
|
name = "client" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
|
||||
|
[dependencies] |
||||
|
entity = { workspace = true } |
||||
|
maps = { workspace = true } |
||||
|
networking = { workspace = true } |
||||
|
shops = { workspace = true } |
||||
|
items = { workspace = true } |
||||
|
|
||||
|
libpso = { workspace = true } |
||||
|
|
||||
|
async-std = { workspace = true } |
||||
|
futures = { workspace = true } |
||||
|
anyhow = { workspace = true } |
||||
|
thiserror = { workspace = true } |
||||
|
chrono = { workspace = true } |
@ -0,0 +1,17 @@ |
|||||
|
[package] |
||||
|
name = "drops" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
|
||||
|
[dependencies] |
||||
|
entity = { workspace = true } |
||||
|
maps = { workspace = true } |
||||
|
stats = { workspace = true } |
||||
|
|
||||
|
rand = { workspace = true } |
||||
|
rand_chacha = { workspace = true } |
||||
|
serde = { workspace = true } |
||||
|
enum-utils = { workspace = true } |
||||
|
toml = { workspace = true } |
||||
|
chrono = { workspace = true } |
@ -1,14 +1,14 @@ |
|||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||
use rand::{Rng};
|
|
||||
|
use rand::Rng;
|
||||
use rand::seq::IteratorRandom;
|
use rand::seq::IteratorRandom;
|
||||
|
|
||||
use crate::entity::item::unit::{UnitType, Unit, UnitModifier};
|
|
||||
use crate::ship::room::{Difficulty, Episode};
|
|
||||
use crate::ship::map::MapArea;
|
|
||||
use crate::entity::character::SectionID;
|
|
||||
use crate::ship::drops::{ItemDropType, load_data_file};
|
|
||||
use crate::ship::item_stats::{unit_stats, UnitStats};
|
|
||||
|
use entity::character::SectionID;
|
||||
|
use entity::item::unit::{UnitType, Unit, UnitModifier};
|
||||
|
use maps::room::{Difficulty, Episode};
|
||||
|
use maps::area::MapArea;
|
||||
|
use crate::{ItemDropType, load_data_file};
|
||||
|
use stats::items::{unit_stats, UnitStats};
|
||||
|
|
||||
|
|
||||
|
|
@ -1,20 +1,20 @@ |
|||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||
use rand::Rng;
|
use rand::Rng;
|
||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||
use crate::entity::item::weapon::{Weapon, WeaponType};
|
|
||||
use crate::entity::item::armor::{Armor, ArmorType};
|
|
||||
use crate::entity::item::shield::{Shield, ShieldType};
|
|
||||
use crate::entity::item::unit::{Unit, UnitType};
|
|
||||
use crate::entity::item::tool::{Tool, ToolType};
|
|
||||
use crate::entity::item::mag::{Mag, MagType};
|
|
||||
use crate::entity::character::SectionID;
|
|
||||
use crate::ship::monster::MonsterType;
|
|
||||
use crate::ship::room::{Difficulty, Episode};
|
|
||||
use crate::ship::map::MapArea;
|
|
||||
use crate::ship::drops::{ItemDropType, load_data_file};
|
|
||||
use crate::ship::drops::generic_weapon::AttributeTable;
|
|
||||
use crate::ship::drops::generic_armor::GenericArmorTable;
|
|
||||
use crate::ship::drops::generic_shield::GenericShieldTable;
|
|
||||
|
use entity::item::weapon::{Weapon, WeaponType};
|
||||
|
use entity::item::armor::{Armor, ArmorType};
|
||||
|
use entity::item::shield::{Shield, ShieldType};
|
||||
|
use entity::item::unit::{Unit, UnitType};
|
||||
|
use entity::item::tool::{Tool, ToolType};
|
||||
|
use entity::item::mag::{Mag, MagType};
|
||||
|
use entity::character::SectionID;
|
||||
|
use maps::monster::MonsterType;
|
||||
|
use maps::room::{Difficulty, Episode};
|
||||
|
use maps::area::MapArea;
|
||||
|
use crate::{ItemDropType, load_data_file};
|
||||
|
use crate::generic_weapon::AttributeTable;
|
||||
|
use crate::generic_armor::GenericArmorTable;
|
||||
|
use crate::generic_shield::GenericShieldTable;
|
||||
|
|
||||
type ItemParseFn = Box<dyn Fn(&String) -> Option<RareDropItem>>;
|
type ItemParseFn = Box<dyn Fn(&String) -> Option<RareDropItem>>;
|
||||
|
|
@ -1,14 +1,14 @@ |
|||||
use std::collections::{BTreeMap};
|
|
||||
|
use std::collections::BTreeMap;
|
||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||
use rand::{Rng};
|
|
||||
|
use rand::Rng;
|
||||
use rand::distributions::{WeightedIndex, Distribution};
|
use rand::distributions::{WeightedIndex, Distribution};
|
||||
|
|
||||
use crate::entity::item::tool::{Tool, ToolType};
|
|
||||
use crate::ship::room::{Difficulty, Episode};
|
|
||||
use crate::ship::map::MapArea;
|
|
||||
use crate::entity::character::SectionID;
|
|
||||
use crate::ship::drops::{ItemDropType, load_data_file};
|
|
||||
use crate::ship::drops::tech_table::TechniqueTable;
|
|
||||
|
use entity::item::tool::{Tool, ToolType};
|
||||
|
use maps::room::{Difficulty, Episode};
|
||||
|
use maps::area::MapArea;
|
||||
|
use entity::character::SectionID;
|
||||
|
use crate::{ItemDropType, load_data_file};
|
||||
|
use crate::tech_table::TechniqueTable;
|
||||
|
|
||||
|
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, enum_utils::FromStr)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, enum_utils::FromStr)]
|
@ -0,0 +1,23 @@ |
|||||
|
[package] |
||||
|
name = "entity" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
[dependencies] |
||||
|
libpso = { workspace = true } |
||||
|
maps = { workspace = true } |
||||
|
chrono = { workspace = true } |
||||
|
anyhow = { workspace = true } |
||||
|
async-std = { workspace = true } |
||||
|
sqlx = { workspace = true } |
||||
|
thiserror = { workspace = true } |
||||
|
serde = { workspace = true } |
||||
|
async-trait = { workspace = true } |
||||
|
enum-utils = { workspace = true } |
||||
|
derive_more = { workspace = true } |
||||
|
refinery = { workspace = true } |
||||
|
lazy_static = { workspace = true } |
||||
|
futures = { workspace = true } |
||||
|
strum = { workspace = true } |
||||
|
strum_macros = { workspace = true } |
||||
|
toml = { workspace = true } |
@ -1,10 +1,10 @@ |
|||||
use thiserror::Error;
|
use thiserror::Error;
|
||||
use futures::future::{Future, BoxFuture};
|
use futures::future::{Future, BoxFuture};
|
||||
|
|
||||
use crate::entity::account::*;
|
|
||||
use crate::entity::character::*;
|
|
||||
use crate::entity::item::*;
|
|
||||
use crate::entity::room::*;
|
|
||||
|
use crate::account::*;
|
||||
|
use crate::character::*;
|
||||
|
use crate::item::*;
|
||||
|
use crate::room::*;
|
||||
|
|
||||
|
|
||||
// TODO: better granularity?
|
// TODO: better granularity?
|
@ -0,0 +1,3 @@ |
|||||
|
use refinery::include_migration_mods;
|
||||
|
|
||||
|
include_migration_mods!("src/gateway/postgres/migrations");
|
@ -1,5 +1,5 @@ |
|||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||
use crate::entity::item::ItemEntityId;
|
|
||||
|
use crate::item::ItemEntityId;
|
||||
|
|
||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ItemParseError {
|
pub enum ItemParseError {
|
@ -1,9 +1,9 @@ |
|||||
use thiserror::Error;
|
|
||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||
|
use thiserror::Error;
|
||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||
use crate::entity::item::tool::ToolType;
|
|
||||
use crate::entity::character::{CharacterClass, SectionID};
|
|
||||
use crate::entity::item::ItemEntityId;
|
|
||||
|
use crate::item::tool::ToolType;
|
||||
|
use crate::character::{CharacterClass, SectionID};
|
||||
|
use crate::item::ItemEntityId;
|
||||
use std::io::Read;
|
use std::io::Read;
|
||||
|
|
||||
use std::cmp::Ordering::{Less, Greater, Equal};
|
use std::cmp::Ordering::{Less, Greater, Equal};
|
@ -1,4 +1,4 @@ |
|||||
use crate::entity::item::ItemEntityId;
|
|
||||
|
use crate::item::ItemEntityId;
|
||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||
|
|
||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
@ -1,8 +1,8 @@ |
|||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||
|
|
||||
|
|
||||
use crate::entity::character::{CharacterEntityId, SectionID};
|
|
||||
use crate::ship::room::{Episode, Difficulty};
|
|
||||
|
use crate::character::{CharacterEntityId, SectionID};
|
||||
|
use maps::room::{Episode, Difficulty};
|
||||
|
|
||||
|
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
@ -0,0 +1,31 @@ |
|||||
|
use serde::{Serialize, Deserialize};
|
||||
|
|
||||
|
use super::account::UserAccountId;
|
||||
|
|
||||
|
// [2022-10-23 00:11:18][elseware::common::mainloop::client][WARN] error RecvServerPacket::from_bytes: WrongPacketForServerType(490, [40, 0, 234, 1, 0, 0, 0, 0, 9, 0, 74, 0, 97, 0, 115, 0, 100, 0, 102, 0, 0, 0, 0, 0, 192, 52, 67, 3, 60, 159, 129, 0, 32, 64, 233, 10, 196, 156, 152, 0])
|
||||
|
// [2022-10-23 00:20:14][elseware::common::mainloop::client][WARN] error RecvServerPacket::from_bytes: WrongPacketForServerType(490, [40, 0, 234, 1, 0, 0, 0, 0, 9, 0, 74, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 0, 0, 152, 0])
|
||||
|
|
||||
|
|
||||
|
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
|
pub struct TeamEntityId(pub u32);
|
||||
|
|
||||
|
pub struct NewTeamEntity {
|
||||
|
pub created_by: UserAccountId,
|
||||
|
pub name: String,
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
#[derive(Debug, Clone)]
|
||||
|
pub struct TeamEntity {
|
||||
|
pub id: TeamEntityId,
|
||||
|
pub owner: UserAccountId,
|
||||
|
pub name: String,
|
||||
|
|
||||
|
pub team_flag: [u8; 2048],
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1,25 @@ |
|||||
|
[package] |
||||
|
name = "items" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
[dependencies] |
||||
|
entity = { workspace = true } |
||||
|
maps = { workspace = true } |
||||
|
shops = { workspace = true } |
||||
|
location = { workspace = true } |
||||
|
drops = { workspace = true } |
||||
|
|
||||
|
libpso = { workspace = true } |
||||
|
|
||||
|
enum-utils = { workspace = true } |
||||
|
derive_more = { workspace = true } |
||||
|
serde = { workspace = true } |
||||
|
rand = { workspace = true } |
||||
|
rand_chacha = { workspace = true } |
||||
|
async-recursion = { workspace = true } |
||||
|
async-std = { workspace = true } |
||||
|
async-trait = { workspace = true } |
||||
|
futures = { workspace = true } |
||||
|
anyhow = { workspace = true } |
||||
|
thiserror = { workspace = true } |
@ -1,18 +1,18 @@ |
|||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||
use libpso::character::character;
|
use libpso::character::character;
|
||||
use crate::ship::items::ClientItemId;
|
|
||||
use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, EquippedEntity};
|
|
||||
|
use crate::ClientItemId;
|
||||
|
use entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, EquippedEntity};
|
||||
use std::future::Future;
|
use std::future::Future;
|
||||
use async_std::sync::{Arc, Mutex};
|
use async_std::sync::{Arc, Mutex};
|
||||
|
|
||||
use crate::entity::character::CharacterEntityId;
|
|
||||
use crate::entity::item::tool::ToolType;
|
|
||||
use crate::entity::item::mag::Mag;
|
|
||||
use crate::entity::item::weapon::Weapon;
|
|
||||
use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem};
|
|
||||
use crate::ship::items::state::ItemStateError;
|
|
||||
use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult};
|
|
||||
use crate::ship::items::floor::{FloorItem, FloorItemDetail};
|
|
||||
|
use entity::character::CharacterEntityId;
|
||||
|
use entity::item::tool::ToolType;
|
||||
|
use entity::item::mag::Mag;
|
||||
|
use entity::item::weapon::Weapon;
|
||||
|
use shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem};
|
||||
|
use crate::state::ItemStateError;
|
||||
|
use crate::state::{IndividualItemDetail, StackedItemDetail, AddItemResult};
|
||||
|
use crate::floor::{FloorItem, FloorItemDetail};
|
||||
|
|
||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||
pub enum InventoryItemDetail {
|
pub enum InventoryItemDetail {
|
@ -0,0 +1,38 @@ |
|||||
|
use crate::ClientItemId;
|
||||
|
|
||||
|
#[derive(Debug, Clone)]
|
||||
|
pub enum TradeItem {
|
||||
|
Individual(ClientItemId),
|
||||
|
Stacked(ClientItemId, usize),
|
||||
|
}
|
||||
|
|
||||
|
impl TradeItem {
|
||||
|
pub fn stacked(&self) -> Option<(ClientItemId, usize)> {
|
||||
|
match self {
|
||||
|
TradeItem::Stacked(item_id, amount) => Some((*item_id, *amount)),
|
||||
|
_ => None
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn stacked_mut(&mut self) -> Option<(ClientItemId, &mut usize)> {
|
||||
|
match self {
|
||||
|
TradeItem::Stacked(item_id, ref mut amount) => Some((*item_id, amount)),
|
||||
|
_ => None
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn item_id(&self) -> ClientItemId {
|
||||
|
match self {
|
||||
|
TradeItem::Individual(item_id) => *item_id,
|
||||
|
TradeItem::Stacked(item_id, _) => *item_id,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn amount(&self) -> usize {
|
||||
|
match self {
|
||||
|
TradeItem::Individual(_) => 1,
|
||||
|
TradeItem::Stacked(_, amount) => *amount,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
@ -0,0 +1,12 @@ |
|||||
|
[package] |
||||
|
name = "location" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
[dependencies] |
||||
|
networking = { workspace = true } |
||||
|
|
||||
|
async-std = { workspace = true } |
||||
|
derive_more = { workspace = true } |
||||
|
futures= { workspace = true } |
||||
|
thiserror = { workspace = true } |
@ -0,0 +1,14 @@ |
|||||
|
[package] |
||||
|
name = "maps" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
[dependencies] |
||||
|
byteorder = { workspace = true } |
||||
|
serde = { workspace = true } |
||||
|
thiserror = { workspace = true } |
||||
|
rand = { workspace = true } |
||||
|
rand_chacha = { workspace = true } |
||||
|
toml = { workspace = true } |
||||
|
enum-utils = { workspace = true } |
||||
|
derive_more = { workspace = true } |
@ -0,0 +1,59 @@ |
|||||
|
pub mod area;
|
||||
|
pub mod enemy;
|
||||
|
pub mod object;
|
||||
|
pub mod variant;
|
||||
|
pub mod maps;
|
||||
|
pub mod monster;
|
||||
|
pub mod room;
|
||||
|
|
||||
|
#[derive(Clone, Copy)]
|
||||
|
pub enum Holiday {
|
||||
|
None,
|
||||
|
Christmas,
|
||||
|
Valentines,
|
||||
|
Easter,
|
||||
|
Halloween,
|
||||
|
Sonic,
|
||||
|
NewYear,
|
||||
|
Summer,
|
||||
|
White,
|
||||
|
Wedding,
|
||||
|
Fall,
|
||||
|
Spring,
|
||||
|
Summer2,
|
||||
|
Spring2,
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
impl From<Holiday> for u32 {
|
||||
|
fn from(other: Holiday) -> u32 {
|
||||
|
u16::from(other) as u32
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl From<Holiday> for u16 {
|
||||
|
fn from(other: Holiday) -> u16 {
|
||||
|
u8::from(other) as u16
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl From<Holiday> for u8 {
|
||||
|
fn from(other: Holiday) -> u8 {
|
||||
|
match other {
|
||||
|
Holiday::None => 0,
|
||||
|
Holiday::Christmas => 1,
|
||||
|
Holiday::Valentines => 3,
|
||||
|
Holiday::Easter => 4,
|
||||
|
Holiday::Halloween => 5,
|
||||
|
Holiday::Sonic => 6,
|
||||
|
Holiday::NewYear => 7,
|
||||
|
Holiday::Summer => 8,
|
||||
|
Holiday::White => 9,
|
||||
|
Holiday::Wedding => 10,
|
||||
|
Holiday::Fall => 11,
|
||||
|
Holiday::Spring => 12,
|
||||
|
Holiday::Summer2 => 13,
|
||||
|
Holiday::Spring2 => 14,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
@ -1,13 +1,11 @@ |
|||||
// TOOD: `pub(super) for most of these?`
|
// TOOD: `pub(super) for most of these?`
|
||||
|
|
||||
use std::io::{Read};
|
|
||||
|
use std::io::Read;
|
||||
|
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
|
|
||||
use crate::ship::room::Episode;
|
|
||||
|
|
||||
// TODO: don't use *
|
|
||||
use crate::ship::map::*;
|
|
||||
|
use crate::room::Episode;
|
||||
|
use crate::area::MapArea;
|
||||
|
|
||||
|
|
||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
@ -0,0 +1,150 @@ |
|||||
|
#[derive(Debug, Copy, Clone, derive_more::Display)]
|
||||
|
pub enum Episode {
|
||||
|
#[display(fmt="ep1")]
|
||||
|
One,
|
||||
|
#[display(fmt="ep2")]
|
||||
|
Two,
|
||||
|
#[display(fmt="ep4")]
|
||||
|
Four,
|
||||
|
}
|
||||
|
|
||||
|
impl TryFrom<u8> for Episode {
|
||||
|
type Error = ();
|
||||
|
|
||||
|
fn try_from(value: u8) -> Result<Episode, ()> {
|
||||
|
match value {
|
||||
|
1 => Ok(Episode::One),
|
||||
|
2 => Ok(Episode::Two),
|
||||
|
3 => Ok(Episode::Four),
|
||||
|
_ => Err(())
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl From<Episode> for u8 {
|
||||
|
fn from(other: Episode) -> u8 {
|
||||
|
match other {
|
||||
|
Episode::One => 1,
|
||||
|
Episode::Two => 2,
|
||||
|
Episode::Four => 3,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl Episode {
|
||||
|
pub fn from_quest(value: u8) -> Option<Episode> {
|
||||
|
match value {
|
||||
|
0 => Some(Episode::One),
|
||||
|
1 => Some(Episode::Two),
|
||||
|
2 => Some(Episode::Four),
|
||||
|
_ => None,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)]
|
||||
|
pub enum Difficulty {
|
||||
|
Normal,
|
||||
|
Hard,
|
||||
|
VeryHard,
|
||||
|
Ultimate,
|
||||
|
}
|
||||
|
|
||||
|
impl TryFrom<u8> for Difficulty {
|
||||
|
type Error = ();
|
||||
|
|
||||
|
fn try_from(value: u8) -> Result<Difficulty, ()> {
|
||||
|
match value {
|
||||
|
0 => Ok(Difficulty::Normal),
|
||||
|
1 => Ok(Difficulty::Hard),
|
||||
|
2 => Ok(Difficulty::VeryHard),
|
||||
|
3 => Ok(Difficulty::Ultimate),
|
||||
|
_ => Err(())
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl From<Difficulty> for u8 {
|
||||
|
fn from(other: Difficulty) -> u8 {
|
||||
|
match other {
|
||||
|
Difficulty::Normal => 0,
|
||||
|
Difficulty::Hard => 1,
|
||||
|
Difficulty::VeryHard => 2,
|
||||
|
Difficulty::Ultimate => 3,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Debug, Copy, Clone)]
|
||||
|
pub enum PlayerMode {
|
||||
|
Single,
|
||||
|
Multi,
|
||||
|
}
|
||||
|
|
||||
|
impl PlayerMode {
|
||||
|
pub fn value(&self) -> u8 {
|
||||
|
match self {
|
||||
|
PlayerMode::Single => 1,
|
||||
|
PlayerMode::Multi => 0,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Debug, Copy, Clone, derive_more::Display)]
|
||||
|
pub enum RoomMode {
|
||||
|
#[display(fmt="single")]
|
||||
|
Single {
|
||||
|
episode: Episode,
|
||||
|
difficulty: Difficulty,
|
||||
|
},
|
||||
|
#[display(fmt="multi")]
|
||||
|
Multi {
|
||||
|
episode: Episode,
|
||||
|
difficulty: Difficulty,
|
||||
|
},
|
||||
|
#[display(fmt="challenge")]
|
||||
|
Challenge {
|
||||
|
episode: Episode,
|
||||
|
},
|
||||
|
#[display(fmt="battle")]
|
||||
|
Battle {
|
||||
|
episode: Episode,
|
||||
|
difficulty: Difficulty,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
impl RoomMode {
|
||||
|
pub fn difficulty(&self) -> Difficulty {
|
||||
|
match self {
|
||||
|
RoomMode::Single {difficulty, ..} => *difficulty,
|
||||
|
RoomMode::Multi {difficulty, ..} => *difficulty,
|
||||
|
RoomMode::Battle {difficulty, ..} => *difficulty,
|
||||
|
RoomMode::Challenge {..} => Difficulty::Normal,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn episode(&self) -> Episode {
|
||||
|
match self {
|
||||
|
RoomMode::Single {episode, ..} => *episode,
|
||||
|
RoomMode::Multi {episode, ..} => *episode,
|
||||
|
RoomMode::Battle {episode, ..} => *episode,
|
||||
|
RoomMode::Challenge {episode, ..} => *episode,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn battle(&self) -> bool {
|
||||
|
matches!(self, RoomMode::Battle {..})
|
||||
|
}
|
||||
|
|
||||
|
pub fn challenge(&self) -> bool {
|
||||
|
matches!(self, RoomMode::Challenge {..})
|
||||
|
}
|
||||
|
|
||||
|
pub fn player_mode(&self) -> PlayerMode {
|
||||
|
match self {
|
||||
|
RoomMode::Single {..} => PlayerMode::Single,
|
||||
|
_ => PlayerMode::Multi,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,18 @@ |
|||||
|
[package] |
||||
|
name = "networking" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
|
||||
|
[dependencies] |
||||
|
entity = { workspace = true } |
||||
|
|
||||
|
libpso = { workspace = true } |
||||
|
|
||||
|
async-std = { workspace = true } |
||||
|
async-trait = { workspace = true } |
||||
|
futures = { workspace = true } |
||||
|
log = { workspace = true } |
||||
|
serde = { workspace = true } |
||||
|
serde_json = { workspace = true } |
||||
|
derive_more = { workspace = true } |
@ -1,7 +1,6 @@ |
|||||
pub mod cipherkeys;
|
pub mod cipherkeys;
|
||||
pub mod serverstate;
|
pub mod serverstate;
|
||||
pub mod mainloop;
|
pub mod mainloop;
|
||||
pub mod leveltable;
|
|
||||
pub mod interserver;
|
pub mod interserver;
|
||||
|
|
||||
// https://www.reddit.com/r/rust/comments/33xhhu/how_to_create_an_array_of_structs_that_havent/
|
// https://www.reddit.com/r/rust/comments/33xhhu/how_to_create_an_array_of_structs_that_havent/
|
@ -0,0 +1,22 @@ |
|||||
|
[package] |
||||
|
name = "pktbuilder" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
[dependencies] |
||||
|
quests = { workspace = true } |
||||
|
stats = { workspace = true } |
||||
|
location = { workspace = true } |
||||
|
client = { workspace = true } |
||||
|
items = { workspace = true } |
||||
|
networking = { workspace = true } |
||||
|
maps = { workspace = true } |
||||
|
room = { workspace = true } |
||||
|
shops = { workspace = true } |
||||
|
entity = { workspace = true } |
||||
|
|
||||
|
libpso = { workspace = true } |
||||
|
|
||||
|
anyhow = { workspace = true } |
||||
|
futures = { workspace = true } |
||||
|
thiserror = { workspace = true } |
@ -1,10 +1,9 @@ |
|||||
use libpso::character::character;
|
use libpso::character::character;
|
||||
use crate::common::leveltable::CharacterStats;
|
|
||||
use crate::entity::character::CharacterEntity;
|
|
||||
//use crate::ship::items::{CharacterInventory, CharacterBank};
|
|
||||
use crate::ship::items::bank::BankState;
|
|
||||
use crate::ship::items::inventory::InventoryState;
|
|
||||
use crate::entity::item::Meseta;
|
|
||||
|
use stats::leveltable::CharacterStats;
|
||||
|
use entity::character::CharacterEntity;
|
||||
|
use items::bank::BankState;
|
||||
|
use items::inventory::InventoryState;
|
||||
|
use entity::item::Meseta;
|
||||
|
|
||||
|
|
||||
#[derive(Default)]
|
#[derive(Default)]
|
@ -1,8 +1,9 @@ |
|||||
use crate::ship::quests::{Quest, QuestList};
|
|
||||
use crate::ship::ship::{QUEST_CATEGORY_MENU_ID, QUEST_SELECT_MENU_ID};
|
|
||||
|
use quests::{Quest, QuestList};
|
||||
use libpso::packet::ship::*;
|
use libpso::packet::ship::*;
|
||||
use libpso::{utf8_to_array, utf8_to_utf16_array};
|
use libpso::{utf8_to_array, utf8_to_utf16_array};
|
||||
|
|
||||
|
pub const QUEST_CATEGORY_MENU_ID: u32 = 0xA2;
|
||||
|
pub const QUEST_SELECT_MENU_ID: u32 = 0xA3;
|
||||
|
|
||||
pub fn quest_category_list(quests: &QuestList) -> QuestCategoryList {
|
pub fn quest_category_list(quests: &QuestList) -> QuestCategoryList {
|
||||
let categories = quests.iter()
|
let categories = quests.iter()
|
@ -1,8 +1,9 @@ |
|||||
use libpso::packet::login::{ShipList, ShipListEntry};
|
use libpso::packet::login::{ShipList, ShipListEntry};
|
||||
use libpso::utf8_to_utf16_array;
|
use libpso::utf8_to_utf16_array;
|
||||
|
|
||||
use crate::common::interserver::Ship;
|
|
||||
use crate::login::character::SHIP_MENU_ID;
|
|
||||
|
use networking::interserver::Ship;
|
||||
|
|
||||
|
pub const SHIP_MENU_ID: u32 = 1;
|
||||
|
|
||||
pub fn ship_list(ships: &[Ship]) -> ShipList {
|
pub fn ship_list(ships: &[Ship]) -> ShipList {
|
||||
let ships = ships.iter()
|
let ships = ships.iter()
|
@ -0,0 +1,107 @@ |
|||||
|
use futures::stream::{FuturesOrdered, StreamExt};
|
||||
|
use libpso::packet::ship::*;
|
||||
|
use crate::common::serverstate::ClientId;
|
||||
|
use crate::entity::gateway::EntityGateway;
|
||||
|
use crate::ship::client::{Clients, ClientState};
|
||||
|
use crate::ship::teams::Teams;
|
||||
|
use crate::ship::location::ClientLocation;
|
||||
|
use crate::ship::ship::ShipError;
|
||||
|
use crate::entity::team::TeamEntity;
|
||||
|
|
||||
|
|
||||
|
pub fn client_team_state_changed(client_id: ClientId, client: &ClientState, team: &TeamEntity) -> ClientTeamStateChanged {
|
||||
|
ClientTeamStateChanged {
|
||||
|
unknown: 0,
|
||||
|
guildcard: client.user.guildcard(),
|
||||
|
team_id: team.id.0,
|
||||
|
unknown2: [0;2],
|
||||
|
privilege: 0x40, // TODO: improve
|
||||
|
team_name: libpso::utf8_to_utf16_array!(team.name, 14),
|
||||
|
unknown3: 0x00986C84, // TODO: what if we omit this?
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn player_team_info(client_id: ClientId, client: &ClientState, team: &TeamEntity) -> PlayerTeamInfo {
|
||||
|
PlayerTeamInfo {
|
||||
|
guildcard: client.user.guildcard(),
|
||||
|
team_id: team.id.0,
|
||||
|
info: 0,
|
||||
|
info2: 0,
|
||||
|
privilege: 0x40, // TODO: improve
|
||||
|
team_name: libpso::utf8_to_utf16_array!(team.name, 14),
|
||||
|
unknown: 0x00986C84, // TODO: what if we omit this?
|
||||
|
guildcard_again: client.user.guildcard(),
|
||||
|
client_id: client_id.0 as u32,
|
||||
|
character_name: libpso::utf8_to_utf16_array!(client.character.name, 12),
|
||||
|
unknown2: 0,
|
||||
|
unknown3: 0,
|
||||
|
team_flag: team.team_flag,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn team_info_individual(client_id: ClientId, client: &ClientState, team: &TeamEntity) -> TeamInfo {
|
||||
|
TeamInfo {
|
||||
|
clients: vec![player_team_info(client_id, client, team)]
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
pub async fn player_team_info_list<EG>(id: ClientId,
|
||||
|
client_location: &ClientLocation,
|
||||
|
clients: &Clients,
|
||||
|
teams: &Teams<EG>,
|
||||
|
) -> Result<Vec<PlayerTeamInfo>, ShipError>
|
||||
|
where
|
||||
|
EG: EntityGateway + Clone + 'static,
|
||||
|
{
|
||||
|
Ok(futures::stream::iter(client_location.get_all_clients_by_client(id).await?.into_iter())
|
||||
|
.filter_map(|area_client| {
|
||||
|
let clients = clients.clone();
|
||||
|
async move {
|
||||
|
clients.with(area_client.client, |client| {
|
||||
|
let mut teams = teams.clone();
|
||||
|
Box::pin(async move {
|
||||
|
let team = teams.get_team(area_client.client).await.ok()??;
|
||||
|
Some(player_team_info(area_client.client, client, &team))
|
||||
|
})}).await.ok()?
|
||||
|
}})
|
||||
|
.collect::<Vec<_>>()
|
||||
|
.await)
|
||||
|
}
|
||||
|
|
||||
|
pub async fn team_info<EG>(id: ClientId,
|
||||
|
client_location: &ClientLocation,
|
||||
|
clients: &Clients,
|
||||
|
teams: &Teams<EG>,
|
||||
|
) -> Result<TeamInfo, ShipError>
|
||||
|
where
|
||||
|
EG: EntityGateway + Clone + 'static,
|
||||
|
{
|
||||
|
Ok(TeamInfo {
|
||||
|
clients: player_team_info_list(id, client_location, clients, teams).await?,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
pub async fn lobby_team_list<EG>(id: ClientId,
|
||||
|
client_location: &ClientLocation,
|
||||
|
clients: &Clients,
|
||||
|
teams: &Teams<EG>,
|
||||
|
) -> Result<TeamLobbyList, ShipError>
|
||||
|
where
|
||||
|
EG: EntityGateway + Clone + 'static,
|
||||
|
{
|
||||
|
Ok(TeamLobbyList {
|
||||
|
clients: player_team_info_list(id, client_location, clients, teams).await?,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
pub fn team_invitation_info(client_id: ClientId, client: &ClientState, team: &TeamEntity) -> TeamInvitationInfo {
|
||||
|
TeamInvitationInfo {
|
||||
|
guildcard: client.user.guildcard(),
|
||||
|
team_id: team.id.0,
|
||||
|
unknown: [0; 2],
|
||||
|
team_name: libpso::utf8_to_utf16_array!(team.name, 14),
|
||||
|
unknown2: 0x00986C84, // TODO: what if we omit this?
|
||||
|
team_flag: team.team_flag,
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,18 @@ |
|||||
|
[package] |
||||
|
name = "quests" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
[dependencies] |
||||
|
maps = { workspace = true } |
||||
|
|
||||
|
libpso = { workspace = true } |
||||
|
|
||||
|
async-std = { workspace = true } |
||||
|
ages-prs = { workspace = true } |
||||
|
log = { workspace = true } |
||||
|
byteorder = { workspace = true } |
||||
|
thiserror = { workspace = true } |
||||
|
anyhow = { workspace = true } |
||||
|
toml = { workspace = true } |
||||
|
serde = { workspace = true } |
@ -0,0 +1,17 @@ |
|||||
|
[package] |
||||
|
name = "room" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
[dependencies] |
||||
|
maps = { workspace = true } |
||||
|
entity = { workspace = true } |
||||
|
quests = { workspace = true } |
||||
|
location = { workspace = true } |
||||
|
drops = { workspace = true } |
||||
|
|
||||
|
rand = { workspace = true } |
||||
|
async-std = { workspace = true } |
||||
|
futures = { workspace = true } |
||||
|
anyhow = { workspace = true } |
||||
|
thiserror = { workspace = true } |
@ -0,0 +1,17 @@ |
|||||
|
[package] |
||||
|
name = "shops" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
[dependencies] |
||||
|
maps = { workspace = true } |
||||
|
stats = { workspace = true } |
||||
|
entity = { workspace = true } |
||||
|
|
||||
|
async-std = { workspace = true } |
||||
|
async-trait = { workspace = true } |
||||
|
futures = { workspace = true } |
||||
|
rand = { workspace = true } |
||||
|
rand_chacha = { workspace = true } |
||||
|
toml = { workspace = true } |
||||
|
serde = { workspace = true } |
@ -0,0 +1,95 @@ |
|||||
|
mod weapon;
|
||||
|
mod tool;
|
||||
|
mod armor;
|
||||
|
|
||||
|
use async_std::sync::{Arc, Mutex};
|
||||
|
use futures::future::OptionFuture;
|
||||
|
use std::collections::HashMap;
|
||||
|
use entity::item::ItemDetail;
|
||||
|
use maps::room::Difficulty;
|
||||
|
use entity::character::SectionID;
|
||||
|
|
||||
|
pub use weapon::{WeaponShop, WeaponShopItem};
|
||||
|
pub use tool::{ToolShop, ToolShopItem};
|
||||
|
pub use armor::{ArmorShop, ArmorShopItem};
|
||||
|
|
||||
|
pub trait ShopItem {
|
||||
|
fn price(&self) -> usize;
|
||||
|
fn as_bytes(&self) -> [u8; 12];
|
||||
|
fn as_item(&self) -> ItemDetail;
|
||||
|
}
|
||||
|
|
||||
|
#[async_trait::async_trait]
|
||||
|
pub trait ItemShops {
|
||||
|
async fn generate_weapon_list(&self, difficulty: Difficulty, section_id: SectionID, char_level: usize) -> Option<Vec<WeaponShopItem>>;
|
||||
|
async fn generate_tool_list(&self, char_level: usize) -> Vec<ToolShopItem>;
|
||||
|
async fn generate_armor_list(&self, char_level: usize) -> Vec<ArmorShopItem>;
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
#[derive(Clone)]
|
||||
|
pub struct StandardItemShops {
|
||||
|
weapon_shop: HashMap<(Difficulty, SectionID), Arc<Mutex<WeaponShop<rand_chacha::ChaCha20Rng>>>>,
|
||||
|
tool_shop: Arc<Mutex<ToolShop<rand_chacha::ChaCha20Rng>>>,
|
||||
|
armor_shop: Arc<Mutex<ArmorShop<rand_chacha::ChaCha20Rng>>>,
|
||||
|
}
|
||||
|
|
||||
|
impl Default for StandardItemShops {
|
||||
|
fn default() -> StandardItemShops {
|
||||
|
let difficulty = [Difficulty::Normal, Difficulty::Hard, Difficulty::VeryHard, Difficulty::Ultimate];
|
||||
|
let section_id = [SectionID::Viridia, SectionID::Greenill, SectionID::Skyly, SectionID::Bluefull, SectionID::Purplenum,
|
||||
|
SectionID::Pinkal, SectionID::Redria, SectionID::Oran, SectionID::Yellowboze, SectionID::Whitill];
|
||||
|
|
||||
|
let mut weapon_shop = HashMap::new();
|
||||
|
for d in difficulty.iter() {
|
||||
|
for id in section_id.iter() {
|
||||
|
weapon_shop.insert((*d, *id), Arc::new(Mutex::new(WeaponShop::new(*d, *id))));
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
StandardItemShops {
|
||||
|
weapon_shop,
|
||||
|
tool_shop: Arc::new(Mutex::new(ToolShop::default())),
|
||||
|
armor_shop: Arc::new(Mutex::new(ArmorShop::default())),
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[async_trait::async_trait]
|
||||
|
impl ItemShops for StandardItemShops {
|
||||
|
async fn generate_weapon_list(&self, difficulty: Difficulty, section_id: SectionID, char_level: usize) -> Option<Vec<WeaponShopItem>> {
|
||||
|
OptionFuture::from(
|
||||
|
self.weapon_shop
|
||||
|
.get(&(difficulty, section_id))
|
||||
|
.map(|shop| async {
|
||||
|
shop
|
||||
|
.lock()
|
||||
|
.await
|
||||
|
.generate_weapon_list(char_level)
|
||||
|
})).await
|
||||
|
}
|
||||
|
|
||||
|
async fn generate_tool_list(&self, char_level: usize) -> Vec<ToolShopItem> {
|
||||
|
self.tool_shop
|
||||
|
.lock()
|
||||
|
.await
|
||||
|
.generate_tool_list(char_level)
|
||||
|
}
|
||||
|
|
||||
|
async fn generate_armor_list(&self, char_level: usize) -> Vec<ArmorShopItem> {
|
||||
|
self.armor_shop
|
||||
|
.lock()
|
||||
|
.await
|
||||
|
.generate_armor_list(char_level)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
pub enum ShopType {
|
||||
|
Weapon,
|
||||
|
Tool,
|
||||
|
Armor
|
||||
|
}
|
||||
|
|
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue