use std::convert::TryFrom;
use std::marker::PhantomData;
use getset::{CopyGetters, Getters, Setters};
use num_traits::FromPrimitive;
use super::{
gamedata::{EliteSpec, Profession},
raw, EvtcError,
};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Hash, PartialEq, Eq, CopyGetters)]
pub struct Player {
#[get_copy = "pub"]
profession: Profession,
#[get_copy = "pub"]
elite: Option<EliteSpec>,
character_name: String,
account_name: String,
#[get_copy = "pub"]
subgroup: u8,
}
impl Player {
pub fn character_name(&self) -> &str {
&self.character_name
}
pub fn account_name(&self) -> &str {
&self.account_name
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Hash, PartialEq, Eq, CopyGetters)]
pub struct Gadget {
#[get_copy = "pub"]
id: u16,
name: String,
}
impl Gadget {
pub fn name(&self) -> &str {
&self.name
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Hash, PartialEq, Eq, CopyGetters)]
pub struct Character {
#[get_copy = "pub"]
id: u16,
name: String,
}
impl Character {
pub fn name(&self) -> &str {
&self.name
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum AgentKind {
Player(Player),
Gadget(Gadget),
Character(Character),
}
impl AgentKind {
fn from_raw_character(raw_agent: &raw::Agent) -> Result<Character, EvtcError> {
assert!(raw_agent.is_character());
let name = raw::cstr_up_to_nul(&raw_agent.name).ok_or(EvtcError::InvalidData)?;
Ok(Character {
id: raw_agent.prof as u16,
name: name.to_str()?.to_owned(),
})
}
fn from_raw_gadget(raw_agent: &raw::Agent) -> Result<Gadget, EvtcError> {
assert!(raw_agent.is_gadget());
let name = raw::cstr_up_to_nul(&raw_agent.name).ok_or(EvtcError::InvalidData)?;
Ok(Gadget {
id: raw_agent.prof as u16,
name: name.to_str()?.to_owned(),
})
}
fn from_raw_player(raw_agent: &raw::Agent) -> Result<Player, EvtcError> {
assert!(raw_agent.is_player());
let character_name = raw::cstr_up_to_nul(&raw_agent.name)
.ok_or(EvtcError::InvalidData)?
.to_str()?;
let remainder = &raw_agent.name[character_name.len() + 1..];
let account_name = raw::cstr_up_to_nul(remainder)
.ok_or(EvtcError::InvalidData)?
.to_str()?;
let remainder = &remainder[account_name.len() + 1..];
let subgroup = raw::cstr_up_to_nul(remainder)
.ok_or(EvtcError::InvalidData)?
.to_str()?;
let subgroup = if subgroup.is_empty() {
0
} else {
subgroup.parse().map_err(|_| EvtcError::InvalidData)?
};
let elite = if raw_agent.is_elite == 0 {
None
} else {
Some(
EliteSpec::from_u32(raw_agent.is_elite)
.ok_or(EvtcError::InvalidEliteSpec(raw_agent.is_elite))?,
)
};
Ok(Player {
profession: Profession::from_u32(raw_agent.prof)
.ok_or(EvtcError::InvalidProfession(raw_agent.prof))?,
elite,
character_name: character_name.to_owned(),
account_name: account_name.to_owned(),
subgroup,
})
}
pub fn as_player(&self) -> Option<&Player> {
if let AgentKind::Player(ref player) = *self {
Some(player)
} else {
None
}
}
pub fn is_player(&self) -> bool {
self.as_player().is_some()
}
pub fn as_gadget(&self) -> Option<&Gadget> {
if let AgentKind::Gadget(ref gadget) = *self {
Some(gadget)
} else {
None
}
}
pub fn is_gadget(&self) -> bool {
self.as_gadget().is_some()
}
pub fn as_character(&self) -> Option<&Character> {
if let AgentKind::Character(ref character) = *self {
Some(character)
} else {
None
}
}
pub fn is_character(&self) -> bool {
self.as_character().is_some()
}
}
impl TryFrom<raw::Agent> for AgentKind {
type Error = EvtcError;
fn try_from(raw_agent: raw::Agent) -> Result<Self, Self::Error> {
Self::try_from(&raw_agent)
}
}
impl TryFrom<&raw::Agent> for AgentKind {
type Error = EvtcError;
fn try_from(raw_agent: &raw::Agent) -> Result<Self, Self::Error> {
if raw_agent.is_character() {
Ok(AgentKind::Character(AgentKind::from_raw_character(
raw_agent,
)?))
} else if raw_agent.is_gadget() {
Ok(AgentKind::Gadget(AgentKind::from_raw_gadget(raw_agent)?))
} else if raw_agent.is_player() {
Ok(AgentKind::Player(AgentKind::from_raw_player(raw_agent)?))
} else {
Err(EvtcError::InvalidData)
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Debug, Clone, Hash, PartialEq, Eq, Getters, CopyGetters, Setters)]
#[repr(C)]
pub struct Agent<Kind = ()> {
#[getset(get_copy = "pub", set = "pub(crate)")]
addr: u64,
#[getset(get = "pub", set = "pub(crate)")]
kind: AgentKind,
#[get_copy = "pub"]
toughness: i16,
#[getset(get_copy = "pub", set = "pub(crate)")]
concentration: i16,
#[getset(get_copy = "pub", set = "pub(crate)")]
healing: i16,
#[getset(get_copy = "pub", set = "pub(crate)")]
condition: i16,
#[getset(get_copy = "pub", set = "pub(crate)")]
instance_id: u16,
#[getset(get_copy = "pub", set = "pub(crate)")]
first_aware: u64,
#[getset(get_copy = "pub", set = "pub(crate)")]
last_aware: u64,
#[getset(get_copy = "pub", set = "pub(crate)")]
master_agent: Option<u64>,
#[cfg_attr(feature = "serde", serde(skip_serializing))]
phantom_data: PhantomData<Kind>,
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Agent {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de::{self, MapAccess, SeqAccess, Visitor};
use std::fmt;
#[derive(serde::Deserialize)]
#[serde(field_identifier, rename_all = "snake_case")]
enum Field {
Addr,
Kind,
Toughness,
Concentration,
Healing,
Condition,
InstanceId,
FirstAware,
LastAware,
MasterAgent,
}
struct AgentVisitor;
impl<'de> Visitor<'de> for AgentVisitor {
type Value = Agent;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Agent")
}
fn visit_seq<V: SeqAccess<'de>>(self, mut seq: V) -> Result<Agent, V::Error> {
let addr = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let kind = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let toughness = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
let concentration = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(3, &self))?;
let healing = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(4, &self))?;
let condition = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(5, &self))?;
let instance_id = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(6, &self))?;
let first_aware = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(7, &self))?;
let last_aware = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(8, &self))?;
let master_agent = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(9, &self))?;
Ok(Agent {
addr,
kind,
toughness,
concentration,
healing,
condition,
instance_id,
first_aware,
last_aware,
master_agent,
phantom_data: PhantomData,
})
}
fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<Agent, V::Error> {
let mut addr = None;
let mut kind = None;
let mut toughness = None;
let mut concentration = None;
let mut healing = None;
let mut condition = None;
let mut instance_id = None;
let mut first_aware = None;
let mut last_aware = None;
let mut master_agent = None;
while let Some(key) = map.next_key()? {
match key {
Field::Addr => {
if addr.is_some() {
return Err(de::Error::duplicate_field("addr"));
}
addr = Some(map.next_value()?);
}
Field::Kind => {
if kind.is_some() {
return Err(de::Error::duplicate_field("kind"));
}
kind = Some(map.next_value()?);
}
Field::Toughness => {
if toughness.is_some() {
return Err(de::Error::duplicate_field("toughness"));
}
toughness = Some(map.next_value()?);
}
Field::Concentration => {
if concentration.is_some() {
return Err(de::Error::duplicate_field("concentration"));
}
concentration = Some(map.next_value()?);
}
Field::Healing => {
if healing.is_some() {
return Err(de::Error::duplicate_field("healing"));
}
healing = Some(map.next_value()?);
}
Field::Condition => {
if condition.is_some() {
return Err(de::Error::duplicate_field("condition"));
}
condition = Some(map.next_value()?);
}
Field::InstanceId => {
if instance_id.is_some() {
return Err(de::Error::duplicate_field("instance_id"));
}
instance_id = Some(map.next_value()?);
}
Field::FirstAware => {
if first_aware.is_some() {
return Err(de::Error::duplicate_field("first_aware"));
}
first_aware = Some(map.next_value()?);
}
Field::LastAware => {
if last_aware.is_some() {
return Err(de::Error::duplicate_field("last_aware"));
}
last_aware = Some(map.next_value()?);
}
Field::MasterAgent => {
if master_agent.is_some() {
return Err(de::Error::duplicate_field("master_agent"));
}
master_agent = Some(map.next_value()?);
}
}
}
Ok(Agent {
addr: addr.ok_or_else(|| de::Error::missing_field("addr"))?,
kind: kind.ok_or_else(|| de::Error::missing_field("kind"))?,
toughness: toughness.ok_or_else(|| de::Error::missing_field("toughness"))?,
concentration: concentration
.ok_or_else(|| de::Error::missing_field("concentration"))?,
healing: healing.ok_or_else(|| de::Error::missing_field("healing"))?,
condition: condition.ok_or_else(|| de::Error::missing_field("condition"))?,
instance_id: instance_id
.ok_or_else(|| de::Error::missing_field("instance_id"))?,
first_aware: first_aware
.ok_or_else(|| de::Error::missing_field("first_aware"))?,
last_aware: last_aware.ok_or_else(|| de::Error::missing_field("last_aware"))?,
master_agent: master_agent
.ok_or_else(|| de::Error::missing_field("master_agent"))?,
phantom_data: PhantomData,
})
}
}
const FIELDS: &[&str] = &[
"addr",
"kind",
"toughness",
"concentration",
"healing",
"condition",
"instance_id",
"first_aware",
"last_aware",
"master_agent",
];
deserializer.deserialize_struct("Agent", FIELDS, AgentVisitor)
}
}
impl TryFrom<&raw::Agent> for Agent {
type Error = EvtcError;
fn try_from(raw_agent: &raw::Agent) -> Result<Self, Self::Error> {
let kind = AgentKind::try_from(raw_agent)?;
Ok(Agent {
addr: raw_agent.addr,
kind,
toughness: raw_agent.toughness,
concentration: raw_agent.concentration,
healing: raw_agent.healing,
condition: raw_agent.condition,
instance_id: 0,
first_aware: 0,
last_aware: u64::max_value(),
master_agent: None,
phantom_data: PhantomData,
})
}
}
impl TryFrom<raw::Agent> for Agent {
type Error = EvtcError;
fn try_from(raw_agent: raw::Agent) -> Result<Self, Self::Error> {
Agent::try_from(&raw_agent)
}
}
impl<Kind> Agent<Kind> {
#[inline]
fn transmute<T>(&self) -> &Agent<T> {
unsafe { &*(self as *const Agent<Kind> as *const Agent<T>) }
}
#[inline]
pub fn erase(&self) -> &Agent {
self.transmute()
}
#[inline]
pub fn as_player(&self) -> Option<&Agent<Player>> {
if self.kind.is_player() {
Some(self.transmute())
} else {
None
}
}
#[inline]
pub fn as_gadget(&self) -> Option<&Agent<Gadget>> {
if self.kind.is_gadget() {
Some(self.transmute())
} else {
None
}
}
#[inline]
pub fn as_character(&self) -> Option<&Agent<Character>> {
if self.kind.is_character() {
Some(self.transmute())
} else {
None
}
}
}
impl Agent<Player> {
#[inline]
pub fn player(&self) -> &Player {
self.kind.as_player().expect("Agent<Player> had no player!")
}
#[inline]
pub fn account_name(&self) -> &str {
self.player().account_name()
}
#[inline]
pub fn character_name(&self) -> &str {
self.player().character_name()
}
#[inline]
pub fn elite(&self) -> Option<EliteSpec> {
self.player().elite()
}
#[inline]
pub fn profession(&self) -> Profession {
self.player().profession()
}
#[inline]
pub fn subgroup(&self) -> u8 {
self.player().subgroup()
}
}
impl Agent<Gadget> {
#[inline]
pub fn gadget(&self) -> &Gadget {
self.kind.as_gadget().expect("Agent<Gadget> had no gadget!")
}
#[inline]
pub fn id(&self) -> u16 {
self.gadget().id()
}
#[inline]
pub fn name(&self) -> &str {
self.gadget().name()
}
}
impl Agent<Character> {
#[inline]
pub fn character(&self) -> &Character {
self.kind
.as_character()
.expect("Agent<Character> had no character!")
}
#[inline]
pub fn id(&self) -> u16 {
self.character().id()
}
#[inline]
pub fn name(&self) -> &str {
self.character().name()
}
}
#[cfg(all(feature = "serde", test))]
mod tests {
use super::*;
fn agent() -> Agent {
Agent {
addr: 0xdeadbeef,
kind: AgentKind::Character(Character {
id: 0xf00,
name: "Foo Bar".into(),
}),
toughness: -13,
concentration: -14,
healing: -15,
condition: -16,
instance_id: 1337,
first_aware: 0,
last_aware: 0xffffff,
master_agent: None,
phantom_data: PhantomData,
}
}
#[test]
fn serialization() {
let agent = agent();
let json = serde_json::to_string(&agent).unwrap();
let expected = r#"{"addr":3735928559,"kind":{"Character":{"id":3840,"name":"Foo Bar"}},"toughness":-13,"concentration":-14,"healing":-15,"condition":-16,"instance_id":1337,"first_aware":0,"last_aware":16777215,"master_agent":null}"#;
assert_eq!(json, expected);
}
#[test]
fn deserialization() {
let json = r#"{"addr":3735928559,"kind":{"Character":{"id":3840,"name":"Foo Bar"}},"toughness":-13,"concentration":-14,"healing":-15,"condition":-16,"instance_id":1337,"first_aware":0,"last_aware":16777215,"master_agent":null}"#;
let deserialized: Agent = serde_json::from_str(json).unwrap();
assert_eq!(deserialized, agent());
}
#[test]
#[should_panic(expected = "missing field `master_agent`")]
fn deserialization_missing_field() {
let json = r#"{"addr":3735928559,"kind":{"Character":{"id":3840,"name":"Foo Bar"}},"toughness":-13,"concentration":-14,"healing":-15,"condition":-16,"instance_id":1337,"first_aware":0,"last_aware":16777215}"#;
serde_json::from_str::<Agent>(json).unwrap();
}
#[test]
#[should_panic(expected = "duplicate field `master_agent`")]
fn deserialization_duplicated_field() {
let json = r#"{"addr":3735928559,"kind":{"Character":{"id":3840,"name":"Foo Bar"}},"toughness":-13,"concentration":-14,"healing":-15,"condition":-16,"instance_id":1337,"first_aware":0,"last_aware":16777215,"master_agent":null,"master_agent":null}"#;
serde_json::from_str::<Agent>(json).unwrap();
}
#[test]
fn deserialization_sequence() {
let json = r#"[3735928559,{"Character":{"id":3840,"name":"Foo Bar"}},-13,-14,-15,-16,1337,0,16777215,null]"#;
let deserialized: Agent = serde_json::from_str(json).unwrap();
assert_eq!(deserialized, agent());
}
#[test]
#[should_panic(expected = "invalid length 9")]
fn deserialization_sequence_too_short() {
let json = r#"[3735928559,{"Character":{"id":3840,"name":"Foo Bar"}},-13,-14,-15,-16,1337,0,16777215]"#;
serde_json::from_str::<Agent>(json).unwrap();
}
#[test]
#[should_panic(expected = "trailing characters")]
fn deserialization_sequence_too_long() {
let json = r#"[3735928559,{"Character":{"id":3840,"name":"Foo Bar"}},-13,-14,-15,-16,1337,0,16777215,null,null]"#;
serde_json::from_str::<Agent>(json).unwrap();
}
}