mirror of
https://github.com/Squareville/lu_packets.git
synced 2026-05-05 02:29:46 -05:00
Add UiMessageServerToSingleClient game message
This commit is contained in:
@@ -132,7 +132,6 @@ fn parse(path: &Path) -> Res<usize> {
|
||||
&& !file.name().contains("[4d-03]")
|
||||
&& !file.name().contains("[6d-03]")
|
||||
&& !file.name().contains("[91-03]")
|
||||
&& !file.name().contains("[a0-04]")
|
||||
&& !file.name().contains("[1a-05]")
|
||||
&& !file.name().contains("[e6-05]")
|
||||
&& !file.name().contains("[16-06]")
|
||||
@@ -151,7 +150,6 @@ fn parse(path: &Path) -> Res<usize> {
|
||||
&& !file.name().contains("[845]")
|
||||
&& !file.name().contains("[877]")
|
||||
&& !file.name().contains("[913]")
|
||||
&& !file.name().contains("[1184]")
|
||||
&& !file.name().contains("[1306]")
|
||||
&& !file.name().contains("[1510]")
|
||||
&& !file.name().contains("[1558]")
|
||||
|
||||
@@ -69,7 +69,7 @@ fn gen_test_case(type_name: &Ident, test_params: &Option<Punctuated<NestedMeta,
|
||||
let mut expected = include!(#rs_path);
|
||||
let mut input = bin;
|
||||
let mut reader = #reader_code;
|
||||
let parsed: #type_name<#test_params> = ::endio::LERead::read(&mut reader).unwrap();
|
||||
let parsed: #type_name<#test_params> = ::endio::LERead::read(&mut reader).expect("error while parsing bin");
|
||||
let mut read_buf = [0];
|
||||
let amount_read = std::io::Read::read(&mut reader, &mut read_buf).unwrap();
|
||||
assert_eq!(amount_read, 0, "bin not fully read");
|
||||
|
||||
+73
-1
@@ -5,6 +5,78 @@
|
||||
#![feature(specialization)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
/**
|
||||
Creates an [`Amf3::Array`](crate::world::amf3::Amf3::Array) containing the arguments.
|
||||
|
||||
Since AMF3 arrays are both vectors and maps at the same time, there are multiple forms of the macro, for map and for vector usage.
|
||||
|
||||
### Map usage
|
||||
|
||||
The syntax is `name: value`, where `name` is a string literal that will be converted to an [`Amf3String`](crate::world::amf3::Amf3String), and `value` is an expression that will be converted to an [`Amf3`] object.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# #[macro_use] extern crate lu_packets;
|
||||
# use lu_packets::amf3;
|
||||
# fn main() {
|
||||
amf3! {
|
||||
"false": false,
|
||||
"true": true,
|
||||
"double1": 3.14f32,
|
||||
"double2": 3.14f64,
|
||||
"string": "string",
|
||||
"array": amf3! { "inner": "array"},
|
||||
};
|
||||
# }
|
||||
```
|
||||
|
||||
### Vector usage
|
||||
|
||||
The syntax is the exact same as with the [`vec!`] macro, except that the arguments will be converted to an [`Amf3`] object before being inserted.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# #[macro_use] extern crate lu_packets;
|
||||
# use lu_packets::amf3;
|
||||
# fn main() {
|
||||
amf3! [true, false, true];
|
||||
amf3! [true; 4];
|
||||
# }
|
||||
```
|
||||
|
||||
[`Amf3`]: crate::world::amf3::Amf3
|
||||
*/
|
||||
#[macro_export]
|
||||
macro_rules! amf3 {
|
||||
{} => { $crate::world::amf3::Amf3::Array($crate::world::amf3::Amf3Array::new()) };
|
||||
($($name:literal:$value:expr),+ $(,)?) => {
|
||||
{
|
||||
let mut array = $crate::world::amf3::Amf3Array::new();
|
||||
$(array.map.insert(::std::convert::TryInto::try_into($name).unwrap(), ::std::convert::TryInto::try_into($value).unwrap());)*
|
||||
$crate::world::amf3::Amf3::Array(array)
|
||||
}
|
||||
};
|
||||
($value:expr; $n:expr) => {
|
||||
{
|
||||
let converted = ::std::convert::TryInto::try_into($value).unwrap();
|
||||
let mut array = $crate::world::amf3::Amf3Array {
|
||||
map: ::std::collections::HashMap::new(),
|
||||
vec: vec![converted; $n],
|
||||
};
|
||||
$crate::world::amf3::Amf3::Array(array)
|
||||
}
|
||||
};
|
||||
($($value:expr),+ $(,)?) => {
|
||||
{
|
||||
let mut array = $crate::world::amf3::Amf3Array::new();
|
||||
$(array.vec.push(::std::convert::TryInto::try_into($value).unwrap());)*
|
||||
$crate::world::amf3::Amf3::Array(array)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a [`LuNameValue`] containing the arguments.
|
||||
|
||||
@@ -33,7 +105,7 @@
|
||||
Care should be taken with integer and float literals to suffix them with the correct type, as seen above. Rust assumes `i32` for integer and `f64` for float literals by default, which may not be what you want, and can lead to incorrect serialization.
|
||||
|
||||
[`LuNameValue`]: crate::world::LuNameValue
|
||||
[`LuVarWString<u32>`]: crate::common::str::variable::LuVarWString
|
||||
[`LuVarWString<u32>`]: crate::common::LuVarWString
|
||||
[`LnvValue`]: crate::world::LnvValue
|
||||
*/
|
||||
#[macro_export]
|
||||
|
||||
@@ -0,0 +1,400 @@
|
||||
//! (De-)serialization support for the [AMF3 format](https://wwwimages2.adobe.com/content/dam/acom/en/devnet/pdf/amf-file-format-spec.pdf).
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fmt::{Debug, Formatter, Result as FmtResult};
|
||||
use std::io::{Error, ErrorKind::InvalidData, Read, Result as Res, Write};
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use endio::{Deserialize, LE, LERead, LEWrite, Serialize};
|
||||
use lu_packets_derive::GmParam;
|
||||
|
||||
struct Amf3Reader<'a, R: Read> {
|
||||
inner: &'a mut R,
|
||||
string_ref_table: Vec<Amf3String>,
|
||||
}
|
||||
|
||||
impl<R: Read> Read for Amf3Reader<'_, R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Res<usize> {
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
struct Amf3Writer<'a, W: Write> {
|
||||
inner: &'a mut W,
|
||||
string_ref_table: Vec<Amf3String>,
|
||||
}
|
||||
|
||||
impl<W: Write> Write for Amf3Writer<'_, W> {
|
||||
fn write(&mut self, buf: &[u8]) -> Res<usize> {
|
||||
self.inner.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Res<()> {
|
||||
self.inner.flush()
|
||||
}
|
||||
}
|
||||
|
||||
/// An error returned when an integer is too large to be represented by an `U29`.
|
||||
#[derive(Debug)]
|
||||
pub struct U29Error;
|
||||
|
||||
/// An unsigned integer whose serialized form has variable length, with a maximum value of 2^29-1. [`See spec section 1.3.1`](https://wwwimages2.adobe.com/content/dam/acom/en/devnet/pdf/amf-file-format-spec.pdf#%5B%7B%22num%22%3A18%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C88%2C643%2C0%5D).
|
||||
#[derive(Debug)]
|
||||
struct U29(u32);
|
||||
|
||||
/// Converts the value to a [`U29`] if it is less than 2^29, returns [`U29Error`] otherwise.
|
||||
impl TryFrom<usize> for U29 {
|
||||
type Error = U29Error;
|
||||
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
if value >= 1 << 29 {
|
||||
Err(U29Error)
|
||||
} else {
|
||||
Ok(Self(value as u32))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Deserialize<LE, R> for U29 {
|
||||
fn deserialize(reader: &mut R) -> Res<Self> {
|
||||
let mut value = 0;
|
||||
for _ in 0..3 {
|
||||
let byte: u8 = LERead::read(reader)?;
|
||||
let byte = byte as u32;
|
||||
value = (value << 7) | (byte & 0x7f);
|
||||
if byte & 0x80 == 0 {
|
||||
return Ok(Self(value));
|
||||
}
|
||||
}
|
||||
let byte: u8 = LERead::read(reader)?;
|
||||
let byte = byte as u32;
|
||||
value = (value << 8) | byte;
|
||||
Ok(Self(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: Write> Serialize<LE, W> for &'a U29 {
|
||||
fn serialize(self, writer: &mut W) -> Res<()> {
|
||||
let v = self.0;
|
||||
if v <= 0x7f {
|
||||
LEWrite::write(writer, v as u8)
|
||||
} else if v <= 0x3fff {
|
||||
LEWrite::write(writer, (v >> 7) as u8 | 0x80)?;
|
||||
LEWrite::write(writer, v as u8 & 0x7f)
|
||||
} else if v <= 0x1fffff {
|
||||
LEWrite::write(writer, (v >> 14) as u8 | 0x80)?;
|
||||
LEWrite::write(writer, (v >> 7 ) as u8 | 0x80)?;
|
||||
LEWrite::write(writer, v as u8 & 0x7f)
|
||||
} else {
|
||||
LEWrite::write(writer, (v >> 22) as u8 | 0x80)?;
|
||||
LEWrite::write(writer, (v >> 15) as u8 | 0x80)?;
|
||||
LEWrite::write(writer, (v >> 8 ) as u8 | 0x80)?;
|
||||
LEWrite::write(writer, v as u8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
A string with a maximum length of 2^29-1.
|
||||
|
||||
[See spec section 3.8 for more](https://wwwimages2.adobe.com/content/dam/acom/en/devnet/pdf/amf-file-format-spec.pdf#%5B%7B%22num%22%3A22%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C88%2C196%2C0%5D).
|
||||
*/
|
||||
#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Amf3String(String);
|
||||
|
||||
impl Debug for Amf3String {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for Amf3String {
|
||||
fn borrow(&self) -> &str {
|
||||
&self.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Amf3String {
|
||||
type Error = U29Error;
|
||||
|
||||
fn try_from(string: &str) -> Result<Self, Self::Error> {
|
||||
if string.len() & (1 << 29) != 0 {
|
||||
Err(U29Error)
|
||||
} else {
|
||||
Ok(Self(string.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Deserialize<LE, Amf3Reader<'_, R>> for Amf3String {
|
||||
fn deserialize(reader: &mut Amf3Reader<'_, R>) -> Res<Self> {
|
||||
let value_and_is_inline: U29 = LERead::read(reader)?;
|
||||
let is_inline = value_and_is_inline.0 & 0x01 == 1;
|
||||
let value = value_and_is_inline.0 >> 1;
|
||||
let string = if !is_inline {
|
||||
let index = value;
|
||||
match reader.string_ref_table.get(index as usize) {
|
||||
Some(x) => x.0.clone(),
|
||||
None => { return Err(Error::new(InvalidData, "invalid reference index")) }
|
||||
}
|
||||
} else {
|
||||
let length = value;
|
||||
|
||||
let mut vec = vec![0u8; length as usize];
|
||||
Read::read_exact(reader, &mut vec)?;
|
||||
|
||||
let string = match String::from_utf8(vec) {
|
||||
Ok(x) => x,
|
||||
Err(_) => { return Err(Error::new(InvalidData, "string is not valid utf8")) }
|
||||
};
|
||||
if string != "" {
|
||||
reader.string_ref_table.push(Self(string.clone()));
|
||||
}
|
||||
string
|
||||
};
|
||||
|
||||
Ok(Self(string))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: Write> Serialize<LE, Amf3Writer<'_, W>> for &'a Amf3String {
|
||||
fn serialize(self, writer: &mut Amf3Writer<'_, W>) -> Res<()> {
|
||||
if self.0 == "" {
|
||||
let length_and_is_inline = U29(1);
|
||||
return LEWrite::write(writer, &length_and_is_inline);
|
||||
}
|
||||
match writer.string_ref_table.iter().position(|x| x == self) {
|
||||
Some(index) => {
|
||||
let index_and_is_inline = U29((index as u32) << 1);
|
||||
LEWrite::write(writer, &index_and_is_inline)
|
||||
}
|
||||
None => {
|
||||
let length_and_is_inline = U29((self.0.len() as u32) << 1 | 1);
|
||||
LEWrite::write(writer, &length_and_is_inline)?;
|
||||
writer.string_ref_table.push(Amf3String(self.0.clone()));
|
||||
Write::write_all(writer, self.0.as_bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Both a dense and associative array at the same time.
|
||||
|
||||
[See spec section 3.11 for more](https://wwwimages2.adobe.com/content/dam/acom/en/devnet/pdf/amf-file-format-spec.pdf#%5B%7B%22num%22%3A24%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C88%2C720%2C0%5D).
|
||||
*/
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Amf3Array {
|
||||
pub map: HashMap<Amf3String, Amf3>,
|
||||
pub vec: Vec<Amf3>,
|
||||
}
|
||||
|
||||
impl Amf3Array {
|
||||
pub fn new() -> Self {
|
||||
Self { map: HashMap::new(), vec: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Amf3Array {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
let map_empty = self.map.is_empty();
|
||||
let vec_empty = self.vec.is_empty();
|
||||
if !map_empty && vec_empty {
|
||||
write!(f, "amf3! ")?;
|
||||
self.map.fmt(f)
|
||||
} else if map_empty && !vec_empty {
|
||||
write!(f, "amf3! ")?;
|
||||
self.vec.fmt(f)
|
||||
} else if map_empty && vec_empty {
|
||||
write!(f, "amf3! {{}}")
|
||||
} else {
|
||||
write!(f, "Amf3Array {{ map: {:?}, vec: {:?} }}", self.map, self.vec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for Amf3Array {
|
||||
type Output = Amf3;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.vec[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Amf3Array {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
&mut self.vec[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<&Amf3String> for Amf3Array {
|
||||
type Output = Amf3;
|
||||
|
||||
fn index(&self, key: &Amf3String) -> &Amf3 {
|
||||
&self.map[key]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<&str> for Amf3Array {
|
||||
type Output = Amf3;
|
||||
|
||||
fn index(&self, key: &str) -> &Amf3 {
|
||||
&self.map[key]
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Deserialize<LE, Amf3Reader<'_, R>> for Amf3Array {
|
||||
fn deserialize(reader: &mut Amf3Reader<'_, R>) -> Res<Self> {
|
||||
let length_and_is_inline: U29 = LERead::read(reader)?;
|
||||
let is_inline = length_and_is_inline.0 & 0x01 == 1;
|
||||
let length = length_and_is_inline.0 >> 1;
|
||||
if !is_inline { todo!() }
|
||||
let mut map = HashMap::new();
|
||||
loop {
|
||||
let key: Amf3String = LERead::read(reader)?;
|
||||
if key.0 == "" {
|
||||
break;
|
||||
}
|
||||
let value = deser_amf3(reader)?;
|
||||
map.insert(key, value);
|
||||
}
|
||||
let mut vec = Vec::with_capacity(length as usize);
|
||||
for _ in 0..length {
|
||||
let value = deser_amf3(reader)?;
|
||||
vec.push(value);
|
||||
}
|
||||
|
||||
Ok(Self { map, vec })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: Write> Serialize<LE, Amf3Writer<'_, W>> for &'a Amf3Array {
|
||||
fn serialize(self, writer: &mut Amf3Writer<'_, W>) -> Res<()> {
|
||||
let length_and_is_inline = U29((self.vec.len() as u32) << 1 | 1);
|
||||
LEWrite::write(writer, &length_and_is_inline)?;
|
||||
#[cfg(test)]
|
||||
let key_value = {
|
||||
let mut key_value: Vec<_> = self.map.iter().collect();
|
||||
key_value.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2));
|
||||
key_value
|
||||
};
|
||||
#[cfg(not(test))]
|
||||
let key_value = self.map.iter();
|
||||
for (key, value) in key_value {
|
||||
LEWrite::write(writer, key)?;
|
||||
ser_amf3(writer, value)?;
|
||||
}
|
||||
LEWrite::write(writer, &Amf3String("".into()))?;
|
||||
for value in &self.vec {
|
||||
ser_amf3(writer, value)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
/**
|
||||
A type that can be (de-)serialized in the AMF3 format.
|
||||
|
||||
[See spec section 3 for more](https://wwwimages2.adobe.com/content/dam/acom/en/devnet/pdf/amf-file-format-spec.pdf#%5B%7B%22num%22%3A20%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C88%2C305%2C0%5D).
|
||||
*/
|
||||
#[derive(Clone, GmParam, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum Amf3 {
|
||||
False = 2,
|
||||
True = 3,
|
||||
Double(f64) = 5,
|
||||
String(Amf3String) = 6,
|
||||
Array(Amf3Array) = 9,
|
||||
}
|
||||
|
||||
impl Debug for Amf3 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
match self {
|
||||
Self::False => write!(f, "false"),
|
||||
Self::True => write!(f, "true"),
|
||||
Self::Double(x) => x.fmt(f),
|
||||
Self::String(x) => x.fmt(f),
|
||||
Self::Array (x) => x.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Deserialize<LE, R> for Amf3 {
|
||||
fn deserialize(reader: &mut R) -> Res<Self> {
|
||||
let mut reader = Amf3Reader { inner: reader, string_ref_table: vec![] };
|
||||
deser_amf3(&mut reader)
|
||||
}
|
||||
}
|
||||
|
||||
fn deser_amf3<R: Read>(reader: &mut Amf3Reader<R>) -> Res<Amf3> {
|
||||
let disc: u8 = LERead::read(reader)?;
|
||||
Ok(match disc {
|
||||
2 => Amf3::False,
|
||||
3 => Amf3::True,
|
||||
5 => Amf3::Double(LERead::read(reader)?),
|
||||
6 => Amf3::String(LERead::read(reader)?),
|
||||
9 => Amf3::Array (LERead::read(reader)?),
|
||||
_ => { return Err(Error::new(InvalidData, format!("invalid discriminant value for Amf3: {}", disc))) }
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a, W: Write> Serialize<LE, W> for &'a Amf3 {
|
||||
fn serialize(self, writer: &mut W) -> Res<()> {
|
||||
let mut writer = Amf3Writer { inner: writer, string_ref_table: vec![] };
|
||||
ser_amf3(&mut writer, self)
|
||||
}
|
||||
}
|
||||
|
||||
fn ser_amf3<W: Write>(writer: &mut Amf3Writer<W>, amf3: &Amf3) -> Res<()> {
|
||||
match amf3 {
|
||||
Amf3::False => LEWrite::write(writer, 2u8),
|
||||
Amf3::True => LEWrite::write(writer, 3u8),
|
||||
Amf3::Double(x) => { LEWrite::write(writer, 5u8)?; LEWrite::write(writer, x) },
|
||||
Amf3::String(x) => { LEWrite::write(writer, 6u8)?; LEWrite::write(writer, x) },
|
||||
Amf3::Array (x) => { LEWrite::write(writer, 9u8)?; LEWrite::write(writer, x) },
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Amf3 {
|
||||
fn from(b: bool) -> Self {
|
||||
if b { Self::True } else { Self::False }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for Amf3 {
|
||||
fn from(f: f32) -> Self {
|
||||
Self::Double(f.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Amf3 {
|
||||
fn from(f: f64) -> Self {
|
||||
Self::Double(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Amf3 {
|
||||
type Error = U29Error;
|
||||
|
||||
fn try_from(string: &str) -> Result<Self, Self::Error> {
|
||||
Ok(Self::String(string.try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use endio::{LERead, LEWrite};
|
||||
use super::U29;
|
||||
|
||||
#[test]
|
||||
fn test_u29() {
|
||||
for (bytes, integer) in &[(&b"\x7f"[..], 0x7f), (&b"\xa2\x43"[..], 4419), (&b"\x88\x00"[..], 1024), (&b"\xff\xff\x7e"[..], 0x1ffffe), (&b"\x80\xc0\x80\x00"[..], 0x200000), (&b"\xbf\xff\xff\xfe"[..], 0xffffffe)] {
|
||||
let mut reader = &bytes[..];
|
||||
let val: U29 = reader.read().unwrap();
|
||||
assert_eq!(val.0, *integer);
|
||||
let mut writer = vec![];
|
||||
writer.write(&val).unwrap();
|
||||
assert_eq!(&&writer[..], bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ use lu_packets_derive::{GameMessage, GmParam, VariantTests};
|
||||
|
||||
use crate::common::{ObjId, OBJID_EMPTY};
|
||||
|
||||
use crate::world::{CloneId, CLONE_ID_INVALID, Lot, LOT_NULL, MapId, MAP_ID_INVALID, Quaternion, Vector3, ZoneId};
|
||||
use crate::world::lnv::LuNameValue;
|
||||
use crate::world::{CloneId, CLONE_ID_INVALID, Lot, LOT_NULL, LuNameValue, MapId, MAP_ID_INVALID, Quaternion, Vector3, ZoneId};
|
||||
use crate::world::amf3::Amf3;
|
||||
use super::{EquipInventory, GmString, GmWString, InventoryType, KillType, UnEquipInventory, MissionState, PetNotificationType, MoveItemInInventory, MoveInventoryBatch, RemoveSkill, SetIgnoreProjectileCollision};
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
@@ -128,6 +128,7 @@ pub enum GameMessage {
|
||||
EchoSyncSkill(EchoSyncSkill) = 1144,
|
||||
DoClientProjectileImpact(DoClientProjectileImpact) = 1151,
|
||||
SetPlayerAllowedRespawn(SetPlayerAllowedRespawn) = 1165,
|
||||
UiMessageServerToSingleClient(UiMessageServerToSingleClient) = 1184,
|
||||
UncastSkill(UncastSkill) = 1206,
|
||||
FireEventClientSide(FireEventClientSide) = 1213,
|
||||
ChangeObjectWorldState(ChangeObjectWorldState) = 1223,
|
||||
@@ -1247,6 +1248,12 @@ pub struct SetPlayerAllowedRespawn {
|
||||
pub dont_prompt_for_respawn: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, GameMessage, PartialEq)]
|
||||
pub struct UiMessageServerToSingleClient {
|
||||
pub args: Amf3,
|
||||
pub message_name: GmString,
|
||||
}
|
||||
|
||||
#[derive(Debug, GameMessage, PartialEq)]
|
||||
pub struct UncastSkill {
|
||||
pub skill_id: i32, // todo: type
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
GameMessage::UiMessageServerToSingleClient(
|
||||
UiMessageServerToSingleClient {
|
||||
args: amf3! {
|
||||
"false": false,
|
||||
"true": true,
|
||||
"double": 3.14,
|
||||
"string": "string",
|
||||
"array": amf3! ["inner", "array", true],
|
||||
},
|
||||
message_name: lu!(&b"QueueChoiceBox"[..]),
|
||||
},
|
||||
)
|
||||
+1
-1
@@ -11,7 +11,7 @@ use super::gm::GmParam;
|
||||
|
||||
use crate::common::{LuStrExt, LuVarString, LuVarWString, LuWStr};
|
||||
|
||||
/// A value contained in a [`LuNameValue`]
|
||||
/// A value contained in a [`LuNameValue`].
|
||||
#[derive(Deserialize, PartialEq, Serialize)]
|
||||
#[repr(u8)]
|
||||
pub enum LnvValue {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! World messages.
|
||||
pub mod client;
|
||||
pub mod gm;
|
||||
pub mod amf3;
|
||||
mod lnv;
|
||||
pub mod server;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user