Compare commits
47 Commits
622c6e598d
...
master
Author | SHA1 | Date | |
---|---|---|---|
bdc5a5ae89 | |||
156821cc6e | |||
b18ab064fa | |||
c851818813 | |||
d4913eb6c6 | |||
8f9f2b074c | |||
a71692d62a | |||
6caeb0a8d9 | |||
96dc459e5e | |||
40f5c80e3e | |||
d30a2bd3a3 | |||
8138cb13a2 | |||
e359501be6 | |||
d71287ef9a | |||
d5ddfdb847 | |||
95d23fceac | |||
a7d6d9b5b0 | |||
aa84768e58 | |||
631a0c46c8 | |||
b42056419a | |||
5fc23cd7ca | |||
2cede0077a | |||
8360e0343e | |||
ab0a844c8c | |||
15c0ac50ee | |||
c8813866a5 | |||
1be61556ab | |||
3cd4e9db10 | |||
d0866f5e83 | |||
7b26fdc28d | |||
af629695a5 | |||
183fba9afe | |||
7afe44a520 | |||
3dc48e9d43 | |||
402667d627 | |||
2570749fbb | |||
9cff5ad088 | |||
d73a07391b | |||
926483b6de | |||
5d98fb8cc9 | |||
72aa0f7f13 | |||
3b28d650ee | |||
2c930d4333 | |||
6ac3ad50dc | |||
fc62be05e2 | |||
5bf9ef59bf | |||
fbeaf5e93e |
16
.drone.yml
16
.drone.yml
@ -1,12 +1,24 @@
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: test elseware
|
||||
name: test elseware
|
||||
|
||||
concurrency:
|
||||
limit: 1
|
||||
|
||||
environment:
|
||||
CARGO_INCREMENTAL: false
|
||||
|
||||
steps:
|
||||
- name: clean cache
|
||||
image: rustlang/rust:nightly
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /usr/local/cargo
|
||||
- name: target-cache
|
||||
path: /drone/src/target
|
||||
commands:
|
||||
- cargo prune
|
||||
- name: build
|
||||
image: rustlang/rust:nightly
|
||||
volumes:
|
||||
@ -33,7 +45,7 @@ steps:
|
||||
- name: target-cache
|
||||
path: /drone/src/target
|
||||
commands:
|
||||
- cargo test
|
||||
- cargo test --jobs 1
|
||||
|
||||
volumes:
|
||||
- name: cache
|
||||
|
98
Cargo.toml
98
Cargo.toml
@ -6,18 +6,44 @@ edition = "2021"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"entity",
|
||||
"maps",
|
||||
"common",
|
||||
"networking",
|
||||
"src/client",
|
||||
"src/drops",
|
||||
"src/entity",
|
||||
"src/items",
|
||||
"src/location",
|
||||
"src/maps",
|
||||
"src/networking",
|
||||
"src/pktbuilder",
|
||||
"src/quests",
|
||||
"src/room",
|
||||
"src/shops",
|
||||
"src/stats",
|
||||
"src/trade",
|
||||
"src/patch_server",
|
||||
"src/login_server",
|
||||
"src/ship_server",
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
libpso = { git = "http://git.sharnoth.com/jake/libpso" }
|
||||
entity = { path = "./entity" }
|
||||
maps = { path = "./maps" }
|
||||
common = { path = "./common" }
|
||||
networking = { path = "./networking" }
|
||||
entity = { path = "./src/entity" }
|
||||
maps = { path = "./src/maps" }
|
||||
networking = { path = "./src/networking" }
|
||||
shops = { path = "./src/shops" }
|
||||
stats = { path = "./src/stats" }
|
||||
items = { path = "./src/items" }
|
||||
pktbuilder = { path = "./src/pktbuilder" }
|
||||
quests = { path = "./src/quests" }
|
||||
location = { path = "./src/location" }
|
||||
client = { path = "./src/client" }
|
||||
drops = { path = "./src/drops" }
|
||||
trade = { path = "./src/trade" }
|
||||
room = { path = "./src/room" }
|
||||
patch_server = { path = "./src/patch_server" }
|
||||
login_server = { path = "./src/login_server" }
|
||||
ship_server = { path = "./src/ship_server" }
|
||||
|
||||
libpso = { git = "http://git.sharnoth.com/jake/libpso", rev="90246b6" }
|
||||
|
||||
async-std = { version = "1.9.0", features = ["unstable", "attributes"] }
|
||||
futures = "0.3.5"
|
||||
rand = "0.7.3"
|
||||
@ -39,7 +65,6 @@ ages-prs = "0.1"
|
||||
async-trait = "0.1.51"
|
||||
async-recursion= "1.0.0"
|
||||
lazy_static = "1.4.0"
|
||||
barrel = { version = "0.6.5", features = ["pg"] }
|
||||
refinery = { version = "0.5.0", features = ["postgres"] }
|
||||
sqlx = { version = "0.6.2", features = ["runtime-async-std-native-tls", "postgres", "json", "chrono"] }
|
||||
strum = "0.19.5"
|
||||
@ -47,35 +72,26 @@ strum_macros = "0.19"
|
||||
anyhow = { version = "1.0.68", features = ["backtrace"] }
|
||||
|
||||
[dependencies]
|
||||
libpso = { git = "http://git.sharnoth.com/jake/libpso" }
|
||||
entity = { path = "./entity" }
|
||||
maps = { path = "./maps" }
|
||||
common = { path = "./common" }
|
||||
networking = { path = "./networking" }
|
||||
async-std = { version = "1.9.0", features = ["unstable", "attributes"] }
|
||||
futures = "0.3.5"
|
||||
rand = "0.7.3"
|
||||
rand_chacha = "0.2.2"
|
||||
crc = "^1.0.0"
|
||||
bcrypt = "0.10"
|
||||
entity = { workspace = true }
|
||||
maps = { workspace = true }
|
||||
networking = { workspace = true }
|
||||
patch_server = { workspace = true }
|
||||
login_server = { workspace = true }
|
||||
ship_server = { workspace = true }
|
||||
|
||||
libpso = { workspace = true }
|
||||
|
||||
async-std = { workspace = true }
|
||||
bcrypt = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
serde = "*"
|
||||
serde_json = "*"
|
||||
ron = "*"
|
||||
toml = "*"
|
||||
log = "*"
|
||||
fern = { version = "0.5", features = ["colored"] }
|
||||
byteorder = "1"
|
||||
enum-utils = "0.1.2"
|
||||
derive_more = { version = "0.99.3", features = ["display"]}
|
||||
thiserror = "1.0.37"
|
||||
ages-prs = "0.1"
|
||||
async-trait = "0.1.51"
|
||||
async-recursion= "1.0.0"
|
||||
lazy_static = "1.4.0"
|
||||
barrel = { version = "0.6.5", features = ["pg"] }
|
||||
refinery = { version = "0.5.0", features = ["postgres"] }
|
||||
#sqlx = { version = "0.6.2", features = ["runtime-async-std-native-tls", "postgres", "json", "chrono"] }
|
||||
strum = "0.19.5"
|
||||
strum_macros = "0.19"
|
||||
anyhow = { workspace = true }
|
||||
fern = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
drops = { workspace = true }
|
||||
shops = { workspace = true }
|
||||
items = { workspace = true }
|
||||
quests = { workspace = true }
|
||||
stats = { workspace = true }
|
||||
async-trait = { workspace = true }
|
@ -1,8 +0,0 @@
|
||||
[package]
|
||||
name = "common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
[dependencies]
|
||||
derive_more = { workspace = true }
|
@ -1,57 +0,0 @@
|
||||
[package]
|
||||
name = "entity"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libpso = { workspace = true }
|
||||
maps = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
async-std = { workspace = true }
|
||||
sqlx = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
enum-utils = { workspace = true }
|
||||
derive_more = { workspace = true }
|
||||
refinery = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
|
||||
|
||||
#libpso = { git = "http://git.sharnoth.com/jake/libpso" }
|
||||
#async-std = { version = "1.9.0", features = ["unstable", "attributes"] }
|
||||
#futures = "0.3.5"
|
||||
#rand = "0.7.3"
|
||||
#rand_chacha = "0.2.2"
|
||||
#crc = "^1.0.0"
|
||||
#bcrypt = "0.10"
|
||||
#chrono = "0.4.11"
|
||||
#serde = "*"
|
||||
#serde_json = "*"
|
||||
#ron = "*"
|
||||
#toml = "*"
|
||||
#log = "*"
|
||||
#fern = { version = "0.5", features = ["colored"] }
|
||||
#byteorder = "1"
|
||||
#enum-utils = "0.1.2"
|
||||
#derive_more = { version = "0.99.3", features = ["display"]}
|
||||
#thiserror = "1.0.37"
|
||||
#ages-prs = "0.1"
|
||||
#async-trait = "0.1.51"
|
||||
#async-recursion= "1.0.0"
|
||||
#lazy_static = "1.4.0"
|
||||
#barrel = { version = "0.6.5", features = ["pg"] }
|
||||
#refinery = { version = "0.5.0", features = ["postgres"] }
|
||||
#sqlx = { version = "0.6.2", features = ["runtime-async-std-native-tls", "postgres", "json", "chrono"] }
|
||||
#strum = "0.19.5"
|
||||
#strum_macros = "0.19"
|
||||
#anyhow = { version = "1.0.68", features = ["backtrace"] }
|
||||
|
||||
#[lib]
|
||||
#name = "entity"
|
||||
#path = "lib.rs"
|
@ -1,7 +0,0 @@
|
||||
pub mod area;
|
||||
pub mod enemy;
|
||||
pub mod object;
|
||||
pub mod variant;
|
||||
pub mod maps;
|
||||
pub mod monster;
|
||||
pub mod room;
|
@ -1,4 +0,0 @@
|
||||
[package]
|
||||
name = "networking"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
@ -1,8 +1,8 @@
|
||||
use log::{info};
|
||||
use entity::gateway::postgres::PostgresGateway;
|
||||
use elseware::login::login::LoginServerState;
|
||||
use elseware::login::character::CharacterServerState;
|
||||
use elseware::common::interserver::AuthToken;
|
||||
use login_server::login::LoginServerState;
|
||||
use login_server::character::CharacterServerState;
|
||||
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, login_server::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, login_server::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, login_server::login::COMMUNICATION_PORT).await;
|
||||
});
|
||||
|
||||
info!("[auth/character] starting server");
|
||||
|
@ -1,14 +1,14 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use log::{info};
|
||||
|
||||
use elseware::common::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 networking::interserver::AuthToken;
|
||||
use login_server::login::LoginServerState;
|
||||
use login_server::character::CharacterServerState;
|
||||
use patch_server::{PatchServerState, generate_patch_tree, load_config, load_motd};
|
||||
use ship_server::ShipServerStateBuilder;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use entity::gateway::{EntityGateway, InMemoryGateway, PostgresGateway};
|
||||
use maps::Holiday;
|
||||
use entity::gateway::{EntityGateway, InMemoryGateway};
|
||||
use entity::account::{NewUserAccountEntity, NewUserSettingsEntity};
|
||||
use entity::character::NewCharacterEntity;
|
||||
use entity::item::{NewItemEntity, ItemDetail, InventoryItemEntity};
|
||||
@ -338,73 +338,73 @@ 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, login_server::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, login_server::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, login_server::login::COMMUNICATION_PORT).await;
|
||||
});
|
||||
|
||||
info!("[ship] starting servers");
|
||||
let ship_state = ShipServerStateBuilder::default()
|
||||
.name("US/Sona-Nyl".into())
|
||||
.ip(Ipv4Addr::new(127,0,0,1))
|
||||
.port(elseware::ship::ship::SHIP_PORT)
|
||||
.event(ShipEvent::Halloween)
|
||||
.port(ship_server::SHIP_PORT)
|
||||
.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, ship_server::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), login_server::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)
|
||||
.port(ship_server::SHIP_PORT+2000)
|
||||
.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, ship_server::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), login_server::login::COMMUNICATION_PORT).await;
|
||||
});
|
||||
|
||||
let ship_state = ShipServerStateBuilder::default()
|
||||
.name("JP/Thalarion".into())
|
||||
.ip(Ipv4Addr::new(127,0,0,1))
|
||||
.port(elseware::ship::ship::SHIP_PORT+3000)
|
||||
.port(ship_server::SHIP_PORT+3000)
|
||||
.gateway(entity_gateway.clone())
|
||||
.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, ship_server::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), login_server::login::COMMUNICATION_PORT).await;
|
||||
});
|
||||
|
||||
futures::future::join_all(vec![patch_loop, login_loop, character_loop, inter_character_loop,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config_env, load_motd};
|
||||
use log::{info};
|
||||
use patch_server::{PatchServerState, generate_patch_tree, load_config_env, load_motd};
|
||||
use log::info;
|
||||
|
||||
fn main() {
|
||||
info!("[patch] starting server");
|
||||
@ -9,10 +9,8 @@ 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(async move {
|
||||
patch_loop.await
|
||||
});
|
||||
async_std::task::block_on(patch_loop);
|
||||
}
|
||||
|
@ -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 ship_server::ShipServerStateBuilder;
|
||||
use networking::interserver::AuthToken;
|
||||
|
||||
fn main() {
|
||||
let colors = fern::colors::ColoredLevelConfig::new()
|
||||
@ -40,7 +40,7 @@ fn main() {
|
||||
let ship_state = ShipServerStateBuilder::default()
|
||||
.name(ship_name)
|
||||
.ip(ip)
|
||||
.port(elseware::ship::ship::SHIP_PORT)
|
||||
.port(ship_server::SHIP_PORT)
|
||||
.gateway(entity_gateway)
|
||||
.auth_token(AuthToken(shipgate_token))
|
||||
.build();
|
||||
@ -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, ship_server::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, login_server::login::COMMUNICATION_PORT).await;
|
||||
});
|
||||
|
||||
info!("[auth/character] starting server");
|
||||
|
20
src/client/Cargo.toml
Normal file
20
src/client/Cargo.toml
Normal file
@ -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 }
|
@ -6,15 +6,20 @@ 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 maps::area::MapArea;
|
||||
use crate::ship::shops::{WeaponShopItem, ToolShopItem, ArmorShopItem};
|
||||
use shops::{WeaponShopItem, ToolShopItem, ArmorShopItem};
|
||||
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ClientError {
|
||||
#[error("not found {0}")]
|
||||
NotFound(ClientId),
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@ -46,7 +51,7 @@ impl Clients {
|
||||
.await;
|
||||
let client = clients
|
||||
.get(&client_id)
|
||||
.ok_or_else(|| ShipError::ClientNotFound(client_id))?
|
||||
.ok_or(ClientError::NotFound(client_id))?
|
||||
.read()
|
||||
.await;
|
||||
|
||||
@ -69,17 +74,14 @@ 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(ClientError::NotFound(*client_id))?
|
||||
.read()
|
||||
.await;
|
||||
client_states[cindex].write(c);
|
||||
}
|
||||
|
||||
let client_states = unsafe {
|
||||
// TODO: this should just be a normal transmute but due to compiler limitations it
|
||||
// does not yet work with const generics
|
||||
// https://github.com/rust-lang/rust/issues/61956
|
||||
std::mem::transmute_copy::<_, [RwLockReadGuard<ClientState>; N]>(&client_states)
|
||||
std::mem::transmute_copy(&client_states)
|
||||
};
|
||||
|
||||
Ok(func(client_states).await)
|
||||
@ -95,7 +97,7 @@ impl Clients {
|
||||
.await;
|
||||
let mut client = clients
|
||||
.get(&client_id)
|
||||
.ok_or_else(|| ShipError::ClientNotFound(client_id))?
|
||||
.ok_or(ClientError::NotFound(client_id))?
|
||||
.write()
|
||||
.await;
|
||||
|
@ -1,19 +0,0 @@
|
||||
pub mod cipherkeys;
|
||||
pub mod serverstate;
|
||||
pub mod mainloop;
|
||||
pub mod leveltable;
|
||||
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
|
||||
}
|
||||
)
|
||||
);
|
17
src/drops/Cargo.toml
Normal file
17
src/drops/Cargo.toml
Normal file
@ -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 }
|
@ -5,15 +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 entity::item::ItemDetail;
|
||||
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 {
|
||||
@ -176,8 +175,8 @@ impl BoxDropTable {
|
||||
fn random_box_drop<R: Rng>(&self, map_area: &MapArea, rng: &mut R) -> Option<ItemDropType> {
|
||||
self.rare_drop(map_area, rng).or_else(|| {
|
||||
let rate = self.box_rates.rates_by_area(map_area);
|
||||
let type_weights = WeightedIndex::new(&[rate.weapon_rate, rate.armor_rate, rate.shield_rate, rate.unit_rate,
|
||||
rate.tool_rate, rate.meseta_rate, rate.nothing_rate]).unwrap();
|
||||
let type_weights = WeightedIndex::new([rate.weapon_rate, rate.armor_rate, rate.shield_rate, rate.unit_rate,
|
||||
rate.tool_rate, rate.meseta_rate, rate.nothing_rate]).unwrap();
|
||||
let btype = type_weights.sample(rng);
|
||||
match btype {
|
||||
0 => self.weapon_table.get_drop(map_area, rng),
|
@ -7,8 +7,8 @@ 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::ship::item_stats::{armor_stats, ArmorStats};
|
||||
use crate::{ItemDropType, load_data_file};
|
||||
use stats::items::{armor_stats, ArmorStats};
|
||||
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@ -46,8 +46,8 @@ impl GenericArmorTable {
|
||||
}
|
||||
|
||||
fn armor_type<R: Rng>(&self, area_map: &MapArea, rng: &mut R) -> ArmorType {
|
||||
let rank_weights = WeightedIndex::new(&[self.rank_rates.rank0, self.rank_rates.rank1, self.rank_rates.rank2,
|
||||
self.rank_rates.rank3, self.rank_rates.rank4]).unwrap();
|
||||
let rank_weights = WeightedIndex::new([self.rank_rates.rank0, self.rank_rates.rank1, self.rank_rates.rank2,
|
||||
self.rank_rates.rank3, self.rank_rates.rank4]).unwrap();
|
||||
let rank = rank_weights.sample(rng) as i32;
|
||||
let armor_level = std::cmp::max(0i32, self.armor_set as i32 - 3i32 + rank + area_map.drop_area_value().unwrap_or(0) as i32);
|
||||
match armor_level {
|
||||
@ -80,8 +80,8 @@ impl GenericArmorTable {
|
||||
}
|
||||
|
||||
pub fn slots<R: Rng>(&self, _area_map: &MapArea, rng: &mut R) -> usize {
|
||||
let slot_weights = WeightedIndex::new(&[self.slot_rates.slot0, self.slot_rates.slot1, self.slot_rates.slot2,
|
||||
self.slot_rates.slot3, self.slot_rates.slot4]).unwrap();
|
||||
let slot_weights = WeightedIndex::new([self.slot_rates.slot0, self.slot_rates.slot1, self.slot_rates.slot2,
|
||||
self.slot_rates.slot3, self.slot_rates.slot4]).unwrap();
|
||||
slot_weights.sample(rng)
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ 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::ship::item_stats::{shield_stats, ShieldStats};
|
||||
use crate::{ItemDropType, load_data_file};
|
||||
use stats::items::{shield_stats, ShieldStats};
|
||||
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@ -36,8 +36,8 @@ impl GenericShieldTable {
|
||||
}
|
||||
|
||||
fn shield_type<R: Rng>(&self, area_map: &MapArea, rng: &mut R) -> ShieldType {
|
||||
let rank_weights = WeightedIndex::new(&[self.rank_rates.rank0, self.rank_rates.rank1, self.rank_rates.rank2,
|
||||
self.rank_rates.rank3, self.rank_rates.rank4]).unwrap();
|
||||
let rank_weights = WeightedIndex::new([self.rank_rates.rank0, self.rank_rates.rank1, self.rank_rates.rank2,
|
||||
self.rank_rates.rank3, self.rank_rates.rank4]).unwrap();
|
||||
let rank = rank_weights.sample(rng) as i32;
|
||||
let shield_level = std::cmp::max(0i32, self.shield_set as i32 - 3i32 + rank + area_map.drop_area_value().unwrap_or(0) as i32);
|
||||
match shield_level {
|
@ -7,8 +7,8 @@ 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::ship::item_stats::{unit_stats, UnitStats};
|
||||
use crate::{ItemDropType, load_data_file};
|
||||
use stats::items::{unit_stats, UnitStats};
|
||||
|
||||
|
||||
|
@ -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};
|
||||
|
||||
|
||||
|
||||
@ -240,7 +240,7 @@ impl AttributeTable {
|
||||
|
||||
|
||||
fn generate_attribute<R: Rng>(&self, pattern: &PercentPatternType, rates: &AttributeRate, rng: &mut R) -> Option<WeaponAttribute> {
|
||||
let attribute_weights = WeightedIndex::new(&[rates.none, rates.native, rates.abeast, rates.machine, rates.dark, rates.hit]).unwrap();
|
||||
let attribute_weights = WeightedIndex::new([rates.none, rates.native, rates.abeast, rates.machine, rates.dark, rates.hit]).unwrap();
|
||||
let attr = match attribute_weights.sample(rng) {
|
||||
0 => return None,
|
||||
1 => Attribute::Native,
|
||||
@ -253,7 +253,7 @@ impl AttributeTable {
|
||||
|
||||
let percents = self.percent_rates.get_by_pattern(pattern);
|
||||
|
||||
let value_weights = WeightedIndex::new(&percents.as_array()).unwrap();
|
||||
let value_weights = WeightedIndex::new(percents.as_array()).unwrap();
|
||||
let value = value_weights.sample(rng);
|
||||
let percent = ((value + 1) * 5) as i8;
|
||||
|
||||
@ -477,7 +477,7 @@ impl GenericWeaponTable {
|
||||
let pattern = std::cmp::min(area % ratio.inc, 3);
|
||||
|
||||
let weights = self.grind_rates.grind_rate[pattern as usize];
|
||||
let grind_choice = WeightedIndex::new(&weights).unwrap();
|
||||
let grind_choice = WeightedIndex::new(weights).unwrap();
|
||||
grind_choice.sample(rng)
|
||||
}
|
||||
|
@ -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};
|
||||
|
||||
@ -99,7 +98,7 @@ impl ItemDropType {
|
||||
.or_else(|_| mag::MagType::parse_type([data[0],data[1],data[2]]).map(ItemType::Mag))
|
||||
.or_else(|_| tool::ToolType::parse_type([data[0],data[1],data[2]]).map(ItemType::Tool))
|
||||
.or_else(|_| esweapon::ESWeaponType::parse_type([data[0],data[1],data[2]]).map(ItemType::ESWeapon)).ok()?;
|
||||
|
||||
|
||||
match item_type {
|
||||
ItemType::Weapon(_w) => Some(ItemDropType::Weapon(weapon::Weapon::from_bytes(data).ok()?)),
|
||||
ItemType::Armor(_a) => Some(ItemDropType::Armor(armor::Armor::from_bytes(data).ok()?)),
|
||||
@ -122,7 +121,12 @@ pub struct ItemDrop {
|
||||
}
|
||||
|
||||
|
||||
pub struct DropTable {
|
||||
pub trait DropTable {
|
||||
fn get_drop(&mut self, map_area: &MapArea, monster: &MonsterType) -> Option<ItemDropType>;
|
||||
fn get_box_drop(&mut self, map_area: &MapArea, object: &MapObject) -> Option<ItemDropType>;
|
||||
}
|
||||
|
||||
pub struct StandardDropTable {
|
||||
monster_stats: HashMap<MonsterType, MonsterDropStats>,
|
||||
rare_table: RareDropTable,
|
||||
weapon_table: GenericWeaponTable,
|
||||
@ -134,11 +138,12 @@ pub struct DropTable {
|
||||
rng: rand_chacha::ChaCha20Rng,
|
||||
}
|
||||
|
||||
impl DropTable {
|
||||
pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> DropTable {
|
||||
impl StandardDropTable {
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> Box<dyn DropTable + Send + Sync> {
|
||||
let monster_stats: HashMap<String, MonsterDropStats> = load_data_file(episode, difficulty, section_id, "monster_dar.toml");
|
||||
|
||||
DropTable {
|
||||
|
||||
Box::new(StandardDropTable {
|
||||
monster_stats: monster_stats.into_iter().map(|(m, s)| (m.parse().unwrap(), s)).collect(),
|
||||
rare_table: RareDropTable::new(episode, difficulty, section_id),
|
||||
weapon_table: GenericWeaponTable::new(episode, difficulty, section_id),
|
||||
@ -148,7 +153,7 @@ impl DropTable {
|
||||
tool_table: ToolTable::new(episode, difficulty, section_id),
|
||||
box_table: BoxDropTable::new(episode, difficulty, section_id),
|
||||
rng: rand_chacha::ChaCha20Rng::from_entropy(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn builder() -> DropTableBuilder {
|
||||
@ -178,8 +183,10 @@ impl DropTable {
|
||||
MonsterDropType::None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_drop(&mut self, map_area: &MapArea, monster: &MonsterType) -> Option<ItemDropType> {
|
||||
impl DropTable for StandardDropTable {
|
||||
fn get_drop(&mut self, map_area: &MapArea, monster: &MonsterType) -> Option<ItemDropType> {
|
||||
let monster_stat = *self.monster_stats.get(monster)?;
|
||||
|
||||
let drop_anything = self.rng.gen_range(0, 100);
|
||||
@ -192,7 +199,7 @@ impl DropTable {
|
||||
}
|
||||
|
||||
let drop_type = self.rng.gen_range(0, 3);
|
||||
|
||||
|
||||
match drop_type {
|
||||
0 => {
|
||||
self.generate_meseta(&monster_stat)
|
||||
@ -207,7 +214,7 @@ impl DropTable {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_box_drop(&mut self, map_area: &MapArea, object: &MapObject) -> Option<ItemDropType> {
|
||||
fn get_box_drop(&mut self, map_area: &MapArea, object: &MapObject) -> Option<ItemDropType> {
|
||||
self.box_table.get_drop(map_area, object, &mut self.rng)
|
||||
}
|
||||
}
|
||||
@ -254,8 +261,8 @@ impl DropTableBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self, episode: Episode, difficulty: Difficulty, section_id: SectionID) -> DropTable {
|
||||
DropTable {
|
||||
pub fn build(self, episode: Episode, difficulty: Difficulty, section_id: SectionID) -> Box<dyn DropTable + Send + Sync> {
|
||||
Box::new(StandardDropTable {
|
||||
monster_stats: self.monster_stats.unwrap_or_else(|| {
|
||||
let monster_stats: HashMap<String, MonsterDropStats> = load_data_file(episode, difficulty, section_id, "monster_dar.toml");
|
||||
monster_stats.into_iter().map(|(m, s)| (m.parse().unwrap(), s)).collect()
|
||||
@ -268,11 +275,10 @@ impl DropTableBuilder {
|
||||
tool_table: self.tool_table.unwrap_or_else(|| ToolTable::new(episode, difficulty, section_id)),
|
||||
box_table: self.box_table.unwrap_or_else(|| BoxDropTable::new(episode, difficulty, section_id)),
|
||||
rng: self.rng.unwrap_or_else(rand_chacha::ChaCha20Rng::from_entropy),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
@ -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>>;
|
||||
|
@ -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};
|
||||
|
||||
|
||||
|
@ -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)]
|
23
src/entity/Cargo.toml
Normal file
23
src/entity/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "entity"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libpso = { workspace = true }
|
||||
maps = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
async-std = { workspace = true }
|
||||
sqlx = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
enum-utils = { workspace = true }
|
||||
derive_more = { workspace = true }
|
||||
refinery = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
toml = { workspace = true }
|
@ -223,7 +223,7 @@ impl CharacterInfoboard {
|
||||
}
|
||||
|
||||
pub fn update_infoboard(&mut self, new_board: &WriteInfoboard) {
|
||||
self.board = libpso::utf8_to_utf16_array!(new_board.message, 172);
|
||||
self.board = libpso::util::utf8_to_utf16_array(&new_board.message);
|
||||
}
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ impl From<PgCharacter> for CharacterEntity {
|
||||
raw_data: vec_to_array(other.config)
|
||||
},
|
||||
info_board: CharacterInfoboard {
|
||||
board: libpso::utf8_to_utf16_array!(other.infoboard, 172),
|
||||
board: libpso::util::utf8_to_utf16_array(other.infoboard),
|
||||
},
|
||||
guildcard: CharacterGuildCard {
|
||||
description: other.guildcard,
|
25
src/items/Cargo.toml
Normal file
25
src/items/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "items"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
entity = { workspace = true }
|
||||
maps = { workspace = true }
|
||||
shops = { workspace = true }
|
||||
location = { workspace = true }
|
||||
drops = { workspace = true }
|
||||
|
||||
libpso = { workspace = true }
|
||||
|
||||
enum-utils = { workspace = true }
|
||||
derive_more = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
rand_chacha = { workspace = true }
|
||||
async-recursion = { workspace = true }
|
||||
async-std = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
thiserror = { workspace = true }
|
@ -1,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;
|
||||
@ -8,23 +8,20 @@ use std::pin::Pin;
|
||||
use std::iter::IntoIterator;
|
||||
use anyhow::Context;
|
||||
|
||||
use libpso::packet::{ship::Message, messages::GameMessage};
|
||||
use entity::character::{CharacterEntity, CharacterEntityId};
|
||||
use entity::gateway::{EntityGateway, EntityGatewayTransaction};
|
||||
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::ship::shops::ShopItem;
|
||||
use crate::ship::drops::{ItemDrop, ItemDropType};
|
||||
use crate::ship::packet::builder;
|
||||
use crate::ship::location::AreaClient;
|
||||
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 drops::{ItemDrop, ItemDropType};
|
||||
use location::AreaClient;
|
||||
use maps::monster::MonsterType;
|
||||
|
||||
pub enum TriggerCreateItem {
|
||||
@ -32,6 +29,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 +1147,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 +1164,7 @@ 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 = CreateItem::Stacked(area_client, item_id, tool, 1);
|
||||
let item_detail = StackedItemDetail {
|
||||
entity_ids: vec![new_item.id],
|
||||
tool
|
||||
@ -1173,7 +1176,7 @@ 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 = CreateItem::Individual(area_client, item_id, item_detail.clone());
|
||||
(InventoryItemDetail::Individual(item_detail), create_item)
|
||||
};
|
||||
|
||||
@ -1187,7 +1190,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()
|
@ -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)]
|
||||
@ -226,7 +226,7 @@ pub async fn liberta_kit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell:
|
||||
|
||||
fn jack_o_lantern() -> Result<Vec<ApplyItemAction>, anyhow::Error>
|
||||
{
|
||||
let mag_rate = WeightedIndex::new(&[13, 13, 13, 13, 12, 12, 12, 12]).unwrap();
|
||||
let mag_rate = WeightedIndex::new([13, 13, 13, 13, 12, 12, 12, 12]).unwrap();
|
||||
let mag_type = match mag_rate.sample(&mut rand_chacha::ChaChaRng::from_entropy()) {
|
||||
0 => ToolType::CellOfMag502,
|
||||
1 => ToolType::CellOfMag213,
|
@ -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)]
|
||||
@ -325,15 +325,7 @@ impl std::cmp::Eq for BankItemDetail {}
|
||||
|
||||
impl std::cmp::PartialOrd for BankItemDetail {
|
||||
fn partial_cmp(&self, other: &BankItemDetail) -> Option<std::cmp::Ordering> {
|
||||
let mut self_bytes = [0u8; 4];
|
||||
let mut other_bytes = [0u8; 4];
|
||||
self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]);
|
||||
other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]);
|
||||
|
||||
let self_value = u32::from_be_bytes(self_bytes);
|
||||
let other_value = u32::from_be_bytes(other_bytes);
|
||||
|
||||
self_value.partial_cmp(&other_value)
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
@ -344,8 +336,8 @@ impl std::cmp::Ord for BankItemDetail {
|
||||
self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]);
|
||||
other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]);
|
||||
|
||||
let self_value = u32::from_le_bytes(self_bytes);
|
||||
let other_value = u32::from_le_bytes(other_bytes);
|
||||
let self_value = u32::from_be_bytes(self_bytes);
|
||||
let other_value = u32::from_be_bytes(other_bytes);
|
||||
|
||||
self_value.cmp(&other_value)
|
||||
}
|
||||
@ -362,7 +354,7 @@ impl std::cmp::Eq for BankItem {}
|
||||
|
||||
impl std::cmp::PartialOrd for BankItem {
|
||||
fn partial_cmp(&self, other: &BankItem) -> Option<std::cmp::Ordering> {
|
||||
self.item.partial_cmp(&other.item)
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
@ -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};
|
||||
@ -9,10 +9,10 @@ use entity::character::CharacterEntityId;
|
||||
use entity::item::tool::ToolType;
|
||||
use entity::item::mag::Mag;
|
||||
use entity::item::weapon::Weapon;
|
||||
use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem};
|
||||
use crate::ship::items::state::ItemStateError;
|
||||
use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult};
|
||||
use crate::ship::items::floor::{FloorItem, FloorItemDetail};
|
||||
use shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem};
|
||||
use crate::state::ItemStateError;
|
||||
use crate::state::{IndividualItemDetail, StackedItemDetail, AddItemResult};
|
||||
use crate::floor::{FloorItem, FloorItemDetail};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum InventoryItemDetail {
|
@ -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);
|
@ -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}")]
|
@ -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::ship::shops::ShopItem;
|
||||
use crate::ship::trade::TradeItem;
|
||||
use crate::ship::location::AreaClient;
|
||||
use crate::ship::drops::ItemDrop;
|
||||
use crate::state::{ItemState, ItemStateProxy, IndividualItemDetail};
|
||||
use crate::itemstateaction::{ItemStateAction, ItemAction};
|
||||
use crate::inventory::InventoryItem;
|
||||
use crate::floor::FloorItem;
|
||||
use shops::ShopItem;
|
||||
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,
|
||||
{
|
||||
@ -372,6 +371,8 @@ where
|
||||
Ok((transaction, result))
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn trade_items<'a, EG> (
|
||||
item_state: &'a mut ItemState,
|
||||
entity_gateway: &'a mut EG,
|
38
src/items/src/trade.rs
Normal file
38
src/items/src/trade.rs
Normal file
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
16
src/lib.rs
16
src/lib.rs
@ -1,16 +0,0 @@
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(inline_const)]
|
||||
#![feature(extract_if)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(test)]
|
||||
#![feature(error_generic_member_access)]
|
||||
#![feature(lazy_cell)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
pub mod common;
|
||||
//pub mod entity;
|
||||
pub mod patch;
|
||||
pub mod login;
|
||||
pub mod ship;
|
12
src/location/Cargo.toml
Normal file
12
src/location/Cargo.toml
Normal file
@ -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 }
|
@ -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};
|
@ -1,3 +0,0 @@
|
||||
#[allow(clippy::module_inception)]
|
||||
pub mod login;
|
||||
pub mod character;
|
@ -1,92 +0,0 @@
|
||||
use std::time::SystemTime;
|
||||
use std::io::Write;
|
||||
//use diesel::sql_types::Timestamp;
|
||||
use diesel::{Insertable, Queryable, Identifiable, Associations, AsExpression, FromSqlRow};
|
||||
//use bcrypt::{DEFAULT_COST, hash};
|
||||
use diesel::pg::Pg;
|
||||
use diesel::sql_types;
|
||||
use diesel::deserialize::{self, FromSql};
|
||||
use diesel::serialize::{self, ToSql, Output, IsNull};
|
||||
use diesel::backend::Backend;
|
||||
|
||||
use libpso::character::settings;
|
||||
|
||||
use elseware::schema::*;
|
||||
|
||||
//const ELSEWHERE_COST: u32 = bcrypt::DEFAULT_COST;
|
||||
const ELSEWHERE_COST: u32 = 5;
|
||||
|
||||
#[derive(Debug, AsExpression, FromSqlRow)]
|
||||
#[sql_type="sql_types::Binary"]
|
||||
pub struct EUserSettings(pub settings::UserSettings);
|
||||
|
||||
impl std::ops::Deref for EUserSettings {
|
||||
type Target = settings::UserSettings;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Queryable, Identifiable, Debug)]
|
||||
pub struct UserAccount {
|
||||
pub id: i32,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub guildcard: Option<i32>,
|
||||
pub team_id: Option<i32>,
|
||||
pub banned: bool,
|
||||
pub muted_until: SystemTime,
|
||||
pub created_at: SystemTime,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[table_name="user_accounts"]
|
||||
pub struct NewUser {
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
impl NewUser {
|
||||
pub fn new(username: String, password: String) -> NewUser {
|
||||
let crypt_password = bcrypt::hash(password, ELSEWHERE_COST).expect("could not hash password?");
|
||||
NewUser {
|
||||
username: username,
|
||||
password: crypt_password,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Queryable, Identifiable, Associations)]
|
||||
#[belongs_to(UserAccount, foreign_key="user_id")]
|
||||
#[table_name="user_settings"]
|
||||
pub struct UserSettings {
|
||||
pub id: i32,
|
||||
pub user_id: i32,
|
||||
//settings: Vec<u8>,
|
||||
pub settings: EUserSettings,
|
||||
}
|
||||
|
||||
#[derive(Insertable, Debug)]
|
||||
#[table_name="user_settings"]
|
||||
pub struct NewUserSettings {
|
||||
pub user_id: i32,
|
||||
pub settings: EUserSettings,
|
||||
}
|
||||
|
||||
impl ToSql<sql_types::Binary, Pg> for EUserSettings {
|
||||
fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
|
||||
out.write_all(&self.0.as_bytes()[..])
|
||||
.map(|_| IsNull::No)
|
||||
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<sql_types::Binary, Pg> for EUserSettings {
|
||||
fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
|
||||
let bytes_vec: Vec<u8> = <Vec<u8> as FromSql<sql_types::Binary, Pg>>::from_sql(bytes)?;
|
||||
let mut static_bytes = [0u8; 0x1160];
|
||||
static_bytes[..0x1160].clone_from_slice(&bytes_vec);
|
||||
Ok(EUserSettings(settings::UserSettings::from_bytes(static_bytes)))
|
||||
}
|
||||
}
|
21
src/login_server/Cargo.toml
Normal file
21
src/login_server/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "login_server"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
entity = { workspace = true }
|
||||
networking = { workspace = true }
|
||||
pktbuilder = { workspace = true }
|
||||
stats = { workspace = true }
|
||||
|
||||
libpso = { workspace = true }
|
||||
|
||||
async-std = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
bcrypt = { workspace = true }
|
||||
crc = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
rand= { workspace = true }
|
@ -15,11 +15,11 @@ 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 crate::common::leveltable::LEVEL_TABLE;
|
||||
use libpso::{utf8_to_array, utf8_to_utf16_array};
|
||||
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::util::{utf8_to_array, utf8_to_utf16_array};
|
||||
|
||||
use entity::gateway::{EntityGateway, GatewayError};
|
||||
use entity::account::{UserAccountId, UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
|
||||
@ -31,11 +31,12 @@ use entity::item::tool::Tool;
|
||||
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 crate::login::get_login_status;
|
||||
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 {
|
||||
@ -149,7 +150,7 @@ fn generate_param_data(path: &str) -> (ParamDataHeader, Vec<u8>) {
|
||||
size: len as u32,
|
||||
checksum: crc.sum32(),
|
||||
offset: buffer.len() as u32,
|
||||
filename: utf8_to_array!(param.file_name().unwrap().to_str().unwrap(), 0x40),
|
||||
filename: utf8_to_array(param.file_name().unwrap().to_str().unwrap()),
|
||||
});
|
||||
|
||||
buffer.append(&mut filebuf);
|
||||
@ -176,7 +177,7 @@ impl ClientState {
|
||||
user: None,
|
||||
characters: None,
|
||||
guildcard_data_buffer: None,
|
||||
session: Session::new(),
|
||||
session: Session::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -341,15 +342,15 @@ impl<EG: EntityGateway + Clone> CharacterServerState<EG> {
|
||||
if let Some(connected_client) = self.connected_clients.read().await.get(&user.id) {
|
||||
if let Some(expires) = connected_client.expires {
|
||||
if expires > chrono::Utc::now() {
|
||||
return Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(AccountStatus::AlreadyOnline, Session::new()))]);
|
||||
return Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(AccountStatus::AlreadyOnline, Session::default()))]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(AccountStatus::AlreadyOnline, Session::new()))]);
|
||||
return Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(AccountStatus::AlreadyOnline, Session::default()))]);
|
||||
}
|
||||
}
|
||||
|
||||
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
|
||||
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::default());
|
||||
response.guildcard = user.guildcard;
|
||||
response.team_id = user.team_id.map_or(0, |ti| ti);
|
||||
|
||||
@ -366,7 +367,7 @@ impl<EG: EntityGateway + Clone> CharacterServerState<EG> {
|
||||
Ok(vec![SendCharacterPacket::LoginResponse(response)])
|
||||
},
|
||||
Err(err) => {
|
||||
Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(err, Session::new()))])
|
||||
Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(err, Session::default()))])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -378,7 +379,7 @@ impl<EG: EntityGateway + Clone> CharacterServerState<EG> {
|
||||
menu: SHIP_MENU_ID,
|
||||
item: i.0 as u32,
|
||||
flags: 0,
|
||||
name: utf8_to_utf16_array!(s.name, 0x11)
|
||||
name: utf8_to_utf16_array(&s.name)
|
||||
}
|
||||
}).collect()))
|
||||
])
|
||||
@ -823,7 +824,7 @@ impl<'a> SelectScreenCharacterBuilder<'a> {
|
||||
hair_b: character.appearance.hair_b,
|
||||
prop_x: character.appearance.prop_x,
|
||||
prop_y: character.appearance.prop_y,
|
||||
name: utf8_to_utf16_array!(character.name, 16),
|
||||
name: utf8_to_utf16_array(&character.name),
|
||||
play_time: character.playtime,
|
||||
..character::SelectScreenCharacter::default()
|
||||
}
|
2
src/login_server/src/lib.rs
Normal file
2
src/login_server/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod login;
|
||||
pub mod character;
|
@ -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};
|
||||
@ -83,21 +83,13 @@ pub async fn get_login_status(entity_gateway: &mut impl EntityGateway, pkt: &Log
|
||||
|
||||
pub fn check_if_already_online(user: UserAccountEntity) -> Result<UserAccountEntity, AccountStatus> {
|
||||
Ok(user)
|
||||
/*
|
||||
if user.is_currently_online() {
|
||||
Err(AccountStatus::PayUp)
|
||||
}
|
||||
else {
|
||||
Ok(user)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LoginServerState<EG: EntityGateway + Clone> {
|
||||
character_server_ip: net::Ipv4Addr,
|
||||
entity_gateway: EG,
|
||||
clients: HashMap<ClientId, String>,
|
||||
clients: HashMap<ClientId, String>, // TODO: this should be arc/mutex'd?
|
||||
}
|
||||
|
||||
impl<EG: EntityGateway + Clone> LoginServerState<EG> {
|
||||
@ -119,7 +111,7 @@ impl<EG: EntityGateway + Clone> LoginServerState<EG> {
|
||||
let response = SendLoginPacket::LoginResponse(LoginResponse::by_status(AccountStatus::Ok, pkt.session));
|
||||
let ip = u32::from_ne_bytes(self.character_server_ip.octets());
|
||||
Ok(vec![response,
|
||||
SendLoginPacket::RedirectClient(RedirectClient::new(ip, crate::login::character::CHARACTER_PORT))])
|
||||
SendLoginPacket::RedirectClient(RedirectClient::new(ip, crate::character::CHARACTER_PORT))])
|
||||
},
|
||||
Err(err) => {
|
||||
Ok(vec![SendLoginPacket::LoginResponse(LoginResponse::by_status(err, pkt.session))])
|
@ -4,7 +4,6 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
common = { workspace = true }
|
||||
byteorder = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
thiserror = { workspace = true }
|
@ -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,
|
||||
@ -81,7 +75,7 @@ pub fn load_rare_monster_file<T: serde::de::DeserializeOwned>(episode: Episode)
|
||||
|
||||
let mut f = File::open(path).unwrap();
|
||||
let mut s = String::new();
|
||||
f.read_to_string(&mut s);
|
||||
f.read_to_string(&mut s).unwrap();
|
||||
toml::from_str::<T>(s.as_str()).unwrap()
|
||||
}
|
||||
|
||||
@ -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}},
|
59
src/maps/src/lib.rs
Normal file
59
src/maps/src/lib.rs
Normal file
@ -0,0 +1,59 @@
|
||||
pub mod area;
|
||||
pub mod enemy;
|
||||
pub mod object;
|
||||
pub mod variant;
|
||||
pub mod maps;
|
||||
pub mod monster;
|
||||
pub mod room;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Holiday {
|
||||
None,
|
||||
Christmas,
|
||||
Valentines,
|
||||
Easter,
|
||||
Halloween,
|
||||
Sonic,
|
||||
NewYear,
|
||||
Summer,
|
||||
White,
|
||||
Wedding,
|
||||
Fall,
|
||||
Spring,
|
||||
Summer2,
|
||||
Spring2,
|
||||
}
|
||||
|
||||
|
||||
impl From<Holiday> for u32 {
|
||||
fn from(other: Holiday) -> u32 {
|
||||
u16::from(other) as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Holiday> for u16 {
|
||||
fn from(other: Holiday) -> u16 {
|
||||
u8::from(other) as u16
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Holiday> for u8 {
|
||||
fn from(other: Holiday) -> u8 {
|
||||
match other {
|
||||
Holiday::None => 0,
|
||||
Holiday::Christmas => 1,
|
||||
Holiday::Valentines => 3,
|
||||
Holiday::Easter => 4,
|
||||
Holiday::Halloween => 5,
|
||||
Holiday::Sonic => 6,
|
||||
Holiday::NewYear => 7,
|
||||
Holiday::Summer => 8,
|
||||
Holiday::White => 9,
|
||||
Holiday::Wedding => 10,
|
||||
Holiday::Fall => 11,
|
||||
Holiday::Spring => 12,
|
||||
Holiday::Summer2 => 13,
|
||||
Holiday::Spring2 => 14,
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
@ -375,3 +376,12 @@ pub fn generate_free_roam_maps(room_mode: RoomMode, event: Option<RareEnemyEvent
|
||||
map_variants,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn null_free_roam_maps(_room_mode: RoomMode, _event: Holiday) -> Maps {
|
||||
Maps {
|
||||
enemy_data: Default::default(),
|
||||
object_data: Default::default(),
|
||||
map_variants: Default::default(),
|
||||
}
|
||||
}
|
18
src/networking/Cargo.toml
Normal file
18
src/networking/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "networking"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
[dependencies]
|
||||
entity = { workspace = true }
|
||||
|
||||
libpso = { workspace = true }
|
||||
|
||||
async-std = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
log = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
derive_more = { workspace = true }
|
4
src/networking/src/lib.rs
Normal file
4
src/networking/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod cipherkeys;
|
||||
pub mod serverstate;
|
||||
pub mod mainloop;
|
||||
pub mod interserver;
|
@ -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)]
|
||||
@ -72,7 +72,7 @@ impl<C: PSOCipher> PacketReceiver<C> {
|
||||
let mut dec_buf = {
|
||||
//let mut cipher = self.cipher.lock().await;
|
||||
let block_chunk_len = self.recv_buffer.len() / self.cipher.block_size() * self.cipher.block_size();
|
||||
let buf = self.recv_buffer.drain(..block_chunk_len).collect();
|
||||
let buf = self.recv_buffer.drain(..block_chunk_len).collect::<Vec<_>>();
|
||||
self.cipher.decrypt(&buf)?
|
||||
};
|
||||
self.incoming_data.append(&mut dec_buf);
|
||||
@ -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();
|
@ -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();
|
||||
@ -219,12 +218,8 @@ where
|
||||
let mut buf = [0u8; 1];
|
||||
loop {
|
||||
let peek = socket.peek(&mut buf).await;
|
||||
match peek {
|
||||
Ok(len) if len == 0 => {
|
||||
break
|
||||
},
|
||||
_ => {
|
||||
}
|
||||
if let Ok(0) = peek {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
#[allow(clippy::module_inception)]
|
||||
pub mod patch;
|
15
src/patch_server/Cargo.toml
Normal file
15
src/patch_server/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "patch_server"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
networking = { workspace = true }
|
||||
|
||||
libpso = { workspace = true }
|
||||
|
||||
async-trait = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
crc = { workspace = true }
|
||||
ron = { workspace = true }
|
||||
serde = { workspace = true }
|
@ -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 {
|
||||
@ -341,7 +341,7 @@ impl Iterator for SendFileIterator {
|
||||
if len == 0 {
|
||||
self.current_file = None;
|
||||
self.chunk_num = 0;
|
||||
Some(SendPatchPacket::EndFileSend(EndFileSend::new()))
|
||||
Some(SendPatchPacket::EndFileSend(EndFileSend::default()))
|
||||
}
|
||||
else {
|
||||
let mut crc = crc32::Digest::new(crc32::IEEE);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user