Browse Source

break out the rest of everything into its own crate

pull/141/head
jake 6 months ago
parent
commit
d73a07391b
  1. 24
      Cargo.toml
  2. 20
      client/Cargo.toml
  3. 18
      client/src/client.rs
  4. 3
      client/src/lib.rs
  5. 17
      drops/Cargo.toml
  6. 14
      drops/src/box_drop_table.rs
  7. 2
      drops/src/generic_armor.rs
  8. 2
      drops/src/generic_shield.rs
  9. 2
      drops/src/generic_unit.rs
  10. 2
      drops/src/generic_weapon.rs
  11. 15
      drops/src/lib.rs
  12. 8
      drops/src/rare_drop_table.rs
  13. 2
      drops/src/tech_table.rs
  14. 4
      drops/src/tool_table.rs
  15. 25
      items/Cargo.toml
  16. 36
      items/src/actions.rs
  17. 4
      items/src/apply_item.rs
  18. 8
      items/src/bank.rs
  19. 8
      items/src/floor.rs
  20. 8
      items/src/inventory.rs
  21. 0
      items/src/itemstateaction.rs
  22. 3
      items/src/lib.rs
  23. 18
      items/src/manager.rs
  24. 14
      items/src/state.rs
  25. 21
      items/src/tasks.rs
  26. 38
      items/src/trade.rs
  27. 12
      location/Cargo.toml
  28. 3
      location/src/lib.rs
  29. 2
      location/src/location.rs
  30. 18
      maps/src/enemy.rs
  31. 52
      maps/src/lib.rs
  32. 7
      maps/src/maps.rs
  33. 14
      networking/Cargo.toml
  34. 0
      networking/src/cipherkeys.rs
  35. 0
      networking/src/interserver.rs
  36. 18
      networking/src/lib.rs
  37. 6
      networking/src/mainloop/client.rs
  38. 9
      networking/src/mainloop/interserver.rs
  39. 0
      networking/src/mainloop/mod.rs
  40. 0
      networking/src/serverstate.rs
  41. 22
      pktbuilder/Cargo.toml
  42. 5
      pktbuilder/src/character.rs
  43. 9
      pktbuilder/src/lib.rs
  44. 15
      pktbuilder/src/lobby.rs
  45. 100
      pktbuilder/src/message.rs
  46. 5
      pktbuilder/src/quest.rs
  47. 21
      pktbuilder/src/room.rs
  48. 5
      pktbuilder/src/ship.rs
  49. 107
      pktbuilder/src/team.rs
  50. 0
      pktbuilder/src/trade.rs
  51. 18
      quests/Cargo.toml
  52. 4
      quests/src/lib.rs
  53. 0
      quests/src/quests.rs
  54. 17
      room/Cargo.toml
  55. 3
      room/src/lib.rs
  56. 33
      room/src/room.rs
  57. 8
      src/bin/login.rs
  58. 30
      src/bin/main.rs
  59. 4
      src/bin/patch.rs
  60. 8
      src/bin/ship.rs
  61. 18
      src/common/mod.rs
  62. 3
      src/lib.rs
  63. 11
      src/login/character.rs
  64. 4
      src/login/login.rs
  65. 4
      src/patch/patch.rs
  66. 8
      src/ship/chatcommand.rs
  67. 0
      src/ship/drops/drop_table.rs
  68. 16
      src/ship/mod.rs
  69. 212
      src/ship/monster.rs
  70. 6
      src/ship/packet/handler/auth.rs
  71. 4
      src/ship/packet/handler/communication.rs
  72. 38
      src/ship/packet/handler/direct_message.rs
  73. 30
      src/ship/packet/handler/lobby.rs
  74. 30
      src/ship/packet/handler/message.rs
  75. 16
      src/ship/packet/handler/quest.rs
  76. 23
      src/ship/packet/handler/room.rs
  77. 2
      src/ship/packet/handler/settings.rs
  78. 6
      src/ship/packet/handler/ship.rs
  79. 28
      src/ship/packet/handler/trade.rs
  80. 2
      src/ship/packet/mod.rs
  81. 105
      src/ship/ship.rs
  82. 4
      tests/common.rs
  83. 2
      tests/test_bank.rs
  84. 2
      tests/test_character.rs
  85. 5
      tests/test_exp_gain.rs
  86. 2
      tests/test_item_actions.rs
  87. 12
      tests/test_item_drop.rs
  88. 4
      tests/test_item_id.rs
  89. 2
      tests/test_item_pickup.rs
  90. 3
      tests/test_item_use.rs
  91. 2
      tests/test_mags.rs
  92. 4
      tests/test_rooms.rs
  93. 6
      tests/test_shops.rs
  94. 2
      tests/test_trade.rs
  95. 14
      trade/Cargo.toml
  96. 4
      trade/src/lib.rs
  97. 42
      trade/src/trade.rs

24
Cargo.toml

@ -6,11 +6,19 @@ edition = "2021"
[workspace]
members = [
"client",
"drops",
"entity",
"items",
"location",
"maps",
"networking",
"pktbuilder",
"quests",
"room",
"shops",
"stats",
"trade",
]
[workspace.dependencies]
@ -19,6 +27,14 @@ maps = { path = "./maps" }
networking = { path = "./networking" }
shops = { path = "./shops" }
stats = { path = "./stats" }
items = { path = "./items" }
pktbuilder = { path = "./pktbuilder" }
quests = { path = "./quests" }
location = { path = "./location" }
client = { path = "./client" }
drops = { path = "./drops" }
trade = { path = "./trade" }
room = { path = "./room" }
libpso = { git = "http://git.sharnoth.com/jake/libpso" }
@ -56,6 +72,14 @@ maps = { workspace = true }
networking = { workspace = true }
shops = { workspace = true }
stats = { workspace = true }
items = { workspace = true }
pktbuilder = { workspace = true }
quests = { workspace = true }
location = { workspace = true }
client = { workspace = true }
drops = { workspace = true }
trade = { workspace = true }
room = { workspace = true }
libpso = { workspace = true }

20
client/Cargo.toml

@ -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 }

18
src/ship/client.rs → client/src/client.rs

@ -6,17 +6,23 @@ use futures::future::BoxFuture;
use libpso::packet::ship::*;
use libpso::packet::login::Session;
use crate::common::serverstate::ClientId;
use networking::serverstate::ClientId;
use entity::account::{UserAccountEntity, UserSettingsEntity};
use entity::character::CharacterEntity;
use entity::item;
use crate::ship::ship::ShipError;
use crate::ship::items;
use items;
use maps::area::MapArea;
use shops::{WeaponShopItem, ToolShopItem, ArmorShopItem};
#[derive(thiserror::Error, Debug)]
pub enum ClientError {
#[error("not found {0}")]
NotFound(ClientId),
}
#[derive(Clone, Default)]
pub struct Clients(Arc<RwLock<HashMap<ClientId, RwLock<ClientState>>>>);
@ -46,7 +52,7 @@ impl Clients {
.await;
let client = clients
.get(&client_id)
.ok_or_else(|| ShipError::ClientNotFound(client_id))?
.ok_or_else(|| ClientError::NotFound(client_id))?
.read()
.await;
@ -69,7 +75,7 @@ impl Clients {
for (cindex, client_id) in client_ids.iter().enumerate() {
let c = clients
.get(client_id)
.ok_or_else(|| ShipError::ClientNotFound(*client_id))?
.ok_or_else(|| ClientError::NotFound(*client_id))?
.read()
.await;
client_states[cindex].write(c);
@ -95,7 +101,7 @@ impl Clients {
.await;
let mut client = clients
.get(&client_id)
.ok_or_else(|| ShipError::ClientNotFound(client_id))?
.ok_or_else(|| ClientError::NotFound(client_id))?
.write()
.await;

3
client/src/lib.rs

@ -0,0 +1,3 @@
pub mod client;
pub use client::*;

17
drops/Cargo.toml

@ -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 }

14
src/ship/drops/box_drop_table.rs → drops/src/box_drop_table.rs

@ -5,14 +5,14 @@ use serde::{Serialize, Deserialize};
use entity::character::SectionID;
use maps::room::{Difficulty, Episode};
use maps::area::MapArea;
use crate::ship::drops::{ItemDropType, load_data_file};
use crate::{ItemDropType, load_data_file};
use maps::object::{MapObject, MapObjectType, FixedBoxDropType};
use crate::ship::drops::rare_drop_table::{RareDropTable, RareDropItem};
use crate::ship::drops::generic_weapon::GenericWeaponTable;
use crate::ship::drops::generic_armor::GenericArmorTable;
use crate::ship::drops::generic_shield::GenericShieldTable;
use crate::ship::drops::generic_unit::GenericUnitTable;
use crate::ship::drops::tool_table::ToolTable;
use crate::rare_drop_table::{RareDropTable, RareDropItem};
use crate::generic_weapon::GenericWeaponTable;
use crate::generic_armor::GenericArmorTable;
use crate::generic_shield::GenericShieldTable;
use crate::generic_unit::GenericUnitTable;
use crate::tool_table::ToolTable;
#[derive(Debug, Serialize, Deserialize)]
struct BoxDropRate {

2
src/ship/drops/generic_armor.rs → drops/src/generic_armor.rs

@ -7,7 +7,7 @@ use entity::character::SectionID;
use entity::item::armor::{ArmorType, Armor};
use maps::room::{Difficulty, Episode};
use maps::area::MapArea;
use crate::ship::drops::{ItemDropType, load_data_file};
use crate::{ItemDropType, load_data_file};
use stats::items::{armor_stats, ArmorStats};

2
src/ship/drops/generic_shield.rs → drops/src/generic_shield.rs

@ -7,7 +7,7 @@ use entity::item::shield::{ShieldType, Shield};
use entity::character::SectionID;
use maps::room::{Difficulty, Episode};
use maps::area::MapArea;
use crate::ship::drops::{ItemDropType, load_data_file};
use crate::{ItemDropType, load_data_file};
use stats::items::{shield_stats, ShieldStats};

2
src/ship/drops/generic_unit.rs → drops/src/generic_unit.rs

@ -7,7 +7,7 @@ use entity::character::SectionID;
use entity::item::unit::{UnitType, Unit, UnitModifier};
use maps::room::{Difficulty, Episode};
use maps::area::MapArea;
use crate::ship::drops::{ItemDropType, load_data_file};
use crate::{ItemDropType, load_data_file};
use stats::items::{unit_stats, UnitStats};

2
src/ship/drops/generic_weapon.rs → drops/src/generic_weapon.rs

@ -8,7 +8,7 @@ use entity::character::SectionID;
use entity::item::weapon::{Weapon, WeaponType, Attribute, WeaponAttribute, WeaponSpecial};
use maps::room::{Difficulty, Episode};
use maps::area::MapArea;
use crate::ship::drops::{ItemDropType, load_data_file};
use crate::{ItemDropType, load_data_file};

15
src/ship/drops/mod.rs → drops/src/lib.rs

@ -5,7 +5,6 @@
// to their drops
mod drop_table;
pub mod rare_drop_table;
mod generic_weapon;
mod generic_armor;
@ -26,13 +25,13 @@ use maps::monster::MonsterType;
use maps::room::{Difficulty, Episode};
use maps::area::MapArea;
use entity::character::SectionID;
use crate::ship::drops::generic_weapon::GenericWeaponTable;
use crate::ship::drops::generic_armor::GenericArmorTable;
use crate::ship::drops::generic_shield::GenericShieldTable;
use crate::ship::drops::generic_unit::GenericUnitTable;
use crate::ship::drops::tool_table::ToolTable;
use crate::ship::drops::rare_drop_table::RareDropTable;
use crate::ship::drops::box_drop_table::BoxDropTable;
use crate::generic_weapon::GenericWeaponTable;
use crate::generic_armor::GenericArmorTable;
use crate::generic_shield::GenericShieldTable;
use crate::generic_unit::GenericUnitTable;
use crate::tool_table::ToolTable;
use crate::rare_drop_table::RareDropTable;
use crate::box_drop_table::BoxDropTable;
use maps::object::MapObject;
use entity::item::{ItemType, weapon, armor, shield, unit, mag, tool, tech, esweapon};

8
src/ship/drops/rare_drop_table.rs → drops/src/rare_drop_table.rs

@ -11,10 +11,10 @@ use entity::character::SectionID;
use maps::monster::MonsterType;
use maps::room::{Difficulty, Episode};
use maps::area::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 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>>;

2
src/ship/drops/tech_table.rs → drops/src/tech_table.rs

@ -7,7 +7,7 @@ use entity::item::tech::{Technique, TechniqueDisk};
use maps::room::{Difficulty, Episode};
use maps::area::MapArea;
use entity::character::SectionID;
use crate::ship::drops::{ItemDropType, load_data_file};
use crate::{ItemDropType, load_data_file};

4
src/ship/drops/tool_table.rs → drops/src/tool_table.rs

@ -7,8 +7,8 @@ use entity::item::tool::{Tool, ToolType};
use maps::room::{Difficulty, Episode};
use maps::area::MapArea;
use entity::character::SectionID;
use crate::ship::drops::{ItemDropType, load_data_file};
use crate::ship::drops::tech_table::TechniqueTable;
use crate::{ItemDropType, load_data_file};
use crate::tech_table::TechniqueTable;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, enum_utils::FromStr)]

25
items/Cargo.toml

@ -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 }

36
src/ship/items/actions.rs → items/src/actions.rs

@ -1,5 +1,5 @@
// TODO: replace various u32s and usizes denoting item amounts for ItemAmount(u32) for consistency
use crate::ship::items::ClientItemId;
use crate::ClientItemId;
use entity::item::{Meseta, ItemNote};
use async_std::sync::Arc;
use std::future::Future;
@ -15,16 +15,15 @@ use entity::item::{ItemDetail, NewItemEntity, TradeId, ItemModifier};
use entity::item::tool::Tool;
use entity::room::RoomEntityId;
use maps::area::MapArea;
use crate::ship::ship::SendShipPacket;
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::state::{ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail};
use crate::bank::{BankItem, BankItemDetail};
use crate::inventory::{InventoryItem, InventoryItemDetail};
use crate::floor::{FloorItem, FloorItemDetail};
use crate::apply_item::{apply_item, ApplyItemAction};
use shops::ShopItem;
use crate::ship::drops::{ItemDrop, ItemDropType};
use crate::ship::packet::builder;
use crate::ship::location::AreaClient;
use drops::{ItemDrop, ItemDropType};
//use crate::ship::packet::builder;
use location::AreaClient;
use maps::monster::MonsterType;
pub enum TriggerCreateItem {
@ -32,6 +31,12 @@ pub enum TriggerCreateItem {
No
}
#[derive(Clone)]
pub enum CreateItem {
Individual(AreaClient, ClientItemId, IndividualItemDetail),
Stacked(AreaClient, ClientItemId, Tool, usize),
}
pub(super) fn take_item_from_floor<'a, EG, TR>(
character_id: CharacterEntityId,
item_id: ClientItemId
@ -1144,7 +1149,7 @@ pub(super) fn apply_item_action_packets<'a, EG, TR>(
character_id: CharacterEntityId,
area_client: AreaClient,
) -> impl Fn((ItemStateProxy, TR), ApplyItemAction)
-> BoxFuture<'a, Result<((ItemStateProxy, TR), Vec<SendShipPacket>), anyhow::Error>>
-> BoxFuture<'a, Result<((ItemStateProxy, TR), Vec<CreateItem>), anyhow::Error>>
where
EG: EntityGateway,
TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
@ -1161,7 +1166,8 @@ where
let (inventory_item_detail, create_item) = if item_detail.is_stackable() {
let tool = item_detail.as_tool().ok_or_else(|| ItemStateError::NotATool(ClientItemId(0xFFFFFFFF)))?;
let create_item = builder::message::create_stacked_item(area_client, item_id, &tool, 1).map_err(|_err| ItemStateError::Dummy)?;
//let create_item = builder::message::create_stacked_item(area_client, item_id, &tool, 1).map_err(|_err| ItemStateError::Dummy)?;
let create_item = CreateItem::Stacked(area_client, item_id, tool.clone(), 1);
let item_detail = StackedItemDetail {
entity_ids: vec![new_item.id],
tool
@ -1173,7 +1179,8 @@ where
entity_id: new_item.id,
item: item_detail,
};
let create_item = builder::message::create_individual_item(area_client, item_id, &item_detail).map_err(|_err| ItemStateError::Dummy)?;
//let create_item = builder::message::create_individual_item(area_client, item_id, &item_detail).map_err(|_err| ItemStateError::Dummy)?;
let create_item = CreateItem::Individual(area_client, item_id, item_detail.clone());
(InventoryItemDetail::Individual(item_detail), create_item)
};
@ -1187,7 +1194,8 @@ where
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
item_state.set_inventory(inventory).await;
vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item)))]
//vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item)))]
vec![create_item]
}
else {
Vec::new()

4
src/ship/items/apply_item.rs → items/src/apply_item.rs

@ -11,8 +11,8 @@ use entity::item::tool::{Tool, ToolType};
use entity::item::tech::TechniqueDisk;
use entity::item::{ItemDetail, ItemEntityId};
use entity::item::weapon::WeaponModifier;
use crate::ship::items::state::ItemStateProxy;
use crate::ship::items::inventory::InventoryItemDetail;
use crate::state::ItemStateProxy;
use crate::inventory::InventoryItemDetail;
#[derive(Error, Debug)]

8
src/ship/items/bank.rs → items/src/bank.rs

@ -1,15 +1,15 @@
use std::cmp::Ordering;
use libpso::character::character;
use crate::ship::items::ClientItemId;
use crate::ClientItemId;
use entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, BankEntity, BankItemEntity};
use std::future::Future;
use async_std::sync::{Arc, Mutex};
use entity::character::CharacterEntityId;
use entity::item::BankIdentifier;
use crate::ship::items::state::ItemStateError;
use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult};
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
use crate::state::ItemStateError;
use crate::state::{IndividualItemDetail, StackedItemDetail, AddItemResult};
use crate::inventory::{InventoryItem, InventoryItemDetail};
#[derive(thiserror::Error, Debug)]

