Compare commits
3 commits
f0f739c02c
...
00f68e804b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00f68e804b | ||
|
|
849c090e28 | ||
|
|
a509529aef |
14 changed files with 196 additions and 88 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -2,5 +2,3 @@
|
|||
/data
|
||||
*.json.tmp
|
||||
docs/protocol.md
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
|
|
|||
90
Cargo.lock
generated
90
Cargo.lock
generated
|
|
@ -37,6 +37,15 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-polyfill"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "auth-service"
|
||||
version = "0.1.0"
|
||||
|
|
@ -117,6 +126,15 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
|
|
@ -147,6 +165,12 @@ version = "2.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.13"
|
||||
|
|
@ -221,6 +245,18 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
|
|
@ -386,7 +422,6 @@ dependencies = [
|
|||
"async-trait",
|
||||
"logic",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"service-rpc",
|
||||
"thiserror",
|
||||
"titan",
|
||||
|
|
@ -416,6 +451,15 @@ dependencies = [
|
|||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
|
|
@ -442,6 +486,20 @@ dependencies = [
|
|||
"hashbrown 0.15.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.7.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"hash32",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"spin",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
|
|
@ -872,6 +930,19 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"embedded-io 0.4.0",
|
||||
"embedded-io 0.6.1",
|
||||
"heapless",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.6"
|
||||
|
|
@ -1007,6 +1078,15 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.43"
|
||||
|
|
@ -1076,6 +1156,12 @@ dependencies = [
|
|||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.229"
|
||||
|
|
@ -1136,8 +1222,8 @@ name = "service-rpc"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"postcard",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,28 @@ fn gold() -> LogicDataRef {
|
|||
fn diamonds() -> LogicDataRef {
|
||||
LogicDataRef::by_name(table::RESOURCES, RESOURCE_DIAMONDS)
|
||||
}
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ShopError {
|
||||
#[error("not a card")]
|
||||
NotACard,
|
||||
#[error("not on sale")]
|
||||
NotOnSale,
|
||||
#[error("buy limit reached")]
|
||||
BuyLimitReached,
|
||||
#[error("nothing sells that")]
|
||||
NotSold,
|
||||
#[error("chest not sold")]
|
||||
ChestNotSold,
|
||||
#[error("no market price")]
|
||||
NoMarketPrice,
|
||||
#[error("price is not a resource")]
|
||||
PriceNotResource,
|
||||
#[error("not enough funds")]
|
||||
CannotAfford,
|
||||
#[error("unsupported currency")]
|
||||
UnsupportedCurrency,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TurnResult {
|
||||
pub claims: Vec<LogicClaimRewardCommand>,
|
||||
|
|
@ -92,11 +114,11 @@ impl HomeMode {
|
|||
_ => LogicDataRef::treasure_chest_for_arena(CHEST_FREE, arena),
|
||||
}
|
||||
}
|
||||
fn market_cost(&self, give: &ShopEntry) -> Result<ShopEntry, &'static str> {
|
||||
fn market_cost(&self, give: &ShopEntry) -> Result<ShopEntry, ShopError> {
|
||||
if let Some(chest) = give.data.as_treasure_chest() {
|
||||
let price = chest.shop_price();
|
||||
if price < 1 {
|
||||
return Err("that chest is not sold in the shop");
|
||||
return Err(ShopError::ChestNotSold);
|
||||
}
|
||||
return Ok(ShopEntry::new(diamonds(), price));
|
||||
}
|
||||
|
|
@ -106,27 +128,27 @@ impl HomeMode {
|
|||
LogicGlobals::resource_diamond_cost(give.count),
|
||||
));
|
||||
}
|
||||
Err("that item has no market price")
|
||||
Err(ShopError::NoMarketPrice)
|
||||
}
|
||||
fn pay(&mut self, cost: &ShopEntry) -> Result<(), &'static str> {
|
||||
fn pay(&mut self, cost: &ShopEntry) -> Result<(), ShopError> {
|
||||
if !cost.is_resource() {
|
||||
return Err("the price must be a resource");
|
||||
return Err(ShopError::PriceNotResource);
|
||||
}
|
||||
match cost.data.name() {
|
||||
RESOURCE_GOLD if self.logic.spend_gold(cost.count) => Ok(()),
|
||||
RESOURCE_DIAMONDS if self.logic.spend_diamonds(cost.count) => Ok(()),
|
||||
RESOURCE_GOLD | RESOURCE_DIAMONDS => Err("the player cannot afford it"),
|
||||
_ => Err("only gold and diamonds are accepted"),
|
||||
RESOURCE_GOLD | RESOURCE_DIAMONDS => Err(ShopError::CannotAfford),
|
||||
_ => Err(ShopError::UnsupportedCurrency),
|
||||
}
|
||||
}
|
||||
fn buy_card(&mut self, card: &LogicDataRef) -> Result<Purchase, &'static str> {
|
||||
let spell = card.as_spell().ok_or("that is not a card")?;
|
||||
fn buy_card(&mut self, card: &LogicDataRef) -> Result<Purchase, ShopError> {
|
||||
let spell = card.as_spell().ok_or(ShopError::NotACard)?;
|
||||
let buy_times = self
|
||||
.logic
|
||||
.shop_buy_times(card)
|
||||
.ok_or("that card is not on sale right now")?;
|
||||
.ok_or(ShopError::NotOnSale)?;
|
||||
if buy_times >= spell.shop_buy_limit() {
|
||||
return Err("the buy limit for that card is reached");
|
||||
return Err(ShopError::BuyLimitReached);
|
||||
}
|
||||
let cost = ShopEntry::new(gold(), spell.cost_in_shop(buy_times));
|
||||
self.pay(&cost)?;
|
||||
|
|
@ -143,14 +165,14 @@ impl HomeMode {
|
|||
data: &LogicDataRef,
|
||||
count: i32,
|
||||
shop: &ShopCatalog,
|
||||
) -> Result<Purchase, &'static str> {
|
||||
) -> Result<Purchase, ShopError> {
|
||||
if data.as_spell().is_some() {
|
||||
return self.buy_card(data);
|
||||
}
|
||||
let offer = shop
|
||||
.offer_for(data)
|
||||
.filter(|offer| offer.give.count == count)
|
||||
.ok_or("nothing in the shop sells that")?
|
||||
.ok_or(ShopError::NotSold)?
|
||||
.clone();
|
||||
let cost = match &offer.cost {
|
||||
Some(cost) => cost.clone(),
|
||||
|
|
@ -161,7 +183,7 @@ impl HomeMode {
|
|||
match offer.give.data.name() {
|
||||
RESOURCE_GOLD => self.logic.add_gold(offer.give.count),
|
||||
RESOURCE_DIAMONDS => self.logic.add_diamonds(offer.give.count),
|
||||
_ => return Err("only gold and diamonds can be sold"),
|
||||
_ => return Err(ShopError::UnsupportedCurrency),
|
||||
}
|
||||
}
|
||||
Ok(Purchase {
|
||||
|
|
@ -221,11 +243,11 @@ impl HomeMode {
|
|||
result.changed = true;
|
||||
result.purchases.push(purchase);
|
||||
}
|
||||
Err(reason) => {
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
command = command.command_type(),
|
||||
item = %data,
|
||||
reason,
|
||||
reason = %error,
|
||||
"shop purchase rejected"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ thiserror = { workspace = true }
|
|||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
|
|
|
|||
|
|
@ -9,5 +9,5 @@ tokio = { workspace = true }
|
|||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
postcard = { version = "1.1.3", features = ["alloc"] }
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ pub struct Session {
|
|||
pub server_time: String,
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "outcome", rename_all = "snake_case")]
|
||||
pub enum LoginOutcome {
|
||||
Accepted(Session),
|
||||
Rejected {
|
||||
|
|
@ -44,7 +43,6 @@ pub enum LoginOutcome {
|
|||
},
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "op", rename_all = "snake_case")]
|
||||
pub enum AuthRequest {
|
||||
Login {
|
||||
account: AccountRef,
|
||||
|
|
@ -56,7 +54,6 @@ pub enum AuthRequest {
|
|||
},
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "result", rename_all = "snake_case")]
|
||||
pub enum AuthResponse {
|
||||
Login(LoginOutcome),
|
||||
Resolved { known: bool },
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
pub fn encode(input: &[u8]) -> String {
|
||||
let mut out = String::with_capacity(input.len().div_ceil(3) * 4);
|
||||
for chunk in input.chunks(3) {
|
||||
let b0 = chunk[0] as u32;
|
||||
let b1 = chunk.get(1).copied().unwrap_or(0) as u32;
|
||||
let b2 = chunk.get(2).copied().unwrap_or(0) as u32;
|
||||
let triple = (b0 << 16) | (b1 << 8) | b2;
|
||||
out.push(ALPHABET[(triple >> 18) as usize & 0x3F] as char);
|
||||
out.push(ALPHABET[(triple >> 12) as usize & 0x3F] as char);
|
||||
out.push(if chunk.len() > 1 {
|
||||
ALPHABET[(triple >> 6) as usize & 0x3F] as char
|
||||
} else {
|
||||
'='
|
||||
});
|
||||
out.push(if chunk.len() > 2 {
|
||||
ALPHABET[triple as usize & 0x3F] as char
|
||||
} else {
|
||||
'='
|
||||
});
|
||||
}
|
||||
out
|
||||
}
|
||||
pub fn decode(input: &str) -> Option<Vec<u8>> {
|
||||
let bytes: Vec<u8> = input.bytes().filter(|byte| *byte != b'=').collect();
|
||||
let mut out = Vec::with_capacity(bytes.len() * 3 / 4);
|
||||
let mut accumulator: u32 = 0;
|
||||
let mut bits: u32 = 0;
|
||||
for byte in bytes {
|
||||
let value = ALPHABET.iter().position(|candidate| *candidate == byte)? as u32;
|
||||
accumulator = (accumulator << 6) | value;
|
||||
bits += 6;
|
||||
if bits >= 8 {
|
||||
bits -= 8;
|
||||
out.push((accumulator >> bits) as u8);
|
||||
}
|
||||
}
|
||||
Some(out)
|
||||
}
|
||||
pub mod serde_bytes {
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
pub fn serialize<S: Serializer>(value: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&super::encode(value))
|
||||
}
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
|
||||
let text = String::deserialize(deserializer)?;
|
||||
super::decode(&text).ok_or_else(|| serde::de::Error::custom("invalid base64 payload"))
|
||||
}
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ where
|
|||
&self.endpoint
|
||||
}
|
||||
pub async fn call(&self, request: &Req) -> RpcResult<Res> {
|
||||
let encoded = serde_json::to_vec(request)?;
|
||||
let encoded = postcard::to_allocvec(request)?;
|
||||
let slot = self.cursor.fetch_add(1, Ordering::Relaxed) % self.pool.len();
|
||||
let mut guard = self.pool[slot].lock().await;
|
||||
let mut last_error: Option<RpcError> = None;
|
||||
|
|
@ -56,7 +56,7 @@ where
|
|||
read_frame(stream).await
|
||||
};
|
||||
match exchange.await {
|
||||
Ok(payload) => return Ok(serde_json::from_slice(&payload)?),
|
||||
Ok(payload) => return Ok(postcard::from_bytes(&payload)?),
|
||||
Err(error) => {
|
||||
*guard = None;
|
||||
last_error = Some(error);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ pub enum RpcError {
|
|||
#[error("transport: {0}")]
|
||||
Transport(#[from] std::io::Error),
|
||||
#[error("encoding: {0}")]
|
||||
Encoding(#[from] serde_json::Error),
|
||||
Encoding(#[from] postcard::Error),
|
||||
#[error("frame of {actual} byte(s) exceeds the {limit} byte limit")]
|
||||
FrameTooLarge { actual: usize, limit: usize },
|
||||
#[error("peer closed the connection")]
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ pub enum HomeRequestKind {
|
|||
GoHome,
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "op", rename_all = "snake_case")]
|
||||
pub enum GameRequest {
|
||||
LoadHome {
|
||||
account: AccountRef,
|
||||
|
|
@ -40,17 +39,14 @@ pub enum GameRequest {
|
|||
},
|
||||
EndClientTurn {
|
||||
account: AccountRef,
|
||||
#[serde(with = "crate::base64::serde_bytes")]
|
||||
payload: Vec<u8>,
|
||||
},
|
||||
StartMission {
|
||||
account: AccountRef,
|
||||
#[serde(with = "crate::base64::serde_bytes")]
|
||||
payload: Vec<u8>,
|
||||
},
|
||||
HomeLogicStopped {
|
||||
account: AccountRef,
|
||||
#[serde(with = "crate::base64::serde_bytes")]
|
||||
payload: Vec<u8>,
|
||||
},
|
||||
BattleTick {
|
||||
|
|
@ -61,7 +57,6 @@ pub enum GameRequest {
|
|||
},
|
||||
SectorCommand {
|
||||
account: AccountRef,
|
||||
#[serde(with = "crate::base64::serde_bytes")]
|
||||
payload: Vec<u8>,
|
||||
},
|
||||
CancelMatchmake {
|
||||
|
|
@ -69,7 +64,6 @@ pub enum GameRequest {
|
|||
},
|
||||
BattleEvent {
|
||||
account: AccountRef,
|
||||
#[serde(with = "crate::base64::serde_bytes")]
|
||||
payload: Vec<u8>,
|
||||
},
|
||||
Disconnect {
|
||||
|
|
@ -77,7 +71,6 @@ pub enum GameRequest {
|
|||
},
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "result", rename_all = "snake_case")]
|
||||
pub enum GameResponse {
|
||||
Messages { messages: Vec<WireMessage> },
|
||||
Empty,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
pub mod auth;
|
||||
pub mod base64;
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
pub mod game;
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ async fn handle_connection<S: RpcService>(mut stream: TcpStream, service: Arc<S>
|
|||
Err(RpcError::Closed) => return Ok(()),
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let decoded: S::Request = serde_json::from_slice(&request)?;
|
||||
let decoded: S::Request = postcard::from_bytes(&request)?;
|
||||
let response = service.call(decoded).await?;
|
||||
let encoded = serde_json::to_vec(&response)?;
|
||||
let encoded = postcard::to_allocvec(&response)?;
|
||||
write_frame(&mut stream, &encoded).await?;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ pub struct WireMessage {
|
|||
pub message_type: u16,
|
||||
#[serde(default)]
|
||||
pub message_version: u16,
|
||||
#[serde(with = "crate::base64::serde_bytes")]
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
impl WireMessage {
|
||||
|
|
|
|||
64
crates/service-rpc/tests/postcard_roundtrip.rs
Normal file
64
crates/service-rpc/tests/postcard_roundtrip.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use service_rpc::{
|
||||
AccountRef, AuthRequest, AuthResponse, DeviceInfo, GameRequest, GameResponse, HomeRequestKind,
|
||||
LoginOutcome, Session, WireMessage,
|
||||
};
|
||||
|
||||
fn roundtrip<T>(value: T)
|
||||
where
|
||||
T: serde::Serialize + serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
|
||||
{
|
||||
let bytes = postcard::to_allocvec(&value).expect("encode");
|
||||
let back: T = postcard::from_bytes(&bytes).expect("decode");
|
||||
assert_eq!(value, back);
|
||||
}
|
||||
|
||||
fn session() -> Session {
|
||||
Session {
|
||||
account: AccountRef::new(0, 5),
|
||||
pass_token: "tok".to_owned(),
|
||||
session_count: 3,
|
||||
play_time_seconds: 42,
|
||||
days_since_started_playing: 1,
|
||||
account_created_date: "2026-01-01".to_owned(),
|
||||
server_time: "2026-08-28".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn game_request_survives_the_wire() {
|
||||
roundtrip(GameRequest::BattleTick {
|
||||
account: AccountRef::new(0, 5),
|
||||
});
|
||||
roundtrip(GameRequest::SectorCommand {
|
||||
account: AccountRef::new(0, 5),
|
||||
payload: vec![0, 1, 2, 253, 254, 255],
|
||||
});
|
||||
roundtrip(GameRequest::LoadHome {
|
||||
account: AccountRef::new(1, 2),
|
||||
kind: HomeRequestKind::Login,
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn game_response_survives_the_wire() {
|
||||
roundtrip(GameResponse::Empty);
|
||||
roundtrip(GameResponse::messages(vec![
|
||||
WireMessage::new(20104, 0, vec![9, 8, 7, 0, 255]),
|
||||
WireMessage::new(24101, 1, Vec::new()),
|
||||
]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auth_exchange_survives_the_wire() {
|
||||
roundtrip(AuthRequest::Login {
|
||||
account: AccountRef::new(0, 5),
|
||||
pass_token: Some("tok".to_owned()),
|
||||
device: DeviceInfo::default(),
|
||||
});
|
||||
roundtrip(AuthResponse::Login(LoginOutcome::Accepted(session())));
|
||||
roundtrip(AuthResponse::Login(LoginOutcome::Rejected {
|
||||
error_code: 11,
|
||||
message: Some("banned".to_owned()),
|
||||
}));
|
||||
roundtrip(AuthResponse::Resolved { known: true });
|
||||
}
|
||||
Loading…
Reference in a new issue