8
src/ship/items/floor.rs → items/src/floor.rs

@ -1,4 +1,4 @@
use crate::ship::items::ClientItemId;
use crate::ClientItemId;
use entity::item::{Meseta, ItemEntityId, ItemDetail};
use std::future::Future;
@ -6,9 +6,9 @@ use maps::area::MapArea;
use entity::character::CharacterEntityId;
use entity::item::mag::Mag;
use crate::ship::items::state::ItemStateError;
use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail};
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
use crate::state::ItemStateError;
use crate::state::{IndividualItemDetail, StackedItemDetail};
use crate::inventory::{InventoryItem, InventoryItemDetail};
pub enum FloorType {
Local,

8
src/ship/items/inventory.rs → items/src/inventory.rs

@ -1,6 +1,6 @@
use std::cmp::Ordering;
use libpso::character::character;
use crate::ship::items::ClientItemId;
use crate::ClientItemId;
use entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, EquippedEntity};
use std::future::Future;
use async_std::sync::{Arc, Mutex};
@ -10,9 +10,9 @@ use entity::item::tool::ToolType;
use entity::item::mag::Mag;
use entity::item::weapon::Weapon;
use 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 crate::state::ItemStateError;
use crate::state::{IndividualItemDetail, StackedItemDetail, AddItemResult};
use crate::floor::{FloorItem, FloorItemDetail};
#[derive(Clone, Debug)]
pub enum InventoryItemDetail {

0
src/ship/items/itemstateaction.rs → items/src/itemstateaction.rs

3
src/ship/items/mod.rs → items/src/lib.rs

@ -1,3 +1,5 @@
#![feature(extract_if)]
pub mod state;
pub mod actions;
pub mod apply_item;
@ -6,6 +8,7 @@ pub mod inventory;
pub mod floor;
pub mod bank;
pub mod tasks;
pub mod trade;
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)]
pub struct ClientItemId(pub u32);

18
src/ship/items/manager.rs → items/src/manager.rs

@ -1,4 +1,4 @@
use crate::ship::items::ClientItemId;
use crate::ClientItemId;
use std::collections::HashMap;
use std::cmp::Ordering;
use std::cell::RefCell;
@ -9,18 +9,18 @@ use crate::entity::item::{ItemDetail, ItemNote, BankName};
use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, BankItemEntity};
use crate::entity::item::tool::{Tool, ToolType};
use crate::entity::item::weapon;
use crate::ship::map::MapArea;
use maps::area::MapArea;
use crate::ship::ship::ItemDropLocation;
use crate::ship::trade::TradeItem;
use crate::ship::drops::{ItemDrop, ItemDropType};
use crate::ship::location::{AreaClient, RoomId};
use crate::ship::shops::ShopItem;
use drops::{ItemDrop, ItemDropType};
use location::{AreaClient, RoomId};
use shops::ShopItem;
use crate::ship::packet::handler::trade::{TradeError, OTHER_MESETA_ITEM_ID};
use crate::ship::items::bank::*;
use crate::ship::items::floor::*;
use crate::ship::items::inventory::*;
use crate::ship::items::transaction::{ItemTransaction, ItemAction, TransactionError, TransactionCommitError};
use crate::bank::*;
use crate::floor::*;
use crate::inventory::*;
use crate::transaction::{ItemTransaction, ItemAction, TransactionError, TransactionCommitError};
#[derive(PartialEq, Eq)]
pub enum FloorType {

14
src/ship/items/state.rs → items/src/state.rs

@ -10,12 +10,12 @@ use entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, Ba
use entity::item::tool::Tool;
use entity::item::weapon::Weapon;
use entity::item::mag::Mag;
use crate::ship::drops::ItemDrop;
use crate::ship::items::ClientItemId;
use crate::ship::items::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState};
use crate::ship::items::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType};
use crate::ship::items::bank::{Bank, BankState, BankItem, BankItemDetail, BankError};
use crate::ship::location::{AreaClient, RoomId};
use drops::ItemDrop;
use crate::ClientItemId;
use crate::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState};
use crate::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType};
use crate::bank::{Bank, BankState, BankItem, BankItemDetail, BankError};
use location::{AreaClient, RoomId};
#[derive(thiserror::Error, Debug)]
pub enum ItemStateError {
@ -50,7 +50,7 @@ pub enum ItemStateError {
#[error("stacked item")]
StackedItemError(Vec<ItemEntity>),
#[error("apply item {0}")]
ApplyItemError(#[from] crate::ship::items::apply_item::ApplyItemError),
ApplyItemError(#[from] crate::apply_item::ApplyItemError),
#[error("item is not a mag {0}")]
NotAMag(ClientItemId),
#[error("item is not mag food {0}")]

21
src/ship/items/tasks.rs → items/src/tasks.rs

@ -1,24 +1,23 @@
use futures::future::BoxFuture;
use crate::ship::items::ClientItemId;
use crate::ClientItemId;
use entity::item::Meseta;
use crate::ship::ship::SendShipPacket;
use maps::area::MapArea;
use entity::character::{CharacterEntity, CharacterEntityId};
use entity::gateway::{EntityGateway, EntityGatewayTransaction};
use entity::item::ItemModifier;
use 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::state::{ItemState, ItemStateProxy, IndividualItemDetail};
use crate::itemstateaction::{ItemStateAction, ItemAction};
use crate::inventory::InventoryItem;
use crate::floor::FloorItem;
use shops::ShopItem;
use crate::ship::trade::TradeItem;
use crate::ship::location::AreaClient;
use crate::ship::drops::ItemDrop;
use crate::trade::TradeItem;
use location::AreaClient;
use drops::ItemDrop;
use maps::monster::MonsterType;
use crate::ship::items::actions;
use crate::actions;
pub fn pick_up_item<'a, EG>(
item_state: &'a mut ItemState,
@ -278,7 +277,7 @@ pub fn use_item<'a, EG> (
area_client: AreaClient,
item_id: &'a ClientItemId,
amount: u32,
) -> BoxFuture<'a, Result<Vec<SendShipPacket>, anyhow::Error>>
) -> BoxFuture<'a, Result<Vec<actions::CreateItem>, anyhow::Error>>
where
EG: EntityGateway + 'static,
{

38
items/src/trade.rs

@ -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,
}
}
}

12
location/Cargo.toml

@ -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 }

3
location/src/lib.rs

@ -0,0 +1,3 @@
pub mod location;
pub use location::*;

2
src/ship/location.rs → location/src/location.rs

@ -2,7 +2,7 @@
use std::collections::HashMap;
use std::time::SystemTime;
use thiserror::Error;
use crate::common::serverstate::ClientId;
use networking::serverstate::ClientId;
use async_std::sync::{Arc, RwLock};
use futures::{stream, StreamExt};

18
maps/src/enemy.rs

@ -9,17 +9,11 @@ use thiserror::Error;
use rand::{Rng, SeedableRng};
use serde::{Serialize, Deserialize};
use crate::Holiday;
use crate::area::{MapArea, MapAreaError};
use crate::room::Episode;
use crate::monster::MonsterType;
#[derive(Clone, Copy)]
pub enum RareEnemyEvent {
Easter,
Halloween,
Christmas,
}
#[derive(Debug, Copy, Clone)]
pub struct RawMapEnemy {
id: u32,
@ -120,7 +114,7 @@ impl RareMonsterAppearTable {
rand_chacha::ChaChaRng::from_entropy().gen::<f32>() < *self.appear_rate.get(monster).unwrap_or(&0.0f32)
}
pub fn apply(&self, enemy: MapEnemy, event: Option<RareEnemyEvent>) -> MapEnemy {
pub fn apply(&self, enemy: MapEnemy, event: Holiday) -> MapEnemy {
if enemy.can_be_rare() && self.roll_is_rare(&enemy.monster) {
enemy.into_rare(event)
}
@ -362,12 +356,12 @@ impl MapEnemy {
guaranteed rare monsters don't count towards the limit
*/
#[must_use]
pub fn into_rare(self, event: Option<RareEnemyEvent>) -> MapEnemy {
pub fn into_rare(self, event: Holiday) -> MapEnemy {
match (self.monster, self.map_area.to_episode(), event) {
(MonsterType::RagRappy, Episode::One, _) => {MapEnemy {monster: MonsterType::AlRappy, shiny:true, ..self}},
(MonsterType::RagRappy, Episode::Two, Some(RareEnemyEvent::Easter)) => {MapEnemy {monster: MonsterType::EasterRappy, shiny:true, ..self}},
(MonsterType::RagRappy, Episode::Two, Some(RareEnemyEvent::Halloween)) => {MapEnemy {monster: MonsterType::HalloRappy, shiny:true, ..self}},
(MonsterType::RagRappy, Episode::Two, Some(RareEnemyEvent::Christmas)) => {MapEnemy {monster: MonsterType::StRappy, shiny:true, ..self}},
(MonsterType::RagRappy, Episode::Two, Holiday::Easter) => {MapEnemy {monster: MonsterType::EasterRappy, shiny:true, ..self}},
(MonsterType::RagRappy, Episode::Two, Holiday::Halloween) => {MapEnemy {monster: MonsterType::HalloRappy, shiny:true, ..self}},
(MonsterType::RagRappy, Episode::Two, Holiday::Christmas) => {MapEnemy {monster: MonsterType::StRappy, shiny:true, ..self}},
(MonsterType::RagRappy, Episode::Two, _) => {MapEnemy {monster: MonsterType::LoveRappy, shiny:true, ..self}},
(MonsterType::Hildebear, _, _) => {MapEnemy {monster: MonsterType::Hildeblue, shiny:true, ..self}},
(MonsterType::PoisonLily, _, _) => {MapEnemy {monster: MonsterType::NarLily, shiny:true, ..self}},

52
maps/src/lib.rs

@ -5,3 +5,55 @@ 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,
}
}
}

7
maps/src/maps.rs

@ -9,7 +9,8 @@ use thiserror::Error;
//use crate::ship::ship::ShipEvent;
use crate::area::MapArea;
use crate::enemy::{MapEnemy, RawMapEnemy, RareEnemyEvent, RareMonsterAppearTable};
use crate::Holiday;
use crate::enemy::{MapEnemy, RawMapEnemy, RareMonsterAppearTable};
use crate::monster::MonsterType;
use crate::variant::{MapVariant, MapVariantMode};
use crate::object::{MapObject, RawMapObject};
@ -325,7 +326,7 @@ impl Maps {
enemies: Vec<Option<MapEnemy>>,
objects: Vec<Option<MapObject>>,
rare_monster_table: &RareMonsterAppearTable,
event: Option<RareEnemyEvent>)
event: Holiday)
{
self.enemy_data = enemies
.into_iter()
@ -358,7 +359,7 @@ impl Maps {
}
}
pub fn generate_free_roam_maps(room_mode: RoomMode, event: Option<RareEnemyEvent>) -> Maps {
pub fn generate_free_roam_maps(room_mode: RoomMode, event: Holiday) -> Maps {
let rare_monster_table = RareMonsterAppearTable::new(room_mode.episode());
let map_variants = default_map_variants(room_mode.episode(), room_mode.player_mode());
Maps {

14
networking/Cargo.toml

@ -2,3 +2,17 @@
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 }

0
src/common/cipherkeys.rs → networking/src/cipherkeys.rs

0
src/common/interserver.rs → networking/src/interserver.rs

18
networking/src/lib.rs

@ -0,0 +1,18 @@
pub mod cipherkeys;
pub mod serverstate;
pub mod mainloop;
pub mod interserver;
// https://www.reddit.com/r/rust/comments/33xhhu/how_to_create_an_array_of_structs_that_havent/
#[macro_export]
macro_rules! init_array(
($ty:ty, $len:expr, $val:expr) => (
{
let mut array: [$ty; $len] = unsafe { std::mem::uninitialized() };
for i in array.iter_mut() {
unsafe { ::std::ptr::write(i, $val); }
}
array
}
)
);

6
src/common/mainloop/client.rs → networking/src/mainloop/client.rs

@ -9,8 +9,8 @@ use log::{trace, info, warn, error};
use libpso::crypto::{PSOCipher, NullCipher, CipherError};
use libpso::PacketParseError;
use crate::common::serverstate::ClientId;
use crate::common::serverstate::{RecvServerPacket, SendServerPacket, ServerState, OnConnect};
use crate::serverstate::ClientId;
use crate::serverstate::{RecvServerPacket, SendServerPacket, ServerState, OnConnect};
#[derive(Debug)]
@ -255,7 +255,7 @@ where
let (mut socket, addr) = listener.accept().await.unwrap();
id += 1;
let client_id = crate::common::serverstate::ClientId(id);
let client_id = crate::serverstate::ClientId(id);
info!("new client {:?} {:?} {:?}", client_id, socket, addr);
let (client_tx, client_rx) = async_std::channel::unbounded();

9
src/common/mainloop/interserver.rs → networking/src/mainloop/interserver.rs

@ -8,11 +8,10 @@ use std::collections::HashMap;
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::common::interserver::{ServerId, InterserverActor};
use crate::interserver::{ServerId, InterserverActor};
use libpso::crypto::{PSOCipher, NullCipher, CipherError};
use crate::common::serverstate::{ServerState, SendServerPacket, RecvServerPacket};
use crate::login::character::CharacterServerState;
use crate::serverstate::{ServerState, SendServerPacket, RecvServerPacket};
use entity::gateway::entitygateway::EntityGateway;
use async_std::channel;
@ -148,7 +147,7 @@ where
info!("[interserver listen] new server: {:?} {:?}", socket, addr);
id += 1;
let server_id = crate::common::interserver::ServerId(id);
let server_id = crate::interserver::ServerId(id);
let (client_tx, client_rx) = async_std::channel::unbounded();
state.set_sender(server_id, client_tx.clone()).await;
@ -195,7 +194,7 @@ where
}
};
id += 1;
let server_id = crate::common::interserver::ServerId(id);
let server_id = crate::interserver::ServerId(id);
info!("[interserver connect] found loginserv: {:?} {:?}", server_id, socket);
let (client_tx, client_rx) = async_std::channel::unbounded();

0
src/common/mainloop/mod.rs → networking/src/mainloop/mod.rs

0
src/common/serverstate.rs → networking/src/serverstate.rs

22
pktbuilder/Cargo.toml

@ -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 }

5
src/ship/character.rs → pktbuilder/src/character.rs

@ -1,9 +1,8 @@
use libpso::character::character;
use stats::leveltable::CharacterStats;
use entity::character::CharacterEntity;
//use crate::ship::items::{CharacterInventory, CharacterBank};
use crate::ship::items::bank::BankState;
use crate::ship::items::inventory::InventoryState;
use items::bank::BankState;
use items::inventory::InventoryState;
use entity::item::Meseta;

9
src/ship/packet/builder/mod.rs → pktbuilder/src/lib.rs

@ -3,14 +3,15 @@ pub mod message;
pub mod room;
pub mod quest;
pub mod ship;
pub mod character;
use libpso::character::character::Inventory;
use libpso::packet::ship::{PlayerHeader, PlayerInfo};
use stats::leveltable::LEVEL_TABLE;
use crate::ship::character::CharacterBytesBuilder;
use crate::ship::ship::ClientState;
use crate::ship::location::AreaClient;
use crate::ship::items::inventory::InventoryState;
use crate::character::CharacterBytesBuilder;
use client::ClientState;
use location::AreaClient;
use items::inventory::InventoryState;
pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) -> PlayerHeader {
PlayerHeader {

15
src/ship/packet/builder/lobby.rs → pktbuilder/src/lobby.rs

@ -1,9 +1,10 @@
use libpso::packet::ship::*;
use crate::common::serverstate::ClientId;
use crate::ship::ship::{Clients, ShipEvent};
use crate::ship::location::{ClientLocation, LobbyId, ClientLocationError};
use crate::ship::packet::builder::{player_info};
use crate::ship::items::state::ItemState;
use networking::serverstate::ClientId;
use maps::Holiday;
use client::Clients;
use location::{ClientLocation, LobbyId, ClientLocationError};
use crate::player_info;
use items::state::ItemState;
use futures::future::join_all;
@ -12,7 +13,7 @@ pub async fn join_lobby(id: ClientId,
client_location: &ClientLocation,
clients: &Clients,
item_state: &ItemState,
event: ShipEvent)
event: Holiday)
-> Result<JoinLobby, anyhow::Error> {
let lobby_clients = client_location.get_clients_in_lobby(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?;
@ -52,7 +53,7 @@ pub async fn add_to_lobby(id: ClientId,
client_location: &ClientLocation,
clients: &Clients,
item_state: &ItemState,
event: ShipEvent)
event: Holiday)
-> Result<AddToLobby, anyhow::Error> {
let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
let leader = client_location.get_lobby_leader(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?;

100
src/ship/packet/builder/message.rs → pktbuilder/src/message.rs

@ -2,20 +2,20 @@ use libpso::packet::messages::*;
use libpso::packet::ship::*;
use entity::item;
use stats::leveltable::CharacterStats;
use crate::ship::ship::{ShipError};
use crate::ship::items::ClientItemId;
use crate::ship::items::inventory::InventoryItem;
use crate::ship::items::state::IndividualItemDetail;
use crate::ship::items::bank::BankState;
use crate::ship::items::floor::FloorItem;
use crate::ship::location::AreaClient;
//use crate::ship::ship::{ShipError};
use items::ClientItemId;
use items::inventory::InventoryItem;
use items::state::IndividualItemDetail;
use items::bank::BankState;
use items::floor::FloorItem;
use location::AreaClient;
use std::convert::TryInto;
use shops::ShopItem;
pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result<ItemDrop, ShipError> {
pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> ItemDrop {
let item_bytes = item_drop.as_client_bytes();
Ok(ItemDrop {
ItemDrop {
client,
target,
map_area: item_drop.map_area.area_value(),
@ -24,37 +24,37 @@ pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result<ItemDr
x: item_drop.x,
z: item_drop.z,
y: item_drop.y,
item_bytes: item_bytes[0..12].try_into()?,
item_bytes: item_bytes[0..12].try_into().unwrap(),
item_id: item_drop.item_id.0,
item_bytes2: item_bytes[12..16].try_into()?,
item_bytes2: item_bytes[12..16].try_into().unwrap(),
unknown2: 0,
})
}
}
// TODO: this doesn't need to be a Result, just unwrap try_intos they are guaranteed to succeed
pub fn create_individual_item(area_client: AreaClient, item_id: ClientItemId, item: &IndividualItemDetail) -> Result<CreateItem, ShipError> {
pub fn create_individual_item(area_client: AreaClient, item_id: ClientItemId, item: &IndividualItemDetail) -> CreateItem {
let bytes = item.as_client_bytes();
Ok(CreateItem {
CreateItem {
client: area_client.local_client.id(),
target: 0,
item_data: bytes[0..12].try_into()?,
item_data: bytes[0..12].try_into().unwrap(),
item_id: item_id.0,
item_data2: bytes[12..16].try_into()?,
item_data2: bytes[12..16].try_into().unwrap(),
unknown: 0,
})
}
}
// TODO: this doesn't need to be a Result, just unwrap try_intos they are guaranteed to succeed
pub fn create_stacked_item(area_client: AreaClient, item_id: ClientItemId, tool: &item::tool::Tool, amount: usize) -> Result<CreateItem, ShipError> {
pub fn create_stacked_item(area_client: AreaClient, item_id: ClientItemId, tool: &item::tool::Tool, amount: usize) -> CreateItem {
let bytes = tool.as_stacked_bytes(amount);
Ok(CreateItem {
CreateItem {
client: area_client.local_client.id(),
target: 0,
item_data: bytes[0..12].try_into()?,
item_data: bytes[0..12].try_into().unwrap(),
item_id: item_id.0,
item_data2: bytes[12..16].try_into()?,
item_data2: bytes[12..16].try_into().unwrap(),
unknown: 0,
})
}
}
pub fn create_meseta(area_client: AreaClient, amount: usize) -> CreateItem {
@ -69,32 +69,32 @@ pub fn create_meseta(area_client: AreaClient, amount: usize) -> CreateItem {
}
}
pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> Result<CreateItem, ShipError> {
pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> CreateItem {
let bytes = item.item.as_client_bytes();
Ok(CreateItem {
CreateItem {
client: area_client.local_client.id(),
target: 0,
item_data: bytes[0..12].try_into()?,
item_data: bytes[0..12].try_into().unwrap(),
item_id: item.item_id.0,
item_data2: bytes[12..16].try_into()?,
item_data2: bytes[12..16].try_into().unwrap(),
unknown: 0,
})
}
}
pub fn create_withdrawn_inventory_item2(area_client: AreaClient, item: &InventoryItem) -> Result<CreateItem, ShipError> {
pub fn create_withdrawn_inventory_item2(area_client: AreaClient, item: &InventoryItem) -> CreateItem {
let bytes = item.item.as_client_bytes();
Ok(CreateItem {
CreateItem {
client: area_client.local_client.id(),
target: 0,
item_data: bytes[0..12].try_into()?,
item_data: bytes[0..12].try_into().unwrap(),
item_id: item.item_id.0,
item_data2: bytes[12..16].try_into()?,
item_data2: bytes[12..16].try_into().unwrap(),
unknown: 0,
})
}
}
pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Result<RemoveItemFromFloor, ShipError> {
Ok(RemoveItemFromFloor {
pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> RemoveItemFromFloor {
RemoveItemFromFloor {
client: area_client.local_client.id(),
target: 0,
client_id: area_client.local_client.id(),
@ -102,12 +102,12 @@ pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Resu
map_area: item.map_area.area_value(),
unknown2: 0,
item_id: item.item_id.0,
})
}
}
pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem) -> Result<DropSplitStack, ShipError> {
pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem) -> DropSplitStack {
let item_bytes = item.as_client_bytes();
Ok(DropSplitStack {
DropSplitStack {
client: area_client.local_client.id(),
target: 0,
variety: 0,
@ -115,16 +115,16 @@ pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem) -> Result<Dro
map_area: item.map_area.area_value(),
x: item.x,
z: item.z,
item_bytes: item_bytes[0..12].try_into()?,
item_bytes: item_bytes[0..12].try_into().unwrap(),
item_id: item.item_id.0,
item_bytes2: item_bytes[12..16].try_into()?,
item_bytes2: item_bytes[12..16].try_into().unwrap(),
unknown2: 0,
})
}
}
pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem) -> Result<DropSplitStack, ShipError> {
pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem) -> DropSplitStack {
let item_bytes = item.as_client_bytes();
Ok(DropSplitStack {
DropSplitStack {
client: area_client.local_client.id(),
target: 0,
variety: 0,
@ -132,11 +132,11 @@ pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem) -> Res
map_area: item.map_area.area_value(),
x: item.x,
z: item.z,
item_bytes: item_bytes[0..12].try_into()?,
item_bytes: item_bytes[0..12].try_into().unwrap(),
item_id: item.item_id.0,
item_bytes2: item_bytes[12..16].try_into()?,
item_bytes2: item_bytes[12..16].try_into().unwrap(),
unknown2: 0,
})
}
}
pub fn character_gained_exp(area_client: AreaClient, exp: u32) -> GiveCharacterExp {
@ -215,13 +215,13 @@ pub fn shop_list<I: ShopItem>(shop_type: u8, items: &[I]) -> ShopList {
}
}
pub fn tek_preview(id: ClientItemId, weapon: &item::weapon::Weapon) -> Result<TekPreview, ShipError> {
pub fn tek_preview(id: ClientItemId, weapon: &item::weapon::Weapon) -> TekPreview {
let bytes = weapon.as_bytes();
Ok(TekPreview {
TekPreview {
client: 0x79,
target: 0,
item_bytes: bytes[0..12].try_into()?,
item_bytes: bytes[0..12].try_into().unwrap(),
item_id: id.0,
item_bytes2: bytes[12..16].try_into()?,
})
item_bytes2: bytes[12..16].try_into().unwrap(),
}
}

5
src/ship/packet/builder/quest.rs → pktbuilder/src/quest.rs

@ -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::{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 {
let categories = quests.iter()

21
src/ship/packet/builder/room.rs → pktbuilder/src/room.rs

@ -1,11 +1,12 @@
use libpso::packet::ship::*;
use crate::common::serverstate::ClientId;
use crate::ship::ship::{ShipError, ClientState, Clients, ShipEvent};
use crate::ship::location::{ClientLocation, RoomId, AreaClient, ClientLocationError};
use crate::ship::room::RoomState;
use crate::ship::items::state::ItemState;
use crate::ship::packet::builder::{player_header, player_info};
use std::convert::TryInto;
use libpso::packet::ship::*;
use networking::serverstate::ClientId;
use maps::Holiday;
use client::{ClientState, Clients};
use location::{ClientLocation, RoomId, AreaClient, ClientLocationError};
use room::RoomState;
use items::state::ItemState;
use crate::{player_header, player_info};
use futures::stream::StreamExt;
@ -14,14 +15,14 @@ pub async fn join_room(id: ClientId,
client_location: &ClientLocation,
room_id: RoomId,
room: &RoomState,
event: ShipEvent)
event: Holiday)
-> Result<JoinRoom, anyhow::Error> {
let all_clients = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
#[allow(clippy::manual_try_fold)] // I don't think its even possible to make this work here
let players = futures::stream::iter(all_clients.iter())
.enumerate()
.fold::<Result<_, anyhow::Error>, _, _>(Ok([PlayerHeader::default(); 4]), |acc, (i, c)| async move {
let header_area_client = client_location.get_local_client(id).await.map_err(|err| ShipError::ClientLocationError(err.into()))?;
let header_area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError {err.into() })?;
clients.with(c.client, |client| Box::pin(async move {
acc.map(|mut a| {
a[i] = player_header(0x10000, client, &header_area_client);
@ -58,7 +59,7 @@ pub async fn add_to_room(_id: ClientId,
area_client: &AreaClient,
leader: &AreaClient,
item_state: &ItemState,
event: ShipEvent)
event: Holiday)
-> Result<AddToRoom, anyhow::Error> {
let inventory = item_state.get_character_inventory(&client.character).await?;
Ok(AddToRoom {

5
src/ship/packet/builder/ship.rs → pktbuilder/src/ship.rs

@ -1,8 +1,9 @@
use libpso::packet::login::{ShipList, ShipListEntry};
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 {
let ships = ships.iter()

107
pktbuilder/src/team.rs

@ -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
src/ship/packet/builder/trade.rs → pktbuilder/src/trade.rs

18
quests/Cargo.toml

@ -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 }

4
quests/src/lib.rs

@ -0,0 +1,4 @@
pub mod quests;
pub use quests::*;

0
src/ship/quests.rs → quests/src/quests.rs

17
room/Cargo.toml

@ -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 }

3
room/src/lib.rs

@ -0,0 +1,3 @@
pub mod room;
pub use room::*;

33
src/ship/room.rs → room/src/room.rs

@ -8,21 +8,28 @@ use thiserror::Error;
use rand::Rng;
use maps::maps::Maps;
use crate::ship::drops::DropTable;
use drops::DropTable;
use entity::character::SectionID;
use entity::room::{RoomEntityId, RoomEntityMode};
use maps::monster::{load_monster_stats_table, MonsterType, MonsterStats};
use maps::area::MapAreaLookup;
use maps::enemy::RareEnemyEvent;
use crate::ship::quests;
use crate::ship::ship::{ShipError, ShipEvent};
use crate::ship::location::{MAX_ROOMS, RoomId};
use quests;
use maps::Holiday;
use location::{MAX_ROOMS, RoomId};
use maps::room::{Episode, Difficulty, RoomMode};
#[derive(Error, Debug)]
pub enum RoomError {
#[error("invalid room id {0}")]
Invalid(u32),
}
#[derive(Clone)]
pub struct Rooms([Arc<RwLock<Option<RoomState>>>; MAX_ROOMS]);
impl Default for Rooms {
fn default() -> Rooms {
Rooms(core::array::from_fn(|_| Arc::new(RwLock::new(None))))
@ -33,7 +40,7 @@ impl Rooms {
pub async fn add(&self, room_id: RoomId, room: RoomState) -> Result<(), anyhow::Error> {
*self.0
.get(room_id.0)
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?
.ok_or_else(|| RoomError::Invalid(room_id.0 as u32))?
.write()
.await = Some(room);
Ok(())
@ -66,14 +73,14 @@ impl Rooms {
{
let room = self.0
.get(room_id.0)
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?
.ok_or_else(|| RoomError::Invalid(room_id.0 as u32))?
.read()
.await;
if let Some(room) = room.as_ref() {
Ok(func(room).await)
}
else {
Err(ShipError::InvalidRoom(room_id.0 as u32).into())
Err(RoomError::Invalid(room_id.0 as u32).into())
}
}
@ -84,7 +91,7 @@ impl Rooms {
{
let mut room = self.0
.get(room_id.0)
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?
.ok_or_else(|| RoomError::Invalid(room_id.0 as u32))?
.write()
.await;
@ -92,7 +99,7 @@ impl Rooms {
Ok(func(room).await)
}
else {
Err(ShipError::InvalidRoom(room_id.0 as u32).into())
Err(RoomError::Invalid(room_id.0 as u32).into())
}
}
@ -222,8 +229,8 @@ impl RoomState {
section_id: SectionID,
name: String,
password: [u16; 16],
event: ShipEvent,
map_builder: Arc<Box<dyn Fn(RoomMode, Option<RareEnemyEvent>) -> Maps + Send + Sync>>,
event: Holiday,
map_builder: Arc<Box<dyn Fn(RoomMode, Holiday) -> Maps + Send + Sync>>,
drop_table_builder: Arc<Box<dyn Fn(Episode, Difficulty, SectionID) -> DropTable + Send + Sync>>,
) -> Result<RoomState, anyhow::Error> {
let mode = match mode {
@ -251,7 +258,7 @@ impl RoomState {
random_seed: rand::thread_rng().gen(),
name,
password,
maps: map_builder(mode, event.rare_enemy_event()),
maps: map_builder(mode, event),
section_id,
drop_table: Box::new(drop_table_builder(episode, difficulty, section_id)),
bursting: false,

8
src/bin/login.rs

@ -2,7 +2,7 @@ use log::{info};
use entity::gateway::postgres::PostgresGateway;
use elseware::login::login::LoginServerState;
use elseware::login::character::CharacterServerState;
use elseware::common::interserver::AuthToken;
use networking::interserver::AuthToken;
fn main() {
let colors = fern::colors::ColoredLevelConfig::new()
@ -38,17 +38,17 @@ fn main() {
let login_state = LoginServerState::new(entity_gateway.clone(), charserv_ip);
let login_loop = async_std::task::spawn(async move {
elseware::common::mainloop::run_server(login_state, elseware::login::login::LOGIN_PORT).await;
networking::mainloop::run_server(login_state, elseware::login::login::LOGIN_PORT).await;
});
let char_state = CharacterServerState::new(entity_gateway, AuthToken(shipgate_token));
let sub_char_state = char_state.clone();
let character_loop = async_std::task::spawn(async move {
elseware::common::mainloop::run_server(sub_char_state, elseware::login::character::CHARACTER_PORT).await;
networking::mainloop::run_server(sub_char_state, elseware::login::character::CHARACTER_PORT).await;
});
let inter_character_loop = async_std::task::spawn(async move {
elseware::common::mainloop::run_interserver_listen(char_state, elseware::login::login::COMMUNICATION_PORT).await;
networking::mainloop::run_interserver_listen(char_state, elseware::login::login::COMMUNICATION_PORT).await;
});
info!("[auth/character] starting server");

30
src/bin/main.rs

@ -1,13 +1,13 @@
use std::net::Ipv4Addr;
use log::{info};
use elseware::common::interserver::AuthToken;
use networking::interserver::AuthToken;
use elseware::login::login::LoginServerState;
use elseware::login::character::CharacterServerState;
use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config, load_motd};
use elseware::ship::ship::{ShipServerStateBuilder, ShipEvent};
use elseware::ship::ship::ShipServerStateBuilder;
#[allow(unused_imports)]
use maps::Holiday;
use entity::gateway::{EntityGateway, InMemoryGateway, PostgresGateway};
use entity::account::{NewUserAccountEntity, NewUserSettingsEntity};
use entity::character::NewCharacterEntity;
@ -338,25 +338,25 @@ fn main() {
let (patch_file_tree, patch_file_lookup) = generate_patch_tree(patch_config.path.as_str());
let patch_state = PatchServerState::new(patch_file_tree, patch_file_lookup, patch_motd);
let patch_loop = async_std::task::spawn(async move {
elseware::common::mainloop::run_server(patch_state, patch_config.port).await;
networking::mainloop::run_server(patch_state, patch_config.port).await;
});
info!("[auth] starting server");
let login_state = LoginServerState::new(entity_gateway.clone(), "127.0.0.1".parse().unwrap());
let login_loop = async_std::task::spawn(async move {
elseware::common::mainloop::run_server(login_state, elseware::login::login::LOGIN_PORT).await;
networking::mainloop::run_server(login_state, elseware::login::login::LOGIN_PORT).await;
});
info!("[character] starting server");
let char_state = CharacterServerState::new(entity_gateway.clone(), AuthToken("".into()));
let sub_char_state = char_state.clone();
let character_loop = async_std::task::spawn(async move {
elseware::common::mainloop::run_server(sub_char_state, elseware::login::character::CHARACTER_PORT).await;
networking::mainloop::run_server(sub_char_state, elseware::login::character::CHARACTER_PORT).await;
});
let sub_char_state = char_state.clone();
let inter_character_loop = async_std::task::spawn(async move {
elseware::common::mainloop::run_interserver_listen(sub_char_state, elseware::login::login::COMMUNICATION_PORT).await;
networking::mainloop::run_interserver_listen(sub_char_state, elseware::login::login::COMMUNICATION_PORT).await;
});
info!("[ship] starting servers");
@ -364,32 +364,32 @@ fn main() {
.name("US/Sona-Nyl".into())
.ip(Ipv4Addr::new(127,0,0,1))
.port(elseware::ship::ship::SHIP_PORT)
.event(ShipEvent::Halloween)
.event(Holiday::Halloween)
.gateway(entity_gateway.clone())
.build();
let sub_ship_state = ship_state.clone();
let ship_loop1 = async_std::task::spawn(async move {
elseware::common::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT).await;
networking::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT).await;
});
let sub_ship_state = ship_state.clone();
let inter_ship_loop1 = async_std::task::spawn(async move {
elseware::common::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await;
networking::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await;
});
let ship_state = ShipServerStateBuilder::default()
.name("EU/Dylath-Leen".into())
.ip(Ipv4Addr::new(127,0,0,1))
.port(elseware::ship::ship::SHIP_PORT+2000)
.event(ShipEvent::Christmas)
.event(Holiday::Christmas)
.gateway(entity_gateway.clone())
.build();
let sub_ship_state = ship_state.clone();
let ship_loop2 = async_std::task::spawn(async move {
elseware::common::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT+2000).await;
networking::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT+2000).await;
});
let sub_ship_state = ship_state.clone();
let inter_ship_loop2 = async_std::task::spawn(async move {
elseware::common::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await;
networking::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await;
});
let ship_state = ShipServerStateBuilder::default()
@ -400,11 +400,11 @@ fn main() {
.build();
let sub_ship_state = ship_state.clone();
let ship_loop3 = async_std::task::spawn(async move {
elseware::common::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT+3000).await;
networking::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT+3000).await;
});
let sub_ship_state = ship_state.clone();
let inter_ship_loop3 = async_std::task::spawn(async move {
elseware::common::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await;
networking::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await;
});
futures::future::join_all(vec![patch_loop, login_loop, character_loop, inter_character_loop,

4
src/bin/patch.rs

@ -1,5 +1,5 @@
use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config_env, load_motd};
use log::{info};
use log::info;
fn main() {
info!("[patch] starting server");
@ -9,7 +9,7 @@ fn main() {
let patch_state = PatchServerState::new(patch_file_tree, patch_file_lookup, patch_motd);
let patch_loop = async_std::task::spawn(async move {
elseware::common::mainloop::run_server(patch_state, patch_config.port).await;
networking::mainloop::run_server(patch_state, patch_config.port).await;
});
async_std::task::block_on(patch_loop);

8
src/bin/ship.rs

@ -1,7 +1,7 @@
use log::{info};
use log::info;
use entity::gateway::postgres::PostgresGateway;
use elseware::ship::ship::ShipServerStateBuilder;
use elseware::common::interserver::AuthToken;
use networking::interserver::AuthToken;
fn main() {
let colors = fern::colors::ColoredLevelConfig::new()
@ -49,10 +49,10 @@ fn main() {
let sub_ship_state = ship_state.clone();
let ship_loop = async_std::task::spawn(async move {
elseware::common::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT).await;
networking::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT).await;
});
let inter_ship_loop = async_std::task::spawn(async move {
elseware::common::mainloop::run_interserver_connect(ship_state, shipgate_ip, elseware::login::login::COMMUNICATION_PORT).await;
networking::mainloop::run_interserver_connect(ship_state, shipgate_ip, elseware::login::login::COMMUNICATION_PORT).await;
});
info!("[auth/character] starting server");

18
src/common/mod.rs

@ -1,18 +0,0 @@
pub mod cipherkeys;
pub mod serverstate;
pub mod mainloop;
pub mod interserver;
// https://www.reddit.com/r/rust/comments/33xhhu/how_to_create_an_array_of_structs_that_havent/
#[macro_export]
macro_rules! init_array(
($ty:ty, $len:expr, $val:expr) => (
{
let mut array: [$ty; $len] = unsafe { std::mem::uninitialized() };
for i in array.iter_mut() {
unsafe { ::std::ptr::write(i, $val); }
}
array
}
)
);

3
src/lib.rs

@ -5,11 +5,10 @@
#![feature(try_blocks)]
#![feature(test)]
#![feature(error_generic_member_access)]
#![feature(lazy_cell)]
extern crate test;
pub mod common;
//pub mod common;
//pub mod entity;
pub mod patch;
pub mod login;

11
src/login/character.rs

@ -15,9 +15,9 @@ use libpso::crypto::bb::PSOBBCipher;
use libpso::character::character;
use entity::item;
use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
use crate::common::interserver::{ServerId, InterserverActor, LoginMessage, ShipMessage, Ship};
use networking::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
use networking::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
use networking::interserver::{ServerId, InterserverActor, LoginMessage, ShipMessage, Ship};
use stats::leveltable::LEVEL_TABLE;
use libpso::{utf8_to_array, utf8_to_utf16_array};
@ -32,10 +32,11 @@ use entity::item::mag::Mag;
use entity::character::{CharacterEntity, NewCharacterEntity, CharacterClass, TechLevel};
use crate::login::login::{get_login_status};
use crate::common::interserver::AuthToken;
use networking::interserver::AuthToken;
use pktbuilder::ship::SHIP_MENU_ID;
pub const CHARACTER_PORT: u16 = 12001;
pub const SHIP_MENU_ID: u32 = 1;
#[derive(thiserror::Error, Debug)]
pub enum CharacterError {

4
src/login/login.rs

@ -11,8 +11,8 @@ use libpso::{PacketParseError, PSOPacket};
use libpso::crypto::bb::PSOBBCipher;
use libpso::util::array_to_utf8;
use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
use networking::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
use networking::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
use entity::gateway::EntityGateway;
use entity::account::{UserAccountEntity};

4
src/patch/patch.rs

@ -11,8 +11,8 @@ use libpso::crypto::pc::PSOPCCipher;
use ron::de::from_str;
use serde::Deserialize;
use crate::common::mainloop::{NetworkError};
use crate::common::serverstate::{RecvServerPacket, SendServerPacket, ServerState, OnConnect, ClientId};
use networking::mainloop::{NetworkError};
use networking::serverstate::{RecvServerPacket, SendServerPacket, ServerState, OnConnect, ClientId};
#[derive(Debug)]
pub enum PatchError {

8
src/ship/chatcommand.rs

@ -1,11 +1,11 @@
use libpso::packet::ship::PlayerChat;
use entity::gateway::EntityGateway;
use crate::common::serverstate::ClientId;
use networking::serverstate::ClientId;
use crate::ship::ship::{ShipServerState, SendShipPacket};
use crate::ship::client::Clients;
use crate::ship::items::state::ItemState;
use client::Clients;
use items::state::ItemState;
use entity::item::{BankName, BankIdentifier};
use crate::ship::packet::builder::message::bank_item_list;
use pktbuilder::message::bank_item_list;
async fn default_bank<'a, EG>(id: ClientId,
entity_gateway: &mut EG,

0
src/ship/drops/drop_table.rs

16
src/ship/mod.rs

@ -1,16 +1,16 @@
#[allow(clippy::module_inception)]
pub mod ship;
pub mod location;
pub mod character;
pub mod client;
pub mod room;
pub mod items;
//pub mod location;
//pub mod character;
//pub mod client;
//pub mod room;
//pub mod items;
//pub mod item_stats;
//pub mod map;
//pub mod monster;
pub mod drops;
//pub mod drops;
pub mod packet;
pub mod quests;
//pub mod quests;
//pub mod shops;
pub mod trade;
//pub mod trade;
pub mod chatcommand;

212
src/ship/monster.rs

@ -1,212 +0,0 @@
#![allow(dead_code)]
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use serde::{Serialize, Deserialize};
use crate::ship::room::{Difficulty, Episode, RoomMode};
#[derive(Debug)]
pub enum MonsterParseError {
UnknownMonster(String),
}
pub struct MonsterStatError;
#[derive(Debug, Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq, enum_utils::FromStr, derive_more::Display)]
pub enum MonsterType {
Hildebear,
Hildeblue,
Mothmant,
Monest,
RagRappy,
AlRappy,
SavageWolf,
BarbarousWolf,
Booma,
Gobooma,
Gigobooma,
GrassAssassin,
PoisonLily,
NarLily,
NanoDragon,
EvilShark,
PalShark,
GuilShark,
PofuillySlime,
PouillySlime,
PanArms,
Hidoom,
Migium,
Dubchic,
Garanz,
SinowBeat,
SinowGold,
Canadine,
Canane,
RingCanadine,
Delsaber,
ChaosSorcerer,
BeeR,
BeeL,
DarkGunner,
DeathGunner,
ChaosBringer,
DarkBelra,
Claw,
Bulk,
Bulclaw,
Dimenian,
LaDimenian,
SoDimenian,
Dragon,
DeRolLe,
DeRolLeBody,
DeRolLeMine,
VolOptPartA,
VolOptPillar,
VolOptMonitor,
VolOptAmp,
VolOptCore,
VolOptUnused,
VolOpt,
VolOptTrap,
DarkFalz,
DarkFalz1,
DarkFalz2,
DarkFalz3,
Darvant,
UltDarvant,
Dubwitch,
Gillchic,
EventRappy,
Merillia,
Meriltas,
Gee,
GiGue,
Mericarol,
Merikle,
Mericus,
UlGibbon,
ZolGibbon,
Gibbles,
SinowBerill,
SinowSpigell,
Dolmolm,
Dolmdarl,
Morfos,
Recobox,
Recon,
SinowZoa,
SinowZele,
Deldepth,
Delbiter,
BarbaRay,
PigRay,
GolDragon,
GalGryphon,
OlgaFlow,
OlgaFlow1,
OlgaFlow2,
Gael,
Giel,
StRappy,
HalloRappy,
EasterRappy,
LoveRappy,
IllGill,
DelLily,
Epsilon,
Epsiguard,
Boota,
ZeBoota,
BaBoota,
SandRappyCrater,
SandRappyDesert,
ZuCrater,
PazuzuCrater,
Astark,
SatelliteLizardCrater,
YowieCrater,
Dorphon,
DorphonEclair,
Goran,
GoranDetonator,
PyroGoran,
DelRappyCrater,
DelRappyDesert,
MerissaA,
MerissaAA,
ZuDesert,
PazuzuDesert,
SatelliteLizardDesert,
YowieDesert,
Girtablulu,
SaintMillion,
Shambertin,
Kondrieu,
}
#[derive(serde::Deserialize, Debug)]
pub struct MonsterStats {
pub atp: u16,
pub mst: u16,
pub evp: u16,
pub hp: u16,
pub dfp: u16,
pub ata: u16,
pub lck: u16,
pub esp: u16,
pub exp: u32,
}
fn load_battle_param(filename: &str) -> HashMap<MonsterType, MonsterStats> {
let mut path = PathBuf::from("data/battle_param/");
path.push(filename);
let mut f = File::open(path).unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
toml::from_str::<HashMap<String, MonsterStats>>(s.as_str()).unwrap()
.into_iter()
.map(|(monster_name, stats)| {
(monster_name.parse().unwrap(), stats)
}).collect()
}
pub fn load_monster_stats_table(mode: &RoomMode) -> Result<HashMap<MonsterType, MonsterStats>, MonsterStatError> {
match mode {
RoomMode::Multi {episode: Episode::One, difficulty: Difficulty::Normal} => Ok(load_battle_param("ep1_multi_normal.toml")),
RoomMode::Multi {episode: Episode::One, difficulty: Difficulty::Hard} => Ok(load_battle_param("ep1_multi_hard.toml")),
RoomMode::Multi {episode: Episode::One, difficulty: Difficulty::VeryHard} => Ok(load_battle_param("ep1_multi_veryhard.toml")),
RoomMode::Multi {episode: Episode::One, difficulty: Difficulty::Ultimate} => Ok(load_battle_param("ep1_multi_ultimate.toml")),
RoomMode::Multi {episode: Episode::Two, difficulty: Difficulty::Normal} => Ok(load_battle_param("ep2_multi_normal.toml")),
RoomMode::Multi {episode: Episode::Two, difficulty: Difficulty::Hard} => Ok(load_battle_param("ep2_multi_hard.toml")),
RoomMode::Multi {episode: Episode::Two, difficulty: Difficulty::VeryHard} => Ok(load_battle_param("ep2_multi_veryhard.toml")),
RoomMode::Multi {episode: Episode::Two, difficulty: Difficulty::Ultimate} => Ok(load_battle_param("ep2_multi_ultimate.toml")),
RoomMode::Multi {episode: Episode::Four, difficulty: Difficulty::Normal} => Ok(load_battle_param("ep4_multi_normal.toml")),
RoomMode::Multi {episode: Episode::Four, difficulty: Difficulty::Hard} => Ok(load_battle_param("ep4_multi_hard.toml")),
RoomMode::Multi {episode: Episode::Four, difficulty: Difficulty::VeryHard} => Ok(load_battle_param("ep4_multi_veryhard.toml")),
RoomMode::Multi {episode: Episode::Four, difficulty: Difficulty::Ultimate} => Ok(load_battle_param("ep4_multi_ultimate.toml")),
RoomMode::Single {episode: Episode::One, difficulty: Difficulty::Normal} => Ok(load_battle_param("ep1_solo_normal.toml")),
RoomMode::Single {episode: Episode::One, difficulty: Difficulty::Hard} => Ok(load_battle_param("ep1_solo_hard.toml")),
RoomMode::Single {episode: Episode::One, difficulty: Difficulty::VeryHard} => Ok(load_battle_param("ep1_solo_veryhard.toml")),
RoomMode::Single {episode: Episode::One, difficulty: Difficulty::Ultimate} => Ok(load_battle_param("ep1_solo_ultimate.toml")),
RoomMode::Single {episode: Episode::Two, difficulty: Difficulty::Normal} => Ok(load_battle_param("ep2_solo_normal.toml")),
RoomMode::Single {episode: Episode::Two, difficulty: Difficulty::Hard} => Ok(load_battle_param("ep2_solo_hard.toml")),
RoomMode::Single {episode: Episode::Two, difficulty: Difficulty::VeryHard} => Ok(load_battle_param("ep2_solo_veryhard.toml")),
RoomMode::Single {episode: Episode::Two, difficulty: Difficulty::Ultimate} => Ok(load_battle_param("ep2_solo_ultimate.toml")),
RoomMode::Single {episode: Episode::Four, difficulty: Difficulty::Normal} => Ok(load_battle_param("ep4_solo_normal.toml")),
RoomMode::Single {episode: Episode::Four, difficulty: Difficulty::Hard} => Ok(load_battle_param("ep4_solo_hard.toml")),
RoomMode::Single {episode: Episode::Four, difficulty: Difficulty::VeryHard} => Ok(load_battle_param("ep4_solo_veryhard.toml")),
RoomMode::Single {episode: Episode::Four, difficulty: Difficulty::Ultimate} => Ok(load_battle_param("ep4_solo_ultimate.toml")),
_ => Err(MonsterStatError),
}
}

6
src/ship/packet/handler/auth.rs

@ -1,11 +1,11 @@
use libpso::packet::login::{Login, LoginResponse, AccountStatus, Session};
use libpso::packet::ship::*;
use crate::common::serverstate::ClientId;
use networking::serverstate::ClientId;
use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients};
use crate::login::login::get_login_status;
use entity::gateway::EntityGateway;
use crate::ship::items::state::ItemState;
use crate::common::interserver::ShipMessage;
use items::state::ItemState;
use networking::interserver::ShipMessage;
#[allow(clippy::too_many_arguments)]
pub async fn validate_login<EG>(id: ClientId,

4
src/ship/packet/handler/communication.rs

@ -1,7 +1,7 @@
use libpso::packet::ship::*;
use crate::common::serverstate::ClientId;
use networking::serverstate::ClientId;
use crate::ship::ship::{SendShipPacket, Clients};
use crate::ship::location::{ClientLocation};
use location::{ClientLocation};
use entity::gateway::EntityGateway;
use futures::future::join_all;

38
src/ship/packet/handler/direct_message.rs

@ -4,21 +4,21 @@ use rand::seq::SliceRandom;
use libpso::packet::ship::*;
use libpso::packet::messages::*;
use stats::leveltable::LEVEL_TABLE;
use crate::common::serverstate::ClientId;
use networking::serverstate::ClientId;
use crate::ship::ship::{SendShipPacket, ShipError, Clients, ItemShops};
use crate::ship::location::ClientLocation;
use crate::ship::drops::ItemDrop;
use crate::ship::room::Rooms;
use crate::ship::items::ClientItemId;
use location::ClientLocation;
use drops::ItemDrop;
use room::Rooms;
use items::ClientItemId;
use entity::gateway::EntityGateway;
use entity::item;
use libpso::utf8_to_utf16_array;
use crate::ship::packet::builder;
use pktbuilder as builder;
use 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 items::state::{ItemState, ItemStateError};
use items::floor::{FloorType, FloorItemDetail};
use items::actions::TriggerCreateItem;
use 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};
const BANK_ACTION_DEPOSIT: u8 = 0;
const BANK_ACTION_WITHDRAW: u8 = 1;
@ -122,7 +122,7 @@ where
})).await?;
let floor_item = enemy_drops_item(item_state, entity_gateway, character_id, room_entity_id, monster.monster, item_drop).await?;
let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?;
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)))));
}
@ -149,10 +149,10 @@ where
let mut item_state = item_state.clone();
Box::pin(async move {
let (item, floor_type) = item_state.get_floor_item(&client.character.id, &ClientItemId(pickup_item.item_id)).await?;
let remove_item = builder::message::remove_item_from_floor(area_client, &item)?;
let remove_item = builder::message::remove_item_from_floor(area_client, &item);
let create_item = match &item.item {
FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, individual_floor_item)?),
FloorItemDetail::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id, &stacked_floor_item.tool, stacked_floor_item.count())?),
FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, individual_floor_item)),
FloorItemDetail::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id, &stacked_floor_item.tool, stacked_floor_item.count())),
FloorItemDetail::Meseta(_) => None,
};
@ -234,7 +234,7 @@ where
})).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, &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)?;
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)))))
}
@ -293,7 +293,7 @@ where
}
else {
let item_added_to_inventory = withdraw_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?;
let item_created = builder::message::create_withdrawn_inventory_item2(area_client, &item_added_to_inventory)?;
let item_created = builder::message::create_withdrawn_inventory_item2(area_client, &item_added_to_inventory);
vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(item_created)))]
}
},
@ -440,7 +440,7 @@ where
_ => {}
}
}
Ok::<_, anyhow::Error>(builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)?)
Ok::<_, anyhow::Error>(builder::message::create_withdrawn_inventory_item(area_client, &inventory_item))
})}).await??;
let other_clients_in_area = client_location.get_client_neighbors(id).await?;
@ -503,7 +503,7 @@ where
});
take_meseta(&mut item_state, &mut entity_gateway, &client.character.id, item::Meseta(100)).await?;
Ok::<_, anyhow::Error>(builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?)
Ok::<_, anyhow::Error>(builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon))
})}).await??;
@ -539,7 +539,7 @@ where
};
let weapon = apply_modifier(&mut item_state, &mut entity_gateway, &client.character, item_id, item::ItemModifier::WeaponModifier(modifier)).await?;
let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &weapon)?;
let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &weapon);
Ok(neighbors.into_iter()
.map(move |c| {

30
src/ship/packet/handler/lobby.rs

@ -1,12 +1,14 @@
use libpso::packet::ship::*;
use crate::common::serverstate::ClientId;
use networking::serverstate::ClientId;
use stats::leveltable::LEVEL_TABLE;
use crate::ship::ship::{SendShipPacket, ShipError, Clients, ShipEvent};
use crate::ship::room::Rooms;
use crate::ship::character::{FullCharacterBytesBuilder};
use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError, RoomId};
use crate::ship::packet;
use crate::ship::items::state::ItemState;
use crate::ship::ship::{SendShipPacket, ShipError};
use maps::Holiday;
use client::Clients;
use room::Rooms;
use pktbuilder::character::FullCharacterBytesBuilder;
use location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError, RoomId};
//use pktbuilder;
use items::state::ItemState;
use entity::gateway::EntityGateway;
use entity::room::RoomNote;
use maps::area::MapArea;
@ -57,11 +59,11 @@ pub async fn send_player_to_lobby(id: ClientId,
client_location: &mut ClientLocation,
clients: &Clients,
item_state: &ItemState,
event: ShipEvent)
event: Holiday)
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).await.map_err(|_| ShipError::TooManyClients)?;
let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, event).await?;
let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, event).await?;
let join_lobby = pktbuilder::lobby::join_lobby(id, lobby, client_location, clients, item_state, event).await?;
let addto = pktbuilder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, event).await?;
let neighbors = client_location.get_client_neighbors(id).await.unwrap();
Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))]
.into_iter()
@ -77,7 +79,7 @@ pub async fn change_lobby<EG>(id: ClientId,
item_state: &mut ItemState,
rooms: &Rooms,
entity_gateway: &mut EG,
event: ShipEvent)
event: Holiday)
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
where
EG: EntityGateway + Clone + 'static,
@ -111,7 +113,7 @@ where
})}).await??;
},
}
let leave_lobby = packet::builder::lobby::remove_from_lobby(id, client_location).await?;
let leave_lobby = pktbuilder::lobby::remove_from_lobby(id, client_location).await?;
let old_neighbors = client_location.get_client_neighbors(id).await.unwrap();
let mut lobby = LobbyId(requested_lobby as usize);
if client_location.add_client_to_lobby(id, lobby).await.is_err() {
@ -131,8 +133,8 @@ where
Box::pin(async move {
item_state.load_character(&mut entity_gateway, &client.character).await
})}).await??;
let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, event).await?;
let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, event).await?;
let join_lobby = pktbuilder::lobby::join_lobby(id, lobby, client_location, clients, item_state, event).await?;
let addto = pktbuilder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, event).await?;
let neighbors = client_location.get_client_neighbors(id).await?;
Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))]
.into_iter()

30
src/ship/packet/handler/message.rs

@ -2,15 +2,16 @@ use libpso::packet::ship::*;
use libpso::packet::messages::*;
use entity::gateway::EntityGateway;
use entity::item::Meseta;
use crate::common::serverstate::ClientId;
use networking::serverstate::ClientId;
use stats::leveltable::LEVEL_TABLE;
use crate::ship::ship::{SendShipPacket, ShipError, Clients, ItemDropLocation};
use crate::ship::room::Rooms;
use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::items::ClientItemId;
use crate::ship::packet::builder;
use crate::ship::items::state::ItemState;
use crate::ship::items::tasks::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item, take_meseta, floor_item_limit_reached};
use crate::ship::ship::{SendShipPacket, ShipError};
use client::{Clients, ItemDropLocation};
use ::room::Rooms;
use location::{ClientLocation, ClientLocationError};
use items::ClientItemId;
use pktbuilder as builder;
use items::state::ItemState;
use items::tasks::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item, take_meseta, floor_item_limit_reached};
pub async fn request_exp<EG>(id: ClientId,
request_exp: RequestExp,
@ -162,7 +163,7 @@ where
drop_meseta(&mut item_state, &mut entity_gateway, &client.character, drop_location.map_area, (drop_location.x, drop_location.z), no_longer_has_item.amount).await
})}).await??;
let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?;
let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta);
let no_longer_has_meseta_pkt = builder::message::player_no_longer_has_meseta(area_client, no_longer_has_item.amount);
let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
@ -198,7 +199,7 @@ where
no_longer_has_item.amount)
.await
})}).await??;
let dropped_item_pkt = builder::message::drop_split_stack(area_client, &dropped_item)?;
let dropped_item_pkt = builder::message::drop_split_stack(area_client, &dropped_item);
let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
Ok(clients_in_area.into_iter()
@ -348,6 +349,15 @@ where
})}).await??
.into_iter()
.flat_map(move |pkt| {
let pkt = match pkt {
items::actions::CreateItem::Individual(area_client, item_id, item_detail) => {
builder::message::create_individual_item(area_client, item_id, &item_detail)
},
items::actions::CreateItem::Stacked(area_client, item_id, tool, amount) => {
builder::message::create_stacked_item(area_client, item_id, &tool, amount)
}
};
let pkt = SendShipPacket::Message(Message::new(GameMessage::CreateItem(pkt)));
let player_use_tool = player_use_tool.clone();
neighbors.clone().map(move |client| {
vec![(client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerUseItem(player_use_tool.clone())))), (client.client, pkt.clone())]

16
src/ship/packet/handler/quest.rs

@ -1,12 +1,14 @@
use std::io::{Cursor, Read, Seek, SeekFrom};
use futures::stream::{FuturesOrdered, StreamExt};
use libpso::packet::ship::*;
use crate::common::serverstate::ClientId;
use crate::ship::ship::{SendShipPacket, ShipError, Clients, ShipEvent};
use crate::ship::room::Rooms;
use networking::serverstate::ClientId;
use crate::ship::ship::{SendShipPacket, ShipError};
use client::Clients;
use maps::Holiday;
use room::Rooms;
use maps::enemy::RareMonsterAppearTable;
use crate::ship::location::{ClientLocation};
use crate::ship::packet::builder::quest;
use location::{ClientLocation};
use pktbuilder::quest;
use libpso::util::array_to_utf8;
enum QuestFileType {
@ -97,7 +99,7 @@ pub async fn player_chose_quest(id: ClientId,
clients: &Clients,
client_location: &ClientLocation,
rooms: &Rooms,
event: ShipEvent)
event: Holiday)
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
let room_id = client_location.get_room(id).await?;
@ -118,7 +120,7 @@ pub async fn player_chose_quest(id: ClientId,
.clone();
let rare_monster_table = RareMonsterAppearTable::new(room.mode.episode());
room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone(), &rare_monster_table, event.rare_enemy_event());
room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone(), &rare_monster_table, event);
room.map_areas = quest.map_areas.clone();
let bin = quest::quest_header(&questmenuselect, &quest.bin_blob, "bin");

23
src/ship/packet/handler/room.rs

@ -5,20 +5,21 @@ use async_std::sync::Arc;
use libpso::packet::ship::*;
use libpso::packet::messages::*;
use crate::common::serverstate::ClientId;
use networking::serverstate::ClientId;
use stats::leveltable::LEVEL_TABLE;
use entity::gateway::EntityGateway;
use entity::character::SectionID;
use entity::room::{NewRoomEntity, RoomEntityMode, RoomNote};
use crate::ship::drops::DropTable;
use crate::ship::ship::{SendShipPacket, Clients, ShipEvent};
use crate::ship::room::{Rooms, RoomState, RoomCreationError};
use drops::DropTable;
use crate::ship::ship::SendShipPacket;
use client::Clients;
use room::{Rooms, RoomState, RoomCreationError};
use maps::Holiday;
use maps::room::{Episode, Difficulty, RoomMode};
use maps::enemy::RareEnemyEvent;
use maps::maps::Maps;
use crate::ship::location::{ClientLocation, RoomId, RoomLobby, GetAreaError};
use crate::ship::packet::builder;
use crate::ship::items::state::ItemState;
use location::{ClientLocation, RoomId, RoomLobby, GetAreaError};
use pktbuilder as builder;
use items::state::ItemState;
#[allow(clippy::too_many_arguments)]
pub async fn create_room<EG>(id: ClientId,
@ -28,9 +29,9 @@ pub async fn create_room<EG>(id: ClientId,
clients: &Clients,
item_state: &mut ItemState,
rooms: &Rooms,
map_builder: Arc<Box<dyn Fn(RoomMode, Option<RareEnemyEvent>) -> Maps + Send + Sync>>,
map_builder: Arc<Box<dyn Fn(RoomMode, Holiday) -> Maps + Send + Sync>>,
drop_table_builder: Arc<Box<dyn Fn(Episode, Difficulty, SectionID) -> DropTable + Send + Sync>>,
event: ShipEvent)
event: Holiday)
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
where
EG: EntityGateway + Clone + 'static,
@ -136,7 +137,7 @@ pub async fn join_room<EG>(id: ClientId,
clients: &Clients,
item_state: &mut ItemState,
rooms: &Rooms,
event: ShipEvent)
event: Holiday)
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
where
EG: EntityGateway + Clone + 'static,

2
src/ship/packet/handler/settings.rs

@ -1,5 +1,5 @@
use libpso::packet::ship::*;
use crate::common::serverstate::ClientId;
use networking::serverstate::ClientId;
use crate::ship::ship::{SendShipPacket, Clients};
use entity::gateway::EntityGateway;

6
src/ship/packet/handler/ship.rs

@ -1,10 +1,10 @@
use async_std::sync::{Arc, RwLock};
use libpso::packet::ship::*;
use libpso::packet::login::RedirectClient;
use crate::common::serverstate::ClientId;
use crate::common::interserver::Ship;
use networking::serverstate::ClientId;
use networking::interserver::Ship;
use crate::ship::ship::{SendShipPacket, ShipError};
use crate::ship::packet::builder;
use pktbuilder as builder;
pub async fn ship_list(id: ClientId, ship_list: &Arc<RwLock<Vec<Ship>>>) -> Vec<(ClientId, SendShipPacket)> {
let ship_list = ship_list

28
src/ship/packet/handler/trade.rs

@ -1,19 +1,19 @@
use std::convert::TryInto;
use libpso::packet::ship::*;
use libpso::packet::messages::*;
use crate::common::serverstate::ClientId;
use networking::serverstate::ClientId;
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
use crate::ship::location::{ClientLocation};
use crate::ship::items::ClientItemId;
use crate::ship::items::state::{ItemState, ItemStateError};
use crate::ship::items::inventory::InventoryItemDetail;
use crate::ship::trade::{TradeItem, TradeState, TradeStatus};
use location::{ClientLocation};
use items::ClientItemId;
use items::state::{ItemState, ItemStateError};
use items::inventory::InventoryItemDetail;
use items::trade::TradeItem;
use entity::gateway::EntityGateway;
use crate::ship::packet::builder;
use crate::ship::items::tasks::trade_items;
use crate::ship::location::{AreaClient, RoomId};
use pktbuilder as builder;
use items::tasks::trade_items;
use location::{AreaClient, RoomId};
use entity::item::Meseta;
use crate::ship::trade::ClientTradeState;
use trade::{ClientTradeState, TradeState, TradeStatus};
pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01);
pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF);
@ -450,8 +450,8 @@ where
enum TradeReady/*<'a>*/ {
OnePlayer,
BothPlayers(RoomId,
(AreaClient, crate::ship::trade::ClientTradeState),
(AreaClient, crate::ship::trade::ClientTradeState)),
(AreaClient, ClientTradeState),
(AreaClient, ClientTradeState)),
//(AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState),
//(AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState)),
}
@ -527,10 +527,10 @@ where
.map(|(client, item)| {
match item.item {
InventoryItemDetail::Individual(individual_item) => {
GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item).unwrap())
GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item))
},
InventoryItemDetail::Stacked(stacked_item) => {
GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap())
GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()))
}
}
});

2
src/ship/packet/mod.rs

@ -1,2 +1,2 @@
pub mod builder;
//pub mod builder;
pub mod handler;

105
src/ship/ship.rs

@ -14,31 +14,29 @@ use libpso::{PacketParseError, PSOPacket};
use libpso::crypto::bb::PSOBBCipher;
use libpso::packet::ship::{BLOCK_MENU_ID, ROOM_MENU_ID};
use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
use crate::common::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage};
use crate::login::character::SHIP_MENU_ID;
use networking::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
use networking::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
use networking::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage};
use pktbuilder::ship::SHIP_MENU_ID;
use entity::gateway::{EntityGateway, GatewayError};
use entity::character::SectionID;
use entity::room::RoomNote;
use crate::ship::location::{ClientLocation, RoomLobby, ClientLocationError, RoomId};
use crate::ship::drops::DropTable;
use crate::ship::items;
use crate::ship::room;
use location::{ClientLocation, RoomLobby, ClientLocationError, RoomId};
use drops::DropTable;
use items;
use room;
use maps::Holiday;
use maps::area::MapAreaError;
use maps::maps::{Maps, MapsError, generate_free_roam_maps};
use maps::enemy::RareEnemyEvent;
use crate::ship::packet::handler;
use shops::{WeaponShop, ToolShop, ArmorShop};
use crate::ship::trade::TradeState;
use trade::TradeState;
use crate::ship::chatcommand;
use pktbuilder::quest::{QUEST_CATEGORY_MENU_ID, QUEST_SELECT_MENU_ID};
// TODO: remove once stuff settles down
pub use crate::ship::client::*;
pub use client::{Clients, ClientState};
pub const SHIP_PORT: u16 = 23423;
pub const QUEST_CATEGORY_MENU_ID: u32 = 0xA2;
pub const QUEST_SELECT_MENU_ID: u32 = 0xA3;
#[derive(Error, Debug)]
@ -100,7 +98,7 @@ pub enum ShipError {
#[error("trade error {0}")]
TradeError(#[from] crate::ship::packet::handler::trade::TradeError),
#[error("trade state error {0}")]
TradeStateError(#[from] crate::ship::trade::TradeStateError),
TradeStateError(#[from] trade::TradeStateError),
#[error("message error {0}")]
MessageError(#[from] crate::ship::packet::handler::direct_message::MessageError),
#[error("room creation error {0}")]
@ -117,69 +115,6 @@ impl<I: Into<ClientLocationError>> From<I> for ShipError {
}
*/
#[derive(Clone, Copy)]
pub enum ShipEvent {
None,
Christmas,
Valentines,
Easter,
Halloween,
Sonic,
NewYear,
Summer,
White,
Wedding,
Fall,
Spring,
Summer2,
Spring2,
}
impl From<ShipEvent> for u32 {
fn from(other: ShipEvent) -> u32 {
u16::from(other) as u32
}
}
impl From<ShipEvent> for u16 {
fn from(other: ShipEvent) -> u16 {
u8::from(other) as u16
}
}
impl From<ShipEvent> for u8 {
fn from(other: ShipEvent) -> u8 {
match other {
ShipEvent::None => 0,
ShipEvent::Christmas => 1,
ShipEvent::Valentines => 3,
ShipEvent::Easter => 4,
ShipEvent::Halloween => 5,
ShipEvent::Sonic => 6,
ShipEvent::NewYear => 7,
ShipEvent::Summer => 8,
ShipEvent::White => 9,
ShipEvent::Wedding => 10,
ShipEvent::Fall => 11,
ShipEvent::Spring => 12,
ShipEvent::Summer2 => 13,
ShipEvent::Spring2 => 14,
}
}
}
impl ShipEvent {
pub fn rare_enemy_event(&self) -> Option<RareEnemyEvent> {
match self {
ShipEvent::Easter => Some(RareEnemyEvent::Easter),
ShipEvent::Halloween => Some(RareEnemyEvent::Halloween),
ShipEvent::Christmas => Some(RareEnemyEvent::Christmas),
_ => None,
}
}
}
#[derive(Debug)]
@ -391,8 +326,8 @@ pub struct ShipServerStateBuilder<EG: EntityGateway + Clone + 'static> {
ip: Option<Ipv4Addr>,
port: Option<u16>,
auth_token: Option<AuthToken>,
event: Option<ShipEvent>,
map_builder: Option<Box<dyn Fn(maps::room::RoomMode, Option<RareEnemyEvent>) -> Maps + Send + Sync>>,
event: Option<Holiday>,
map_builder: Option<Box<dyn Fn(maps::room::RoomMode, Holiday) -> Maps + Send + Sync>>,
drop_table_builder: Option<Box<dyn Fn(maps::room::Episode, maps::room::Difficulty, SectionID) -> DropTable + Send + Sync>>,
num_blocks: usize,
}
@ -445,13 +380,13 @@ impl<EG: EntityGateway + Clone + 'static> ShipServerStateBuilder<EG> {
}
#[must_use]
pub fn event(mut self, event: ShipEvent) -> ShipServerStateBuilder<EG> {
pub fn event(mut self, event: Holiday) -> ShipServerStateBuilder<EG> {
self.event = Some(event);
self
}
#[must_use]
pub fn map_builder(mut self, map_builder: Box<dyn Fn(maps::room::RoomMode, Option<RareEnemyEvent>) -> Maps + Send + Sync>) -> ShipServerStateBuilder<EG> {
pub fn map_builder(mut self, map_builder: Box<dyn Fn(maps::room::RoomMode, Holiday) -> Maps + Send + Sync>) -> ShipServerStateBuilder<EG> {
self.map_builder = Some(map_builder);
self
}
@ -479,7 +414,7 @@ impl<EG: EntityGateway + Clone + 'static> ShipServerStateBuilder<EG> {
port: self.port.unwrap_or(SHIP_PORT),
shops: ItemShops::default(),
blocks: Blocks(blocks),
event: self.event.unwrap_or(ShipEvent::None),
event: self.event.unwrap_or(Holiday::None),
map_builder: Arc::new(self.map_builder.unwrap_or(Box::new(generate_free_roam_maps))),
drop_table_builder: Arc::new(self.drop_table_builder.unwrap_or(Box::new(DropTable::new))),
@ -520,7 +455,7 @@ pub struct ShipServerState<EG: EntityGateway + Clone + 'static> {
pub(crate) item_state: items::state::ItemState,
shops: ItemShops,
pub blocks: Blocks,
event: ShipEvent,
event: Holiday,
ip: Ipv4Addr,
port: u16,
@ -529,7 +464,7 @@ pub struct ShipServerState<EG: EntityGateway + Clone + 'static> {
ship_list: Arc<RwLock<Vec<Ship>>>,
shipgate_sender: Option<channel::Sender<ShipMessage>>,
trades: TradeState,
map_builder: Arc<Box<dyn Fn(maps::room::RoomMode, Option<RareEnemyEvent>) -> Maps + Send + Sync>>,
map_builder: Arc<Box<dyn Fn(maps::room::RoomMode, Holiday) -> Maps + Send + Sync>>,
drop_table_builder: Arc<Box<dyn Fn(maps::room::Episode, maps::room::Difficulty, SectionID) -> DropTable + Send + Sync>>,
}

4
tests/common.rs

@ -1,10 +1,10 @@
#![allow(dead_code)]
use elseware::common::serverstate::{ClientId, ServerState};
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::EntityGateway;
use entity::account::{UserAccountEntity, NewUserAccountEntity, NewUserSettingsEntity};
use entity::character::{CharacterEntity, NewCharacterEntity};
use entity::item::{Meseta, BankName, BankIdentifier};
use entity::item::{Meseta, BankIdentifier};
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
use maps::room::Difficulty;

2
tests/test_bank.rs

@ -1,5 +1,5 @@
use std::collections::BTreeSet;
use elseware::common::serverstate::{ClientId, ServerState};
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::{EntityGateway, InMemoryGateway};
use entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};

2
tests/test_character.rs

@ -1,4 +1,4 @@
use elseware::common::serverstate::{ClientId, ServerState};
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::ship::ship::{ShipServerState, RecvShipPacket};

5
tests/test_exp_gain.rs

@ -1,8 +1,7 @@
use elseware::common::serverstate::{ClientId, ServerState};
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::common::leveltable::CharacterLevelTable;
use stats::leveltable::CharacterLevelTable;
use elseware::ship::ship::{ShipServerState, SendShipPacket, RecvShipPacket};
use elseware::ship::location::RoomId;
use maps::variant::{MapVariant, MapVariantMode};
use maps::maps::Maps;
use maps::area::MapArea;

2
tests/test_item_actions.rs

@ -1,4 +1,4 @@
use elseware::common::serverstate::{ClientId, ServerState};
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
use entity::item;

12
tests/test_item_drop.rs

@ -1,13 +1,9 @@
use elseware::common::serverstate::{ClientId, ServerState};
use entity::gateway::{EntityGateway, InMemoryGateway};
use entity::character::SectionID;
use elseware::common::leveltable::CharacterLevelTable;
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::InMemoryGateway;
use elseware::ship::ship::{ShipServerState, SendShipPacket, RecvShipPacket};
use maps::room::{Episode, Difficulty};
use maps::monster::MonsterType;
use elseware::ship::location::RoomId;
use elseware::ship::drops::{DropTable, MonsterDropStats, MonsterDropType};
use elseware::ship::drops::rare_drop_table::{RareDropTable, RareDropRate, RareDropItem};
use drops::{DropTable, MonsterDropStats, MonsterDropType};
use drops::rare_drop_table::{RareDropTable, RareDropRate, RareDropItem};
use maps::maps::Maps;
use maps::area::MapArea;
use maps::variant::{MapVariant, MapVariantMode};

4
tests/test_item_id.rs

@ -1,9 +1,7 @@
use elseware::common::serverstate::{ClientId, ServerState};
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::{EntityGateway, InMemoryGateway};
use entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
use entity::character::TechLevel;
//use elseware::ship::items::{ClientItemId, ActiveItemEntityId, HeldItemType, FloorItemType};
use libpso::packet::ship::*;
use libpso::packet::messages::*;

2
tests/test_item_pickup.rs

@ -1,4 +1,4 @@
use elseware::common::serverstate::{ClientId, ServerState};
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::{EntityGateway, InMemoryGateway};
use entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket};

3
tests/test_item_use.rs

@ -1,9 +1,8 @@
use elseware::common::serverstate::{ClientId, ServerState};
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::{EntityGateway, InMemoryGateway};
use entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
use entity::character::TechLevel;
//use elseware::ship::items::{ClientItemId, ActiveItemEntityId, HeldItemType, FloorItemType};
use libpso::packet::ship::*;
use libpso::packet::messages::*;

2
tests/test_mags.rs

@ -1,4 +1,4 @@
use elseware::common::serverstate::{ClientId, ServerState};
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::{EntityGateway, InMemoryGateway};
use entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket};

4
tests/test_rooms.rs

@ -1,8 +1,6 @@
use elseware::common::serverstate::{ClientId, ServerState};
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::{EntityGateway, InMemoryGateway};
use entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};
use elseware::ship::location::RoomId;
use libpso::packet::ship::*;
//use libpso::packet::messages::*;

6
tests/test_shops.rs

@ -1,9 +1,9 @@
use elseware::common::serverstate::{ClientId, ServerState};
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::{EntityGateway, InMemoryGateway};
use entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError};
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};
use maps::room::Difficulty;
use elseware::ship::items::state::ItemStateError;
use items::state::ItemStateError;
use libpso::packet::ship::*;
use libpso::packet::messages::*;

2
tests/test_trade.rs

@ -1,5 +1,5 @@
use std::convert::TryInto;
use elseware::common::serverstate::{ClientId, ServerState};
use networking::serverstate::{ClientId, ServerState};
use entity::gateway::{EntityGateway, InMemoryGateway};
use entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError};

14
trade/Cargo.toml

@ -0,0 +1,14 @@
[package]
name = "trade"
version = "0.1.0"
edition = "2021"
[dependencies]
networking = { workspace = true }
items = { workspace = true }
async-std = { workspace = true }
futures = { workspace = true }
anyhow = { workspace = true }
thiserror = { workspace = true }

4
trade/src/lib.rs

@ -0,0 +1,4 @@
pub mod trade;
pub use trade::*;

42
src/ship/trade.rs → trade/src/trade.rs

@ -1,45 +1,9 @@
use std::collections::HashMap;
use crate::common::serverstate::ClientId;
use crate::ship::items;
use networking::serverstate::ClientId;
use items;
use async_std::sync::{Arc, Mutex, MutexGuard};
use futures::future::{Future, OptionFuture};
#[derive(Debug, Clone)]
pub enum TradeItem {
Individual(items::ClientItemId),
Stacked(items::ClientItemId, usize),
}
impl TradeItem {
pub fn stacked(&self) -> Option<(items::ClientItemId, usize)> {
match self {
TradeItem::Stacked(item_id, amount) => Some((*item_id, *amount)),
_ => None
}
}
pub fn stacked_mut(&mut self) -> Option<(items::ClientItemId, &mut usize)> {
match self {
TradeItem::Stacked(item_id, ref mut amount) => Some((*item_id, amount)),
_ => None
}
}
pub fn item_id(&self) -> items::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,
}
}
}
use items::trade::TradeItem;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TradeStatus {
Loading…
Cancel
Save