Refactor: Amf3 implementation (#998)

* Update AMFDeserializeTests.cpp

Redo Amf3 functionality

Overhaul the whole thing due to it being outdated and clunky to use

Sometimes you want to keep the value

Update AMFDeserializeTests.cpp

* Fix enum and constructors

Correct enum to a class and simplify names.
Add a proper default constructor

* Update MasterServer.cpp

* Fix bugs and add more tests

* Refactor: AMF with templates in mind

- Remove hard coded bodge
- Use templates and generics to allow for much looser typing and strengthened implementation
- Move code into header only implementation for portability

Refactor: Convert AMF implementation to templates

- Rip out previous implementation
- Remove all extraneous terminology
- Add proper overloads for all types of inserts
- Fix up tests and codebase

* Fix compiler errors

* Check for null first

* Add specialization for const char*

* Update tests for new template specialization

* Switch BitStream to use references

* Rename files

* Check enum bounds on deserialize

I did this on a phone
This commit is contained in:
David Markowitz 2023-05-13 15:22:00 -07:00 committed by GitHub
parent 9d105a287d
commit 4fe335cc66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1081 additions and 1420 deletions

View File

@ -1,77 +1,81 @@
#include "AMFDeserialize.h"
#include "AMFFormat.h"
#include <stdexcept>
#include "Amf3.h"
/**
* AMF3 Reference document https://rtmp.veriskope.com/pdf/amf3-file-format-spec.pdf
* AMF3 Deserializer written by EmosewaMC
*/
AMFValue* AMFDeserialize::Read(RakNet::BitStream* inStream) {
AMFBaseValue* AMFDeserialize::Read(RakNet::BitStream* inStream) {
if (!inStream) return nullptr;
AMFValue* returnValue = nullptr;
AMFBaseValue* returnValue = nullptr;
// Read in the value type from the bitStream
int8_t marker;
inStream->Read(marker);
uint8_t i;
inStream->Read(i);
if (i > static_cast<uint8_t>(eAmf::Dictionary)) return nullptr;
eAmf marker = static_cast<eAmf>(i);
// Based on the typing, create the value associated with that and return the base value class
switch (marker) {
case AMFValueType::AMFUndefined: {
returnValue = new AMFUndefinedValue();
case eAmf::Undefined: {
returnValue = new AMFBaseValue();
break;
}
case AMFValueType::AMFNull: {
case eAmf::Null: {
returnValue = new AMFNullValue();
break;
}
case AMFValueType::AMFFalse: {
returnValue = new AMFFalseValue();
case eAmf::False: {
returnValue = new AMFBoolValue(false);
break;
}
case AMFValueType::AMFTrue: {
returnValue = new AMFTrueValue();
case eAmf::True: {
returnValue = new AMFBoolValue(true);
break;
}
case AMFValueType::AMFInteger: {
case eAmf::Integer: {
returnValue = ReadAmfInteger(inStream);
break;
}
case AMFValueType::AMFDouble: {
case eAmf::Double: {
returnValue = ReadAmfDouble(inStream);
break;
}
case AMFValueType::AMFString: {
case eAmf::String: {
returnValue = ReadAmfString(inStream);
break;
}
case AMFValueType::AMFArray: {
case eAmf::Array: {
returnValue = ReadAmfArray(inStream);
break;
}
// TODO We do not need these values, but if someone wants to implement them
// then please do so and add the corresponding unit tests.
case AMFValueType::AMFXMLDoc:
case AMFValueType::AMFDate:
case AMFValueType::AMFObject:
case AMFValueType::AMFXML:
case AMFValueType::AMFByteArray:
case AMFValueType::AMFVectorInt:
case AMFValueType::AMFVectorUInt:
case AMFValueType::AMFVectorDouble:
case AMFValueType::AMFVectorObject:
case AMFValueType::AMFDictionary: {
throw static_cast<AMFValueType>(marker);
// These values are unimplemented in the live client and will remain unimplemented
// unless someone modifies the client to allow serializing of these values.
case eAmf::XMLDoc:
case eAmf::Date:
case eAmf::Object:
case eAmf::XML:
case eAmf::ByteArray:
case eAmf::VectorInt:
case eAmf::VectorUInt:
case eAmf::VectorDouble:
case eAmf::VectorObject:
case eAmf::Dictionary: {
throw marker;
break;
}
default:
throw static_cast<AMFValueType>(marker);
throw std::invalid_argument("Invalid AMF3 marker" + std::to_string(static_cast<int32_t>(marker)));
break;
}
return returnValue;
@ -99,7 +103,7 @@ uint32_t AMFDeserialize::ReadU29(RakNet::BitStream* inStream) {
return actualNumber;
}
std::string AMFDeserialize::ReadString(RakNet::BitStream* inStream) {
const std::string AMFDeserialize::ReadString(RakNet::BitStream* inStream) {
auto length = ReadU29(inStream);
// Check if this is a reference
bool isReference = length % 2 == 1;
@ -113,48 +117,39 @@ std::string AMFDeserialize::ReadString(RakNet::BitStream* inStream) {
return value;
} else {
// Length is a reference to a previous index - use that as the read in value
return accessedElements[length];
return accessedElements.at(length);
}
}
AMFValue* AMFDeserialize::ReadAmfDouble(RakNet::BitStream* inStream) {
auto doubleValue = new AMFDoubleValue();
AMFBaseValue* AMFDeserialize::ReadAmfDouble(RakNet::BitStream* inStream) {
double value;
inStream->Read<double>(value);
doubleValue->SetDoubleValue(value);
return doubleValue;
return new AMFDoubleValue(value);
}
AMFValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream* inStream) {
AMFBaseValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream* inStream) {
auto arrayValue = new AMFArrayValue();
// Read size of dense array
auto sizeOfDenseArray = (ReadU29(inStream) >> 1);
// Then read Key'd portion
// Then read associative portion
while (true) {
auto key = ReadString(inStream);
// No more values when we encounter an empty string
// No more associative values when we encounter an empty string key
if (key.size() == 0) break;
arrayValue->InsertValue(key, Read(inStream));
arrayValue->Insert(key, Read(inStream));
}
// Finally read dense portion
for (uint32_t i = 0; i < sizeOfDenseArray; i++) {
arrayValue->PushBackValue(Read(inStream));
arrayValue->Insert(i, Read(inStream));
}
return arrayValue;
}
AMFValue* AMFDeserialize::ReadAmfString(RakNet::BitStream* inStream) {
auto stringValue = new AMFStringValue();
stringValue->SetStringValue(ReadString(inStream));
return stringValue;
AMFBaseValue* AMFDeserialize::ReadAmfString(RakNet::BitStream* inStream) {
return new AMFStringValue(ReadString(inStream));
}
AMFValue* AMFDeserialize::ReadAmfInteger(RakNet::BitStream* inStream) {
auto integerValue = new AMFIntegerValue();
integerValue->SetIntegerValue(ReadU29(inStream));
return integerValue;
AMFBaseValue* AMFDeserialize::ReadAmfInteger(RakNet::BitStream* inStream) {
return new AMFIntValue(ReadU29(inStream));
}

View File

@ -5,7 +5,8 @@
#include <vector>
#include <string>
class AMFValue;
class AMFBaseValue;
class AMFDeserialize {
public:
/**
@ -14,7 +15,7 @@ public:
* @param inStream inStream to read value from.
* @return Returns an AMFValue with all the information from the bitStream in it.
*/
AMFValue* Read(RakNet::BitStream* inStream);
AMFBaseValue* Read(RakNet::BitStream* inStream);
private:
/**
* @brief Private method to read a U29 integer from a bitstream
@ -30,7 +31,7 @@ private:
* @param inStream bitStream to read data from
* @return The read string
*/
std::string ReadString(RakNet::BitStream* inStream);
const std::string ReadString(RakNet::BitStream* inStream);
/**
* @brief Read an AMFDouble value from a bitStream
@ -38,7 +39,7 @@ private:
* @param inStream bitStream to read data from
* @return Double value represented as an AMFValue
*/
AMFValue* ReadAmfDouble(RakNet::BitStream* inStream);
AMFBaseValue* ReadAmfDouble(RakNet::BitStream* inStream);
/**
* @brief Read an AMFArray from a bitStream
@ -46,7 +47,7 @@ private:
* @param inStream bitStream to read data from
* @return Array value represented as an AMFValue
*/
AMFValue* ReadAmfArray(RakNet::BitStream* inStream);
AMFBaseValue* ReadAmfArray(RakNet::BitStream* inStream);
/**
* @brief Read an AMFString from a bitStream
@ -54,7 +55,7 @@ private:
* @param inStream bitStream to read data from
* @return String value represented as an AMFValue
*/
AMFValue* ReadAmfString(RakNet::BitStream* inStream);
AMFBaseValue* ReadAmfString(RakNet::BitStream* inStream);
/**
* @brief Read an AMFInteger from a bitStream
@ -62,7 +63,7 @@ private:
* @param inStream bitStream to read data from
* @return Integer value represented as an AMFValue
*/
AMFValue* ReadAmfInteger(RakNet::BitStream* inStream);
AMFBaseValue* ReadAmfInteger(RakNet::BitStream* inStream);
/**
* List of strings read so far saved to be read by reference.

View File

@ -1,156 +0,0 @@
#include "AMFFormat.h"
// AMFInteger
void AMFIntegerValue::SetIntegerValue(uint32_t value) {
this->value = value;
}
uint32_t AMFIntegerValue::GetIntegerValue() {
return this->value;
}
// AMFDouble
void AMFDoubleValue::SetDoubleValue(double value) {
this->value = value;
}
double AMFDoubleValue::GetDoubleValue() {
return this->value;
}
// AMFString
void AMFStringValue::SetStringValue(const std::string& value) {
this->value = value;
}
std::string AMFStringValue::GetStringValue() {
return this->value;
}
// AMFXMLDoc
void AMFXMLDocValue::SetXMLDocValue(const std::string& value) {
this->xmlData = value;
}
std::string AMFXMLDocValue::GetXMLDocValue() {
return this->xmlData;
}
// AMFDate
void AMFDateValue::SetDateValue(uint64_t value) {
this->millisecondTimestamp = value;
}
uint64_t AMFDateValue::GetDateValue() {
return this->millisecondTimestamp;
}
// AMFArray Insert Value
void AMFArrayValue::InsertValue(const std::string& key, AMFValue* value) {
this->associative.insert(std::make_pair(key, value));
}
// AMFArray Remove Value
void AMFArrayValue::RemoveValue(const std::string& key) {
_AMFArrayMap_::iterator it = this->associative.find(key);
if (it != this->associative.end()) {
this->associative.erase(it);
}
}
// AMFArray Get Associative Iterator Begin
_AMFArrayMap_::iterator AMFArrayValue::GetAssociativeIteratorValueBegin() {
return this->associative.begin();
}
// AMFArray Get Associative Iterator End
_AMFArrayMap_::iterator AMFArrayValue::GetAssociativeIteratorValueEnd() {
return this->associative.end();
}
// AMFArray Push Back Value
void AMFArrayValue::PushBackValue(AMFValue* value) {
this->dense.push_back(value);
}
// AMFArray Pop Back Value
void AMFArrayValue::PopBackValue() {
this->dense.pop_back();
}
// AMFArray Get Dense List Size
uint32_t AMFArrayValue::GetDenseValueSize() {
return (uint32_t)this->dense.size();
}
// AMFArray Get Dense Iterator Begin
_AMFArrayList_::iterator AMFArrayValue::GetDenseIteratorBegin() {
return this->dense.begin();
}
// AMFArray Get Dense Iterator End
_AMFArrayList_::iterator AMFArrayValue::GetDenseIteratorEnd() {
return this->dense.end();
}
AMFArrayValue::~AMFArrayValue() {
for (auto valueToDelete : GetDenseArray()) {
if (valueToDelete) delete valueToDelete;
}
for (auto valueToDelete : GetAssociativeMap()) {
if (valueToDelete.second) delete valueToDelete.second;
}
}
// AMFObject Constructor
AMFObjectValue::AMFObjectValue(std::vector<std::pair<std::string, AMFValueType>> traits) {
this->traits.reserve(traits.size());
std::vector<std::pair<std::string, AMFValueType>>::iterator it = traits.begin();
while (it != traits.end()) {
this->traits.insert(std::make_pair(it->first, std::make_pair(it->second, new AMFNullValue())));
it++;
}
}
// AMFObject Set Value
void AMFObjectValue::SetTraitValue(const std::string& trait, AMFValue* value) {
if (value) {
_AMFObjectTraits_::iterator it = this->traits.find(trait);
if (it != this->traits.end()) {
if (it->second.first == value->GetValueType()) {
it->second.second = value;
}
}
}
}
// AMFObject Get Value
AMFValue* AMFObjectValue::GetTraitValue(const std::string& trait) {
_AMFObjectTraits_::iterator it = this->traits.find(trait);
if (it != this->traits.end()) {
return it->second.second;
}
return nullptr;
}
// AMFObject Get Trait Iterator Begin
_AMFObjectTraits_::iterator AMFObjectValue::GetTraitsIteratorBegin() {
return this->traits.begin();
}
// AMFObject Get Trait Iterator End
_AMFObjectTraits_::iterator AMFObjectValue::GetTraitsIteratorEnd() {
return this->traits.end();
}
// AMFObject Get Trait Size
uint32_t AMFObjectValue::GetTraitArrayCount() {
return (uint32_t)this->traits.size();
}
AMFObjectValue::~AMFObjectValue() {
for (auto valueToDelete = GetTraitsIteratorBegin(); valueToDelete != GetTraitsIteratorEnd(); valueToDelete++) {
if (valueToDelete->second.second) delete valueToDelete->second.second;
}
}

View File

@ -1,413 +0,0 @@
#pragma once
// Custom Classes
#include "dCommonVars.h"
// C++
#include <unordered_map>
#include <vector>
/*!
\file AMFFormat.hpp
\brief A class for managing AMF values
*/
class AMFValue; // Forward declaration
// Definitions
#define _AMFArrayMap_ std::unordered_map<std::string, AMFValue*>
#define _AMFArrayList_ std::vector<AMFValue*>
#define _AMFObjectTraits_ std::unordered_map<std::string, std::pair<AMFValueType, AMFValue*>>
#define _AMFObjectDynamicTraits_ std::unordered_map<std::string, AMFValue*>
//! An enum for each AMF value type
enum AMFValueType : unsigned char {
AMFUndefined = 0x00, //!< An undefined AMF Value
AMFNull = 0x01, //!< A null AMF value
AMFFalse = 0x02, //!< A false AMF value
AMFTrue = 0x03, //!< A true AMF value
AMFInteger = 0x04, //!< An integer AMF value
AMFDouble = 0x05, //!< A double AMF value
AMFString = 0x06, //!< A string AMF value
AMFXMLDoc = 0x07, //!< An XML Doc AMF value
AMFDate = 0x08, //!< A date AMF value
AMFArray = 0x09, //!< An array AMF value
AMFObject = 0x0A, //!< An object AMF value
AMFXML = 0x0B, //!< An XML AMF value
AMFByteArray = 0x0C, //!< A byte array AMF value
AMFVectorInt = 0x0D, //!< An integer vector AMF value
AMFVectorUInt = 0x0E, //!< An unsigned integer AMF value
AMFVectorDouble = 0x0F, //!< A double vector AMF value
AMFVectorObject = 0x10, //!< An object vector AMF value
AMFDictionary = 0x11 //!< A dictionary AMF value
};
//! An enum for the object value types
enum AMFObjectValueType : unsigned char {
AMFObjectAnonymous = 0x01,
AMFObjectTyped = 0x02,
AMFObjectDynamic = 0x03,
AMFObjectExternalizable = 0x04
};
//! The base AMF value class
class AMFValue {
public:
//! Returns the AMF value type
/*!
\return The AMF value type
*/
virtual AMFValueType GetValueType() = 0;
virtual ~AMFValue() {};
};
//! A typedef for a pointer to an AMF value
typedef AMFValue* NDGFxValue;
// The various AMF value types
//! The undefined value AMF type
class AMFUndefinedValue : public AMFValue {
private:
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFUndefined;
};
//! The null value AMF type
class AMFNullValue : public AMFValue {
private:
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFNull;
};
//! The false value AMF type
class AMFFalseValue : public AMFValue {
private:
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFFalse;
};
//! The true value AMF type
class AMFTrueValue : public AMFValue {
private:
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFTrue;
};
//! The integer value AMF type
class AMFIntegerValue : public AMFValue {
private:
uint32_t value; //!< The value of the AMF type
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFInteger;
//! Sets the integer value
/*!
\param value The value to set
*/
void SetIntegerValue(uint32_t value);
//! Gets the integer value
/*!
\return The integer value
*/
uint32_t GetIntegerValue();
};
//! The double value AMF type
class AMFDoubleValue : public AMFValue {
private:
double value; //!< The value of the AMF type
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFDouble;
//! Sets the double value
/*!
\param value The value to set to
*/
void SetDoubleValue(double value);
//! Gets the double value
/*!
\return The double value
*/
double GetDoubleValue();
};
//! The string value AMF type
class AMFStringValue : public AMFValue {
private:
std::string value; //!< The value of the AMF type
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFString;
//! Sets the string value
/*!
\param value The string value to set to
*/
void SetStringValue(const std::string& value);
//! Gets the string value
/*!
\return The string value
*/
std::string GetStringValue();
};
//! The XML doc value AMF type
class AMFXMLDocValue : public AMFValue {
private:
std::string xmlData; //!< The value of the AMF type
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFXMLDoc;
//! Sets the XML Doc value
/*!
\param value The value to set to
*/
void SetXMLDocValue(const std::string& value);
//! Gets the XML Doc value
/*!
\return The XML Doc value
*/
std::string GetXMLDocValue();
};
//! The date value AMF type
class AMFDateValue : public AMFValue {
private:
uint64_t millisecondTimestamp; //!< The time in milliseconds since the ephoch
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFDate;
//! Sets the date time
/*!
\param value The value to set to
*/
void SetDateValue(uint64_t value);
//! Gets the date value
/*!
\return The date value in milliseconds since the epoch
*/
uint64_t GetDateValue();
};
//! The array value AMF type
// This object will manage it's own memory map and list. Do not delete its values.
class AMFArrayValue : public AMFValue {
private:
_AMFArrayMap_ associative; //!< The array map (associative part)
_AMFArrayList_ dense; //!< The array list (dense part)
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() override { return ValueType; }
public:
static const AMFValueType ValueType = AMFArray;
~AMFArrayValue() override;
//! Inserts an item into the array map for a specific key
/*!
\param key The key to set
\param value The value to add
*/
void InsertValue(const std::string& key, AMFValue* value);
//! Removes an item for a specific key
/*!
\param key The key to remove
*/
void RemoveValue(const std::string& key);
//! Finds an AMF value
/*!
\return The AMF value if found, nullptr otherwise
*/
template <typename T>
T* FindValue(const std::string& key) const {
_AMFArrayMap_::const_iterator it = this->associative.find(key);
if (it != this->associative.end() && T::ValueType == it->second->GetValueType()) {
return dynamic_cast<T*>(it->second);
}
return nullptr;
};
//! Returns where the associative iterator begins
/*!
\return Where the array map iterator begins
*/
_AMFArrayMap_::iterator GetAssociativeIteratorValueBegin();
//! Returns where the associative iterator ends
/*!
\return Where the array map iterator ends
*/
_AMFArrayMap_::iterator GetAssociativeIteratorValueEnd();
//! Pushes back a value into the array list
/*!
\param value The value to push back
*/
void PushBackValue(AMFValue* value);
//! Pops back the last value in the array list
void PopBackValue();
//! Gets the count of the dense list
/*!
\return The dense list size
*/
uint32_t GetDenseValueSize();
//! Gets a specific value from the list for the specified index
/*!
\param index The index to get
*/
template <typename T>
T* GetValueAt(uint32_t index) {
if (index >= this->dense.size()) return nullptr;
AMFValue* foundValue = this->dense.at(index);
return T::ValueType == foundValue->GetValueType() ? dynamic_cast<T*>(foundValue) : nullptr;
};
//! Returns where the dense iterator begins
/*!
\return Where the iterator begins
*/
_AMFArrayList_::iterator GetDenseIteratorBegin();
//! Returns where the dense iterator ends
/*!
\return Where the iterator ends
*/
_AMFArrayList_::iterator GetDenseIteratorEnd();
//! Returns the associative map
/*!
\return The associative map
*/
_AMFArrayMap_ GetAssociativeMap() { return this->associative; };
//! Returns the dense array
/*!
\return The dense array
*/
_AMFArrayList_ GetDenseArray() { return this->dense; };
};
//! The anonymous object value AMF type
class AMFObjectValue : public AMFValue {
private:
_AMFObjectTraits_ traits; //!< The object traits
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() override { return ValueType; }
~AMFObjectValue() override;
public:
static const AMFValueType ValueType = AMFObject;
//! Constructor
/*!
\param traits The traits to set
*/
AMFObjectValue(std::vector<std::pair<std::string, AMFValueType>> traits);
//! Gets the object value type
/*!
\return The object value type
*/
virtual AMFObjectValueType GetObjectValueType() { return AMFObjectAnonymous; }
//! Sets the value of a trait
/*!
\param trait The trait to set the value for
\param value The AMF value to set
*/
void SetTraitValue(const std::string& trait, AMFValue* value);
//! Gets a trait value
/*!
\param trait The trait to get the value for
\return The trait value
*/
AMFValue* GetTraitValue(const std::string& trait);
//! Gets the beginning of the object traits iterator
/*!
\return The AMF trait array iterator begin
*/
_AMFObjectTraits_::iterator GetTraitsIteratorBegin();
//! Gets the end of the object traits iterator
/*!
\return The AMF trait array iterator begin
*/
_AMFObjectTraits_::iterator GetTraitsIteratorEnd();
//! Gets the amount of traits
/*!
\return The amount of traits
*/
uint32_t GetTraitArrayCount();
};

View File

@ -1,259 +0,0 @@
#include "AMFFormat_BitStream.h"
// Writes an AMFValue pointer to a RakNet::BitStream
template<>
void RakNet::BitStream::Write<AMFValue*>(AMFValue* value) {
if (value != nullptr) {
AMFValueType type = value->GetValueType();
switch (type) {
case AMFUndefined: {
AMFUndefinedValue* v = (AMFUndefinedValue*)value;
this->Write(*v);
break;
}
case AMFNull: {
AMFNullValue* v = (AMFNullValue*)value;
this->Write(*v);
break;
}
case AMFFalse: {
AMFFalseValue* v = (AMFFalseValue*)value;
this->Write(*v);
break;
}
case AMFTrue: {
AMFTrueValue* v = (AMFTrueValue*)value;
this->Write(*v);
break;
}
case AMFInteger: {
AMFIntegerValue* v = (AMFIntegerValue*)value;
this->Write(*v);
break;
}
case AMFDouble: {
AMFDoubleValue* v = (AMFDoubleValue*)value;
this->Write(*v);
break;
}
case AMFString: {
AMFStringValue* v = (AMFStringValue*)value;
this->Write(*v);
break;
}
case AMFXMLDoc: {
AMFXMLDocValue* v = (AMFXMLDocValue*)value;
this->Write(*v);
break;
}
case AMFDate: {
AMFDateValue* v = (AMFDateValue*)value;
this->Write(*v);
break;
}
case AMFArray: {
this->Write((AMFArrayValue*)value);
break;
}
case AMFObject:
case AMFXML:
case AMFByteArray:
case AMFVectorInt:
case AMFVectorUInt:
case AMFVectorDouble:
case AMFVectorObject:
case AMFDictionary:
break;
}
}
}
/**
* A private function to write an value to a RakNet::BitStream
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
unsigned char b4 = (unsigned char)v;
if (v < 0x00200000) {
b4 = b4 & 0x7F;
if (v > 0x7F) {
unsigned char b3;
v = v >> 7;
b3 = ((unsigned char)(v)) | 0x80;
if (v > 0x7F) {
unsigned char b2;
v = v >> 7;
b2 = ((unsigned char)(v)) | 0x80;
bs->Write(b2);
}
bs->Write(b3);
}
} else {
unsigned char b1;
unsigned char b2;
unsigned char b3;
v = v >> 8;
b3 = ((unsigned char)(v)) | 0x80;
v = v >> 7;
b2 = ((unsigned char)(v)) | 0x80;
v = v >> 7;
b1 = ((unsigned char)(v)) | 0x80;
bs->Write(b1);
bs->Write(b2);
bs->Write(b3);
}
bs->Write(b4);
}
/**
* Writes a flag number to a RakNet::BitStream
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteFlagNumber(RakNet::BitStream* bs, uint32_t v) {
v = (v << 1) | 0x01;
WriteUInt29(bs, v);
}
/**
* Writes an AMFString to a RakNet::BitStream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFString(RakNet::BitStream* bs, const std::string& str) {
WriteFlagNumber(bs, (uint32_t)str.size());
bs->Write(str.c_str(), (uint32_t)str.size());
}
/**
* Writes an U16 to a bitstream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFU16(RakNet::BitStream* bs, uint16_t value) {
bs->Write(value);
}
/**
* Writes an U32 to a bitstream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFU32(RakNet::BitStream* bs, uint32_t value) {
bs->Write(value);
}
/**
* Writes an U64 to a bitstream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFU64(RakNet::BitStream* bs, uint64_t value) {
bs->Write(value);
}
// Writes an AMFUndefinedValue to BitStream
template<>
void RakNet::BitStream::Write<AMFUndefinedValue>(AMFUndefinedValue value) {
this->Write(AMFUndefined);
}
// Writes an AMFNullValue to BitStream
template<>
void RakNet::BitStream::Write<AMFNullValue>(AMFNullValue value) {
this->Write(AMFNull);
}
// Writes an AMFFalseValue to BitStream
template<>
void RakNet::BitStream::Write<AMFFalseValue>(AMFFalseValue value) {
this->Write(AMFFalse);
}
// Writes an AMFTrueValue to BitStream
template<>
void RakNet::BitStream::Write<AMFTrueValue>(AMFTrueValue value) {
this->Write(AMFTrue);
}
// Writes an AMFIntegerValue to BitStream
template<>
void RakNet::BitStream::Write<AMFIntegerValue>(AMFIntegerValue value) {
this->Write(AMFInteger);
WriteUInt29(this, value.GetIntegerValue());
}
// Writes an AMFDoubleValue to BitStream
template<>
void RakNet::BitStream::Write<AMFDoubleValue>(AMFDoubleValue value) {
this->Write(AMFDouble);
double d = value.GetDoubleValue();
WriteAMFU64(this, *((unsigned long long*) & d));
}
// Writes an AMFStringValue to BitStream
template<>
void RakNet::BitStream::Write<AMFStringValue>(AMFStringValue value) {
this->Write(AMFString);
std::string v = value.GetStringValue();
WriteAMFString(this, v);
}
// Writes an AMFXMLDocValue to BitStream
template<>
void RakNet::BitStream::Write<AMFXMLDocValue>(AMFXMLDocValue value) {
this->Write(AMFXMLDoc);
std::string v = value.GetXMLDocValue();
WriteAMFString(this, v);
}
// Writes an AMFDateValue to BitStream
template<>
void RakNet::BitStream::Write<AMFDateValue>(AMFDateValue value) {
this->Write(AMFDate);
uint64_t date = value.GetDateValue();
WriteAMFU64(this, date);
}
// Writes an AMFArrayValue to BitStream
template<>
void RakNet::BitStream::Write<AMFArrayValue*>(AMFArrayValue* value) {
this->Write(AMFArray);
uint32_t denseSize = value->GetDenseValueSize();
WriteFlagNumber(this, denseSize);
_AMFArrayMap_::iterator it = value->GetAssociativeIteratorValueBegin();
_AMFArrayMap_::iterator end = value->GetAssociativeIteratorValueEnd();
while (it != end) {
WriteAMFString(this, it->first);
this->Write(it->second);
it++;
}
this->Write(AMFNull);
if (denseSize > 0) {
_AMFArrayList_::iterator it2 = value->GetDenseIteratorBegin();
_AMFArrayList_::iterator end2 = value->GetDenseIteratorEnd();
while (it2 != end2) {
this->Write(*it2);
it2++;
}
}
}

View File

@ -1,92 +0,0 @@
#pragma once
// Custom Classes
#include "AMFFormat.h"
// RakNet
#include <BitStream.h>
/*!
\file AMFFormat_BitStream.h
\brief A class that implements native writing of AMF values to RakNet::BitStream
*/
// We are using the RakNet namespace
namespace RakNet {
//! Writes an AMFValue pointer to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFValue*>(AMFValue* value);
//! Writes an AMFUndefinedValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFUndefinedValue>(AMFUndefinedValue value);
//! Writes an AMFNullValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFNullValue>(AMFNullValue value);
//! Writes an AMFFalseValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFFalseValue>(AMFFalseValue value);
//! Writes an AMFTrueValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFTrueValue>(AMFTrueValue value);
//! Writes an AMFIntegerValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFIntegerValue>(AMFIntegerValue value);
//! Writes an AMFDoubleValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFDoubleValue>(AMFDoubleValue value);
//! Writes an AMFStringValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFStringValue>(AMFStringValue value);
//! Writes an AMFXMLDocValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFXMLDocValue>(AMFXMLDocValue value);
//! Writes an AMFDateValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFDateValue>(AMFDateValue value);
//! Writes an AMFArrayValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFArrayValue*>(AMFArrayValue* value);
} // namespace RakNet

368
dCommon/Amf3.h Normal file
View File

@ -0,0 +1,368 @@
#ifndef __AMF3__H__
#define __AMF3__H__
#include "dCommonVars.h"
#include "dLogger.h"
#include "Game.h"
#include <unordered_map>
#include <vector>
enum class eAmf : uint8_t {
Undefined = 0x00, // An undefined AMF Value
Null = 0x01, // A null AMF value
False = 0x02, // A false AMF value
True = 0x03, // A true AMF value
Integer = 0x04, // An integer AMF value
Double = 0x05, // A double AMF value
String = 0x06, // A string AMF value
XMLDoc = 0x07, // Unused in the live client and cannot be serialized without modification. An XML Doc AMF value
Date = 0x08, // Unused in the live client and cannot be serialized without modification. A date AMF value
Array = 0x09, // An array AMF value
Object = 0x0A, // Unused in the live client and cannot be serialized without modification. An object AMF value
XML = 0x0B, // Unused in the live client and cannot be serialized without modification. An XML AMF value
ByteArray = 0x0C, // Unused in the live client and cannot be serialized without modification. A byte array AMF value
VectorInt = 0x0D, // Unused in the live client and cannot be serialized without modification. An integer vector AMF value
VectorUInt = 0x0E, // Unused in the live client and cannot be serialized without modification. An unsigned integer AMF value
VectorDouble = 0x0F, // Unused in the live client and cannot be serialized without modification. A double vector AMF value
VectorObject = 0x10, // Unused in the live client and cannot be serialized without modification. An object vector AMF value
Dictionary = 0x11 // Unused in the live client and cannot be serialized without modification. A dictionary AMF value
};
class AMFBaseValue {
public:
virtual eAmf GetValueType() { return eAmf::Undefined; };
AMFBaseValue() {};
virtual ~AMFBaseValue() {};
};
template<typename ValueType>
class AMFValue : public AMFBaseValue {
public:
AMFValue() {};
AMFValue(ValueType value) { SetValue(value); };
virtual ~AMFValue() override {};
eAmf GetValueType() override { return eAmf::Undefined; };
const ValueType& GetValue() { return data; };
void SetValue(ValueType value) { data = value; };
protected:
ValueType data;
};
// As a string this is much easier to write and read from a BitStream.
template<>
class AMFValue<const char*> : public AMFBaseValue {
public:
AMFValue() {};
AMFValue(const char* value) { SetValue(std::string(value)); };
virtual ~AMFValue() override {};
eAmf GetValueType() override { return eAmf::String; };
const std::string& GetValue() { return data; };
void SetValue(std::string value) { data = value; };
protected:
std::string data;
};
typedef AMFValue<std::nullptr_t> AMFNullValue;
typedef AMFValue<bool> AMFBoolValue;
typedef AMFValue<int32_t> AMFIntValue;
typedef AMFValue<std::string> AMFStringValue;
typedef AMFValue<double> AMFDoubleValue;
template<> inline eAmf AMFValue<std::nullptr_t>::GetValueType() { return eAmf::Null; };
template<> inline eAmf AMFValue<bool>::GetValueType() { return this->data ? eAmf::True : eAmf::False; };
template<> inline eAmf AMFValue<int32_t>::GetValueType() { return eAmf::Integer; };
template<> inline eAmf AMFValue<uint32_t>::GetValueType() { return eAmf::Integer; };
template<> inline eAmf AMFValue<std::string>::GetValueType() { return eAmf::String; };
template<> inline eAmf AMFValue<double>::GetValueType() { return eAmf::Double; };
/**
* The AMFArrayValue object holds 2 types of lists:
* An associative list where a key maps to a value
* A Dense list where elements are stored back to back
*
* Objects that are Registered are owned by this object
* and are not to be deleted by a caller.
*/
class AMFArrayValue : public AMFBaseValue {
typedef std::unordered_map<std::string, AMFBaseValue*> AMFAssociative;
typedef std::vector<AMFBaseValue*> AMFDense;
public:
eAmf GetValueType() override { return eAmf::Array; };
~AMFArrayValue() override {
for (auto valueToDelete : GetDense()) {
if (valueToDelete) {
delete valueToDelete;
valueToDelete = nullptr;
}
}
for (auto valueToDelete : GetAssociative()) {
if (valueToDelete.second) {
delete valueToDelete.second;
valueToDelete.second = nullptr;
}
}
};
/**
* Returns the Associative portion of the object
*/
inline AMFAssociative& GetAssociative() { return this->associative; };
/**
* Returns the dense portion of the object
*/
inline AMFDense& GetDense() { return this->dense; };
/**
* Inserts an AMFValue into the associative portion with the given key.
* If a duplicate is attempted to be inserted, it is ignored and the
* first value with that key is kept in the map.
*
* These objects are not to be deleted by the caller as they are owned by
* the AMFArray object which manages its own memory.
*
* @param key The key to associate with the value
* @param value The value to insert
*
* @return The inserted element if the type matched,
* or nullptr if a key existed and was not the same type
*/
template<typename ValueType>
std::pair<AMFValue<ValueType>*, bool> Insert(const std::string& key, ValueType value) {
auto element = associative.find(key);
AMFValue<ValueType>* val = nullptr;
bool found = true;
if (element == associative.end()) {
val = new AMFValue<ValueType>(value);
associative.insert(std::make_pair(key, val));
} else {
val = dynamic_cast<AMFValue<ValueType>*>(element->second);
found = false;
}
return std::make_pair(val, found);
};
// Associates an array with a string key
std::pair<AMFBaseValue*, bool> Insert(const std::string& key) {
auto element = associative.find(key);
AMFArrayValue* val = nullptr;
bool found = true;
if (element == associative.end()) {
val = new AMFArrayValue();
associative.insert(std::make_pair(key, val));
} else {
val = dynamic_cast<AMFArrayValue*>(element->second);
found = false;
}
return std::make_pair(val, found);
};
// Associates an array with an integer key
std::pair<AMFBaseValue*, bool> Insert(const uint32_t& index) {
AMFArrayValue* val = nullptr;
bool inserted = false;
if (index >= dense.size()) {
dense.resize(index + 1);
val = new AMFArrayValue();
dense.at(index) = val;
inserted = true;
}
return std::make_pair(dynamic_cast<AMFArrayValue*>(dense.at(index)), inserted);
};
/**
* @brief Inserts an AMFValue into the AMFArray key'd by index.
* Attempting to insert the same key to the same value twice overwrites
* the previous value with the new one.
*
* @param index The index to associate with the value
* @param value The value to insert
* @return The inserted element, or nullptr if the type did not match
* what was at the index.
*/
template<typename ValueType>
std::pair<AMFValue<ValueType>*, bool> Insert(const uint32_t& index, ValueType value) {
AMFValue<ValueType>* val = nullptr;
bool inserted = false;
if (index >= this->dense.size()) {
this->dense.resize(index + 1);
val = new AMFValue<ValueType>(value);
this->dense.at(index) = val;
inserted = true;
}
return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(this->dense.at(index)), inserted);
};
/**
* Inserts an AMFValue into the associative portion with the given key.
* If a duplicate is attempted to be inserted, it replaces the original
*
* The inserted element is now owned by this object and is not to be deleted
*
* @param key The key to associate with the value
* @param value The value to insert
*/
void Insert(const std::string& key, AMFBaseValue* value) {
auto element = associative.find(key);
if (element != associative.end() && element->second) {
delete element->second;
element->second = value;
} else {
associative.insert(std::make_pair(key, value));
}
};
/**
* Inserts an AMFValue into the associative portion with the given index.
* If a duplicate is attempted to be inserted, it replaces the original
*
* The inserted element is now owned by this object and is not to be deleted
*
* @param key The key to associate with the value
* @param value The value to insert
*/
void Insert(const uint32_t index, AMFBaseValue* value) {
if (index < dense.size()) {
AMFDense::iterator itr = dense.begin() + index;
if (*itr) delete dense.at(index);
} else {
dense.resize(index + 1);
}
dense.at(index) = value;
};
/**
* Pushes an AMFValue into the back of the dense portion.
*
* These objects are not to be deleted by the caller as they are owned by
* the AMFArray object which manages its own memory.
*
* @param value The value to insert
*
* @return The inserted pointer, or nullptr should the key already be in use.
*/
template<typename ValueType>
inline AMFValue<ValueType>* Push(ValueType value) {
return Insert(this->dense.size(), value).first;
};
/**
* Removes the key from the associative portion
*
* The pointer removed is now no longer managed by this container
*
* @param key The key to remove from the associative portion
*/
void Remove(const std::string& key, bool deleteValue = true) {
AMFAssociative::iterator it = this->associative.find(key);
if (it != this->associative.end()) {
if (deleteValue) delete it->second;
this->associative.erase(it);
}
}
/**
* Pops the last element in the dense portion, deleting it in the process.
*/
void Remove(const uint32_t index) {
if (!this->dense.empty() && index < this->dense.size()) {
auto itr = this->dense.begin() + index;
if (*itr) delete (*itr);
this->dense.erase(itr);
}
}
void Pop() {
if (!this->dense.empty()) Remove(this->dense.size() - 1);
}
AMFArrayValue* GetArray(const std::string& key) {
AMFAssociative::const_iterator it = this->associative.find(key);
if (it != this->associative.end()) {
return dynamic_cast<AMFArrayValue*>(it->second);
}
return nullptr;
};
AMFArrayValue* GetArray(const uint32_t index) {
return index >= this->dense.size() ? nullptr : dynamic_cast<AMFArrayValue*>(this->dense.at(index));
};
inline AMFArrayValue* InsertArray(const std::string& key) {
return static_cast<AMFArrayValue*>(Insert(key).first);
};
inline AMFArrayValue* InsertArray(const uint32_t index) {
return static_cast<AMFArrayValue*>(Insert(index).first);
};
inline AMFArrayValue* PushArray() {
return static_cast<AMFArrayValue*>(Insert(this->dense.size()).first);
};
/**
* Gets an AMFValue by the key from the associative portion and converts it
* to the AmfValue template type. If the key did not exist, it is inserted.
*
* @tparam The target object type
* @param key The key to lookup
*
* @return The AMFValue
*/
template <typename AmfType>
AMFValue<AmfType>* Get(const std::string& key) const {
AMFAssociative::const_iterator it = this->associative.find(key);
return it != this->associative.end() ?
dynamic_cast<AMFValue<AmfType>*>(it->second) :
nullptr;
};
// Get from the array but dont cast it
AMFBaseValue* Get(const std::string& key) const {
AMFAssociative::const_iterator it = this->associative.find(key);
return it != this->associative.end() ? it->second : nullptr;
};
/**
* @brief Get an AMFValue object at a position in the dense portion.
* Gets an AMFValue by the index from the dense portion and converts it
* to the AmfValue template type. If the index did not exist, it is inserted.
*
* @tparam The target object type
* @param index The index to get
* @return The casted object, or nullptr.
*/
template <typename AmfType>
AMFValue<AmfType>* Get(uint32_t index) const {
std::cout << (index < this->dense.size()) << std::endl;
return index < this->dense.size() ?
dynamic_cast<AMFValue<AmfType>*>(this->dense.at(index)) :
nullptr;
};
// Get from the dense but dont cast it
AMFBaseValue* Get(const uint32_t index) const {
return index < this->dense.size() ? this->dense.at(index) : nullptr;
};
private:
/**
* The associative portion. These values are key'd with strings to an AMFValue.
*/
AMFAssociative associative;
/**
* The dense portion. These AMFValue's are stored one after
* another with the most recent addition being at the back.
*/
AMFDense dense;
};
#endif //!__AMF3__H__

184
dCommon/AmfSerialize.cpp Normal file
View File

@ -0,0 +1,184 @@
#include "AmfSerialize.h"
#include "Game.h"
#include "dLogger.h"
// Writes an AMFValue pointer to a RakNet::BitStream
template<>
void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value) {
eAmf type = value.GetValueType();
this->Write(type);
switch (type) {
case eAmf::Integer: {
this->Write<AMFIntValue&>(*static_cast<AMFIntValue*>(&value));
break;
}
case eAmf::Double: {
this->Write<AMFDoubleValue&>(*static_cast<AMFDoubleValue*>(&value));
break;
}
case eAmf::String: {
this->Write<AMFStringValue&>(*static_cast<AMFStringValue*>(&value));
break;
}
case eAmf::Array: {
this->Write<AMFArrayValue&>(*static_cast<AMFArrayValue*>(&value));
break;
}
default: {
Game::logger->Log("AmfSerialize", "Encountered unwritable AMFType %i!", type);
}
case eAmf::Undefined:
case eAmf::Null:
case eAmf::False:
case eAmf::True:
case eAmf::Date:
case eAmf::Object:
case eAmf::XML:
case eAmf::XMLDoc:
case eAmf::ByteArray:
case eAmf::VectorInt:
case eAmf::VectorUInt:
case eAmf::VectorDouble:
case eAmf::VectorObject:
case eAmf::Dictionary:
break;
}
}
/**
* A private function to write an value to a RakNet::BitStream
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
unsigned char b4 = (unsigned char)v;
if (v < 0x00200000) {
b4 = b4 & 0x7F;
if (v > 0x7F) {
unsigned char b3;
v = v >> 7;
b3 = ((unsigned char)(v)) | 0x80;
if (v > 0x7F) {
unsigned char b2;
v = v >> 7;
b2 = ((unsigned char)(v)) | 0x80;
bs->Write(b2);
}
bs->Write(b3);
}
} else {
unsigned char b1;
unsigned char b2;
unsigned char b3;
v = v >> 8;
b3 = ((unsigned char)(v)) | 0x80;
v = v >> 7;
b2 = ((unsigned char)(v)) | 0x80;
v = v >> 7;
b1 = ((unsigned char)(v)) | 0x80;
bs->Write(b1);
bs->Write(b2);
bs->Write(b3);
}
bs->Write(b4);
}
/**
* Writes a flag number to a RakNet::BitStream
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteFlagNumber(RakNet::BitStream* bs, uint32_t v) {
v = (v << 1) | 0x01;
WriteUInt29(bs, v);
}
/**
* Writes an AMFString to a RakNet::BitStream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFString(RakNet::BitStream* bs, const std::string& str) {
WriteFlagNumber(bs, (uint32_t)str.size());
bs->Write(str.c_str(), (uint32_t)str.size());
}
/**
* Writes an U16 to a bitstream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFU16(RakNet::BitStream* bs, uint16_t value) {
bs->Write(value);
}
/**
* Writes an U32 to a bitstream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFU32(RakNet::BitStream* bs, uint32_t value) {
bs->Write(value);
}
/**
* Writes an U64 to a bitstream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFU64(RakNet::BitStream* bs, uint64_t value) {
bs->Write(value);
}
// Writes an AMFIntegerValue to BitStream
template<>
void RakNet::BitStream::Write<AMFIntValue&>(AMFIntValue& value) {
WriteUInt29(this, value.GetValue());
}
// Writes an AMFDoubleValue to BitStream
template<>
void RakNet::BitStream::Write<AMFDoubleValue&>(AMFDoubleValue& value) {
double d = value.GetValue();
WriteAMFU64(this, *reinterpret_cast<uint64_t*>(&d));
}
// Writes an AMFStringValue to BitStream
template<>
void RakNet::BitStream::Write<AMFStringValue&>(AMFStringValue& value) {
WriteAMFString(this, value.GetValue());
}
// Writes an AMFArrayValue to BitStream
template<>
void RakNet::BitStream::Write<AMFArrayValue&>(AMFArrayValue& value) {
uint32_t denseSize = value.GetDense().size();
WriteFlagNumber(this, denseSize);
auto it = value.GetAssociative().begin();
auto end = value.GetAssociative().end();
while (it != end) {
WriteAMFString(this, it->first);
this->Write<AMFBaseValue&>(*it->second);
it++;
}
this->Write(eAmf::Null);
if (denseSize > 0) {
auto it2 = value.GetDense().begin();
auto end2 = value.GetDense().end();
while (it2 != end2) {
this->Write<AMFBaseValue&>(**it2);
it2++;
}
}
}

50
dCommon/AmfSerialize.h Normal file
View File

@ -0,0 +1,50 @@
#pragma once
// Custom Classes
#include "Amf3.h"
// RakNet
#include <BitStream.h>
/*!
\file AmfSerialize.h
\brief A class that implements native writing of AMF values to RakNet::BitStream
*/
// We are using the RakNet namespace
namespace RakNet {
//! Writes an AMFValue pointer to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value);
//! Writes an AMFIntegerValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFIntValue&>(AMFIntValue& value);
//! Writes an AMFDoubleValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFDoubleValue&>(AMFDoubleValue& value);
//! Writes an AMFStringValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFStringValue&>(AMFStringValue& value);
//! Writes an AMFArrayValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFArrayValue&>(AMFArrayValue& value);
} // namespace RakNet

View File

@ -1,6 +1,6 @@
set(DCOMMON_SOURCES "AMFFormat.cpp"
set(DCOMMON_SOURCES
"AMFDeserialize.cpp"
"AMFFormat_BitStream.cpp"
"AmfSerialize.cpp"
"BinaryIO.cpp"
"dConfig.cpp"
"Diagnostics.cpp"

View File

@ -13,7 +13,7 @@
#include "VehiclePhysicsComponent.h"
#include "GameMessages.h"
#include "Item.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "eGameMasterLevel.h"
#include "eGameActivity.h"
@ -734,6 +734,6 @@ void CharacterComponent::RemoveVentureVisionEffect(std::string ventureVisionType
void CharacterComponent::UpdateClientMinimap(bool showFaction, std::string ventureVisionType) const {
if (!m_Parent) return;
AMFArrayValue arrayToSend;
arrayToSend.InsertValue(ventureVisionType, showFaction ? static_cast<AMFValue*>(new AMFTrueValue()) : static_cast<AMFValue*>(new AMFFalseValue()));
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent ? m_Parent->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS, "SetFactionVisibility", &arrayToSend);
arrayToSend.Insert(ventureVisionType, showFaction);
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent ? m_Parent->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS, "SetFactionVisibility", arrayToSend);
}

View File

@ -4,8 +4,8 @@
#include "Game.h"
#include "dConfig.h"
#include "AMFFormat.h"
#include "AMFFormat_BitStream.h"
#include "Amf3.h"
#include "AmfSerialize.h"
#include "GameMessages.h"
#include "User.h"
#include "CDClientManager.h"
@ -245,16 +245,12 @@ void DestroyableComponent::SetMaxHealth(float value, bool playAnim) {
if (playAnim) {
// Now update the player bar
if (!m_Parent->GetParentUser()) return;
AMFStringValue* amount = new AMFStringValue();
amount->SetStringValue(std::to_string(difference));
AMFStringValue* type = new AMFStringValue();
type->SetStringValue("health");
AMFArrayValue args;
args.InsertValue("amount", amount);
args.InsertValue("type", type);
args.Insert("amount", std::to_string(difference));
args.Insert("type", "health");
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
}
EntityManager::Instance()->SerializeEntity(m_Parent);
@ -290,16 +286,12 @@ void DestroyableComponent::SetMaxArmor(float value, bool playAnim) {
if (playAnim) {
// Now update the player bar
if (!m_Parent->GetParentUser()) return;
AMFStringValue* amount = new AMFStringValue();
amount->SetStringValue(std::to_string(value));
AMFStringValue* type = new AMFStringValue();
type->SetStringValue("armor");
AMFArrayValue args;
args.InsertValue("amount", amount);
args.InsertValue("type", type);
args.Insert("amount", std::to_string(value));
args.Insert("type", "armor");
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
}
EntityManager::Instance()->SerializeEntity(m_Parent);
@ -334,16 +326,12 @@ void DestroyableComponent::SetMaxImagination(float value, bool playAnim) {
if (playAnim) {
// Now update the player bar
if (!m_Parent->GetParentUser()) return;
AMFStringValue* amount = new AMFStringValue();
amount->SetStringValue(std::to_string(difference));
AMFStringValue* type = new AMFStringValue();
type->SetStringValue("imagination");
AMFArrayValue args;
args.InsertValue("amount", amount);
args.InsertValue("type", type);
args.Insert("amount", std::to_string(difference));
args.Insert("type", "imagination");
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
}
EntityManager::Instance()->SerializeEntity(m_Parent);
}

View File

@ -13,7 +13,7 @@
#include "InventoryComponent.h"
#include "GameMessages.h"
#include "Game.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "dZoneManager.h"
#include "Mail.h"
#include "MissionPrerequisites.h"

View File

@ -11,7 +11,7 @@
#include "CharacterComponent.h"
#include "UserManager.h"
#include "dLogger.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "eObjectBits.h"
#include "eGameMasterLevel.h"
@ -36,12 +36,9 @@ void PropertyEntranceComponent::OnUse(Entity* entity) {
AMFArrayValue args;
auto* state = new AMFStringValue();
state->SetStringValue("property_menu");
args.Insert("state", "property_menu");
args.InsertValue("state", state);
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", &args);
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", args);
}
void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index, bool returnToZone, const SystemAddress& sysAddr) {

View File

@ -6,7 +6,7 @@
#include <string>
#include <unordered_map>
#include "AMFFormat.h"
#include "Amf3.h"
#include "Component.h"
#include "eReplicaComponentType.h"

View File

@ -28,7 +28,7 @@
#include "eUnequippableActiveType.h"
#include "eMovementPlatformState.h"
#include "LeaderboardManager.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "Loot.h"
#include "eRacingTaskParam.h"
#include "eMissionTaskType.h"
@ -87,7 +87,7 @@
#include "AMFDeserialize.h"
#include "eBlueprintSaveResponseType.h"
#include "eAninmationFlags.h"
#include "AMFFormat_BitStream.h"
#include "AmfSerialize.h"
#include "eReplicaComponentType.h"
#include "eClientMessageType.h"
#include "eGameMessageType.h"
@ -596,14 +596,14 @@ void GameMessages::SendModifyLEGOScore(Entity* entity, const SystemAddress& sysA
SEND_PACKET;
}
void GameMessages::SendUIMessageServerToSingleClient(Entity* entity, const SystemAddress& sysAddr, const std::string& message, AMFValue* args) {
void GameMessages::SendUIMessageServerToSingleClient(Entity* entity, const SystemAddress& sysAddr, const std::string& message, AMFBaseValue& args) {
CBITSTREAM;
CMSGHEADER;
bitStream.Write(entity->GetObjectID());
bitStream.Write((uint16_t)eGameMessageType::UI_MESSAGE_SERVER_TO_SINGLE_CLIENT);
bitStream.Write(args);
bitStream.Write<AMFBaseValue&>(args);
uint32_t strMessageNameLength = message.size();
bitStream.Write(strMessageNameLength);
@ -614,7 +614,7 @@ void GameMessages::SendUIMessageServerToSingleClient(Entity* entity, const Syste
SEND_PACKET;
}
void GameMessages::SendUIMessageServerToAllClients(const std::string& message, AMFValue* args) {
void GameMessages::SendUIMessageServerToAllClients(const std::string& message, AMFBaseValue& args) {
CBITSTREAM;
CMSGHEADER;
@ -622,7 +622,7 @@ void GameMessages::SendUIMessageServerToAllClients(const std::string& message, A
bitStream.Write(empty);
bitStream.Write((uint16_t)eGameMessageType::UI_MESSAGE_SERVER_TO_ALL_CLIENTS);
bitStream.Write(args);
bitStream.Write<AMFBaseValue&>(args);
uint32_t strMessageNameLength = message.size();
bitStream.Write(strMessageNameLength);
@ -2486,8 +2486,8 @@ void GameMessages::SendUnSmash(Entity* entity, LWOOBJID builderID, float duratio
void GameMessages::HandleControlBehaviors(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
AMFDeserialize reader;
std::unique_ptr<AMFValue> amfArguments(reader.Read(inStream));
if (amfArguments->GetValueType() != AMFValueType::AMFArray) return;
std::unique_ptr<AMFBaseValue> amfArguments(reader.Read(inStream));
if (amfArguments->GetValueType() != eAmf::Array) return;
uint32_t commandLength{};
inStream->Read(commandLength);

View File

@ -12,7 +12,7 @@
#include "eLootSourceType.h"
#include "Brick.h"
class AMFValue;
class AMFBaseValue;
class Entity;
class Item;
class NiQuaternion;
@ -88,8 +88,8 @@ namespace GameMessages {
void NotifyLevelRewards(LWOOBJID objectID, const SystemAddress& sysAddr, int level, bool sending_rewards);
void SendModifyLEGOScore(Entity* entity, const SystemAddress& sysAddr, int64_t score, eLootSourceType sourceType);
void SendUIMessageServerToSingleClient(Entity* entity, const SystemAddress& sysAddr, const std::string& message, AMFValue* args);
void SendUIMessageServerToAllClients(const std::string& message, AMFValue* args);
void SendUIMessageServerToSingleClient(Entity* entity, const SystemAddress& sysAddr, const std::string& message, AMFBaseValue& args);
void SendUIMessageServerToAllClients(const std::string& message, AMFBaseValue& args);
void SendPlayEmbeddedEffectOnAllClientsNearObject(Entity* entity, std::u16string effectName, const LWOOBJID& fromObjectID, float radius);
void SendPlayFXEffect(Entity* entity, int32_t effectID, const std::u16string& effectType, const std::string& name, LWOOBJID secondary, float priority = 1, float scale = 1, bool serialize = true);

View File

@ -12,19 +12,19 @@ Action::Action(AMFArrayValue* arguments) {
valueParameterName = "";
valueParameterString = "";
valueParameterDouble = 0.0;
for (auto& typeValueMap : arguments->GetAssociativeMap()) {
for (auto& typeValueMap : arguments->GetAssociative()) {
if (typeValueMap.first == "Type") {
if (typeValueMap.second->GetValueType() != AMFValueType::AMFString) continue;
type = static_cast<AMFStringValue*>(typeValueMap.second)->GetStringValue();
if (typeValueMap.second->GetValueType() != eAmf::String) continue;
type = static_cast<AMFStringValue*>(typeValueMap.second)->GetValue();
} else {
valueParameterName = typeValueMap.first;
// Message is the only known string parameter
if (valueParameterName == "Message") {
if (typeValueMap.second->GetValueType() != AMFValueType::AMFString) continue;
valueParameterString = static_cast<AMFStringValue*>(typeValueMap.second)->GetStringValue();
if (typeValueMap.second->GetValueType() != eAmf::String) continue;
valueParameterString = static_cast<AMFStringValue*>(typeValueMap.second)->GetValue();
} else {
if (typeValueMap.second->GetValueType() != AMFValueType::AMFDouble) continue;
valueParameterDouble = static_cast<AMFDoubleValue*>(typeValueMap.second)->GetDoubleValue();
if (typeValueMap.second->GetValueType() != eAmf::Double) continue;
valueParameterDouble = static_cast<AMFDoubleValue*>(typeValueMap.second)->GetValue();
}
}
}

View File

@ -2,7 +2,7 @@
#include <stdexcept>
#include "AMFFormat.h"
#include "Amf3.h"
ActionContext::ActionContext() {
stripId = 0;
@ -17,15 +17,15 @@ ActionContext::ActionContext(AMFArrayValue* arguments, std::string customStateKe
}
BehaviorState ActionContext::GetBehaviorStateFromArgument(AMFArrayValue* arguments, const std::string& key) {
auto* stateIDValue = arguments->FindValue<AMFDoubleValue>(key);
auto* stateIDValue = arguments->Get<double>(key);
if (!stateIDValue) throw std::invalid_argument("Unable to find behavior state from argument \"" + key + "\"");
return static_cast<BehaviorState>(stateIDValue->GetDoubleValue());
return static_cast<BehaviorState>(stateIDValue->GetValue());
}
StripId ActionContext::GetStripIdFromArgument(AMFArrayValue* arguments, const std::string& key) {
auto* stripIdValue = arguments->FindValue<AMFDoubleValue>(key);
auto* stripIdValue = arguments->Get<double>(key);
if (!stripIdValue) throw std::invalid_argument("Unable to find strip ID from argument \"" + key + "\"");
return static_cast<StripId>(stripIdValue->GetDoubleValue());
return static_cast<StripId>(stripIdValue->GetValue());
}

View File

@ -4,7 +4,7 @@ AddActionMessage::AddActionMessage(AMFArrayValue* arguments) : BehaviorMessageBa
actionContext = ActionContext(arguments);
actionIndex = GetActionIndexFromArgument(arguments);
auto* actionValue = arguments->FindValue<AMFArrayValue>("action");
auto* actionValue = arguments->GetArray("action");
if (!actionValue) return;
action = Action(actionValue);

View File

@ -2,10 +2,10 @@
AddMessage::AddMessage(AMFArrayValue* arguments) : BehaviorMessageBase(arguments) {
behaviorIndex = 0;
auto* behaviorIndexValue = arguments->FindValue<AMFDoubleValue>("BehaviorIndex");
auto* behaviorIndexValue = arguments->Get<double>("BehaviorIndex");
if (!behaviorIndexValue) return;
behaviorIndex = static_cast<uint32_t>(behaviorIndexValue->GetDoubleValue());
behaviorIndex = static_cast<uint32_t>(behaviorIndexValue->GetValue());
Game::logger->LogDebug("AddMessage", "behaviorId %i index %i", behaviorId, behaviorIndex);
}

View File

@ -4,17 +4,16 @@
AddStripMessage::AddStripMessage(AMFArrayValue* arguments) : BehaviorMessageBase(arguments) {
actionContext = ActionContext(arguments);
position = StripUiPosition(arguments);
auto* strip = arguments->FindValue<AMFArrayValue>("strip");
auto* strip = arguments->GetArray("strip");
if (!strip) return;
auto* actions = strip->FindValue<AMFArrayValue>("actions");
auto* actions = strip->GetArray("actions");
if (!actions) return;
for (uint32_t actionNumber = 0; actionNumber < actions->GetDenseValueSize(); actionNumber++) {
auto* actionValue = actions->GetValueAt<AMFArrayValue>(actionNumber);
for (uint32_t actionNumber = 0; actionNumber < actions->GetDense().size(); actionNumber++) {
auto* actionValue = actions->GetArray(actionNumber);
if (!actionValue) continue;
actionsToAdd.push_back(Action(actionValue));

View File

@ -1,6 +1,6 @@
#include "BehaviorMessageBase.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "BehaviorStates.h"
#include "dCommonVars.h"
@ -11,12 +11,12 @@ BehaviorMessageBase::BehaviorMessageBase(AMFArrayValue* arguments) {
int32_t BehaviorMessageBase::GetBehaviorIdFromArgument(AMFArrayValue* arguments) {
const auto* key = "BehaviorID";
auto* behaviorIDValue = arguments->FindValue<AMFStringValue>(key);
auto* behaviorIDValue = arguments->Get<std::string>(key);
int32_t behaviorID = -1;
if (behaviorIDValue) {
behaviorID = std::stoul(behaviorIDValue->GetStringValue());
} else if (!arguments->FindValue<AMFUndefinedValue>(key)) {
if (behaviorIDValue && behaviorIDValue->GetValueType() == eAmf::String) {
behaviorID = std::stoul(behaviorIDValue->GetValue());
} else if (arguments->Get(key)->GetValueType() != eAmf::Undefined) {
throw std::invalid_argument("Unable to find behavior ID");
}
@ -24,10 +24,10 @@ int32_t BehaviorMessageBase::GetBehaviorIdFromArgument(AMFArrayValue* arguments)
}
uint32_t BehaviorMessageBase::GetActionIndexFromArgument(AMFArrayValue* arguments, const std::string& keyName) {
auto* actionIndexAmf = arguments->FindValue<AMFDoubleValue>(keyName);
auto* actionIndexAmf = arguments->Get<double>(keyName);
if (!actionIndexAmf) {
throw std::invalid_argument("Unable to find actionIndex");
}
return static_cast<uint32_t>(actionIndexAmf->GetDoubleValue());
return static_cast<uint32_t>(actionIndexAmf->GetValue());
}

View File

@ -4,7 +4,7 @@
#include <stdexcept>
#include <string>
#include "AMFFormat.h"
#include "Amf3.h"
#include "dCommonVars.h"
#include "Game.h"

View File

@ -1,9 +1,9 @@
#include "MoveToInventoryMessage.h"
MoveToInventoryMessage::MoveToInventoryMessage(AMFArrayValue* arguments) : BehaviorMessageBase(arguments) {
auto* behaviorIndexValue = arguments->FindValue<AMFDoubleValue>("BehaviorIndex");
auto* behaviorIndexValue = arguments->Get<double>("BehaviorIndex");
if (!behaviorIndexValue) return;
behaviorIndex = static_cast<uint32_t>(behaviorIndexValue->GetDoubleValue());
behaviorIndex = static_cast<uint32_t>(behaviorIndexValue->GetValue());
Game::logger->LogDebug("MoveToInventoryMessage", "behaviorId %i behaviorIndex %i", behaviorId, behaviorIndex);
}

View File

@ -1,9 +1,9 @@
#include "RenameMessage.h"
RenameMessage::RenameMessage(AMFArrayValue* arguments) : BehaviorMessageBase(arguments) {
auto* nameAmf = arguments->FindValue<AMFStringValue>("Name");
auto* nameAmf = arguments->Get<std::string>("Name");
if (!nameAmf) return;
name = nameAmf->GetStringValue();
name = nameAmf->GetValue();
Game::logger->LogDebug("RenameMessage", "behaviorId %i n %s", behaviorId, name.c_str());
}

View File

@ -1,6 +1,6 @@
#include "StripUiPosition.h"
#include "AMFFormat.h"
#include "Amf3.h"
StripUiPosition::StripUiPosition() {
xPosition = 0.0;
@ -10,13 +10,13 @@ StripUiPosition::StripUiPosition() {
StripUiPosition::StripUiPosition(AMFArrayValue* arguments, std::string uiKeyName) {
xPosition = 0.0;
yPosition = 0.0;
auto* uiArray = arguments->FindValue<AMFArrayValue>(uiKeyName);
auto* uiArray = arguments->GetArray(uiKeyName);
if (!uiArray) return;
auto* xPositionValue = uiArray->FindValue<AMFDoubleValue>("x");
auto* yPositionValue = uiArray->FindValue<AMFDoubleValue>("y");
auto* xPositionValue = uiArray->Get<double>("x");
auto* yPositionValue = uiArray->Get<double>("y");
if (!xPositionValue || !yPositionValue) return;
yPosition = yPositionValue->GetDoubleValue();
xPosition = xPositionValue->GetDoubleValue();
yPosition = yPositionValue->GetValue();
xPosition = xPositionValue->GetValue();
}

View File

@ -5,7 +5,7 @@
UpdateActionMessage::UpdateActionMessage(AMFArrayValue* arguments) : BehaviorMessageBase(arguments) {
actionContext = ActionContext(arguments);
auto* actionValue = arguments->FindValue<AMFArrayValue>("action");
auto* actionValue = arguments->GetArray("action");
if (!actionValue) return;
action = Action(actionValue);

View File

@ -1,6 +1,6 @@
#include "ControlBehaviors.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "Entity.h"
#include "Game.h"
#include "GameMessages.h"
@ -43,11 +43,11 @@ void ControlBehaviors::RequestUpdatedID(int32_t behaviorID, ModelComponent* mode
// AMFArrayValue args;
// AMFStringValue* behaviorIDString = new AMFStringValue();
// behaviorIDString->SetStringValue(std::to_string(persistentId));
// behaviorIDString->SetValue(std::to_string(persistentId));
// args.InsertValue("behaviorID", behaviorIDString);
// AMFStringValue* objectIDAsString = new AMFStringValue();
// objectIDAsString->SetStringValue(std::to_string(modelComponent->GetParent()->GetObjectID()));
// objectIDAsString->SetValue(std::to_string(modelComponent->GetParent()->GetObjectID()));
// args.InsertValue("objectID", objectIDAsString);
// GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorID", &args);
@ -63,8 +63,6 @@ void ControlBehaviors::SendBehaviorListToClient(Entity* modelEntity, const Syste
AMFArrayValue behaviorsToSerialize;
AMFArrayValue* behaviors = new AMFArrayValue(); // Empty for now
/**
* The behaviors AMFArray will have up to 5 elements in the dense portion.
* Each element in the dense portion will be made up of another AMFArray
@ -75,20 +73,17 @@ void ControlBehaviors::SendBehaviorListToClient(Entity* modelEntity, const Syste
* "name": The name of the behavior formatted as an AMFString
*/
behaviorsToSerialize.InsertValue("behaviors", behaviors);
behaviorsToSerialize.Insert("behaviors");
behaviorsToSerialize.Insert("objectID", std::to_string(modelComponent->GetParent()->GetObjectID()));
AMFStringValue* amfStringValueForObjectID = new AMFStringValue();
amfStringValueForObjectID->SetStringValue(std::to_string(modelComponent->GetParent()->GetObjectID()));
behaviorsToSerialize.InsertValue("objectID", amfStringValueForObjectID);
GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorList", &behaviorsToSerialize);
GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorList", behaviorsToSerialize);
}
void ControlBehaviors::ModelTypeChanged(AMFArrayValue* arguments, ModelComponent* ModelComponent) {
auto* modelTypeAmf = arguments->FindValue<AMFDoubleValue>("ModelType");
auto* modelTypeAmf = arguments->Get<double>("ModelType");
if (!modelTypeAmf) return;
uint32_t modelType = static_cast<uint32_t>(modelTypeAmf->GetDoubleValue());
uint32_t modelType = static_cast<uint32_t>(modelTypeAmf->GetValue());
//TODO Update the model type here
}
@ -179,7 +174,7 @@ void ControlBehaviors::SendBehaviorBlocksToClient(ModelComponent* modelComponent
// AMFArrayValue* state = new AMFArrayValue();
// AMFDoubleValue* stateAsDouble = new AMFDoubleValue();
// stateAsDouble->SetDoubleValue(it->first);
// stateAsDouble->SetValue(it->first);
// state->InsertValue("id", stateAsDouble);
// AMFArrayValue* strips = new AMFArrayValue();
@ -189,16 +184,16 @@ void ControlBehaviors::SendBehaviorBlocksToClient(ModelComponent* modelComponent
// AMFArrayValue* thisStrip = new AMFArrayValue();
// AMFDoubleValue* stripID = new AMFDoubleValue();
// stripID->SetDoubleValue(strip->first);
// stripID->SetValue(strip->first);
// thisStrip->InsertValue("id", stripID);
// AMFArrayValue* uiArray = new AMFArrayValue();
// AMFDoubleValue* yPosition = new AMFDoubleValue();
// yPosition->SetDoubleValue(strip->second->GetYPosition());
// yPosition->SetValue(strip->second->GetYPosition());
// uiArray->InsertValue("y", yPosition);
// AMFDoubleValue* xPosition = new AMFDoubleValue();
// xPosition->SetDoubleValue(strip->second->GetXPosition());
// xPosition->SetValue(strip->second->GetXPosition());
// uiArray->InsertValue("x", xPosition);
// thisStrip->InsertValue("ui", uiArray);
@ -211,19 +206,19 @@ void ControlBehaviors::SendBehaviorBlocksToClient(ModelComponent* modelComponent
// AMFArrayValue* thisAction = new AMFArrayValue();
// AMFStringValue* actionName = new AMFStringValue();
// actionName->SetStringValue(behaviorAction->actionName);
// actionName->SetValue(behaviorAction->actionName);
// thisAction->InsertValue("Type", actionName);
// if (behaviorAction->parameterValueString != "")
// {
// AMFStringValue* valueAsString = new AMFStringValue();
// valueAsString->SetStringValue(behaviorAction->parameterValueString);
// valueAsString->SetValue(behaviorAction->parameterValueString);
// thisAction->InsertValue(behaviorAction->parameterName, valueAsString);
// }
// else if (behaviorAction->parameterValueDouble != 0.0)
// {
// AMFDoubleValue* valueAsDouble = new AMFDoubleValue();
// valueAsDouble->SetDoubleValue(behaviorAction->parameterValueDouble);
// valueAsDouble->SetValue(behaviorAction->parameterValueDouble);
// thisAction->InsertValue(behaviorAction->parameterName, valueAsDouble);
// }
// stripSerialize->PushBackValue(thisAction);
@ -237,11 +232,11 @@ void ControlBehaviors::SendBehaviorBlocksToClient(ModelComponent* modelComponent
// behaviorInfo.InsertValue("states", stateSerialize);
// AMFStringValue* objectidAsString = new AMFStringValue();
// objectidAsString->SetStringValue(std::to_string(targetObjectID));
// objectidAsString->SetValue(std::to_string(targetObjectID));
// behaviorInfo.InsertValue("objectID", objectidAsString);
// AMFStringValue* behaviorIDAsString = new AMFStringValue();
// behaviorIDAsString->SetStringValue(std::to_string(behaviorID));
// behaviorIDAsString->SetValue(std::to_string(behaviorID));
// behaviorInfo.InsertValue("BehaviorID", behaviorIDAsString);
// GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorBlocks", &behaviorInfo);
@ -275,10 +270,9 @@ void ControlBehaviors::MoveToInventory(ModelComponent* modelComponent, const Sys
// This closes the UI menu should it be open while the player is removing behaviors
AMFArrayValue args;
AMFFalseValue* stateToPop = new AMFFalseValue();
args.InsertValue("visible", stateToPop);
args.Insert("visible", false);
GameMessages::SendUIMessageServerToSingleClient(modelOwner, modelOwner->GetParentUser()->GetSystemAddress(), "ToggleBehaviorEditor", &args);
GameMessages::SendUIMessageServerToSingleClient(modelOwner, modelOwner->GetParentUser()->GetSystemAddress(), "ToggleBehaviorEditor", args);
MoveToInventoryMessage moveToInventoryMessage(arguments);

View File

@ -69,7 +69,7 @@
#include "BinaryPathFinder.h"
#include "dConfig.h"
#include "eBubbleType.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "MovingPlatformComponent.h"
#include "eMissionState.h"
#include "TriggerComponent.h"
@ -251,26 +251,20 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
{
AMFArrayValue args;
auto* state = new AMFStringValue();
state->SetStringValue("Story");
args.Insert("state", "Story");
args.InsertValue("state", state);
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", &args);
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", args);
}
entity->AddCallbackTimer(0.5f, [customText, entity]() {
AMFArrayValue args;
auto* text = new AMFStringValue();
text->SetStringValue(customText);
args.InsertValue("visible", new AMFTrueValue());
args.InsertValue("text", text);
args.Insert("visible", true);
args.Insert("text", customText);
Game::logger->Log("SlashCommandHandler", "Sending %s", customText.c_str());
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "ToggleStoryBox", &args);
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "ToggleStoryBox", args);
});
return;
@ -530,12 +524,11 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
}
if (chatCommand == "setuistate" && args.size() == 1 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
AMFStringValue* value = new AMFStringValue();
value->SetStringValue(args[0]);
AMFArrayValue uiState;
AMFArrayValue args;
args.InsertValue("state", value);
GameMessages::SendUIMessageServerToSingleClient(entity, sysAddr, "pushGameState", &args);
uiState.Insert("state", args.at(0));
GameMessages::SendUIMessageServerToSingleClient(entity, sysAddr, "pushGameState", uiState);
ChatPackets::SendSystemMessage(sysAddr, u"Switched UI state.");
@ -543,11 +536,11 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
}
if (chatCommand == "toggle" && args.size() == 1 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
AMFTrueValue* value = new AMFTrueValue();
AMFArrayValue amfArgs;
amfArgs.InsertValue("visible", value);
GameMessages::SendUIMessageServerToSingleClient(entity, sysAddr, args[0], &amfArgs);
amfArgs.Insert("visible", true);
GameMessages::SendUIMessageServerToSingleClient(entity, sysAddr, args[0], amfArgs);
ChatPackets::SendSystemMessage(sysAddr, u"Toggled UI state.");
@ -1617,7 +1610,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
if ((chatCommand == "debugui") && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
ChatPackets::SendSystemMessage(sysAddr, u"Opening UIDebugger...");
AMFArrayValue args;
GameMessages::SendUIMessageServerToSingleClient(entity, sysAddr, "ToggleUIDebugger;", nullptr);
GameMessages::SendUIMessageServerToSingleClient(entity, sysAddr, "ToggleUIDebugger;", args);
}
if ((chatCommand == "boost") && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
@ -2023,15 +2016,11 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::string& message) {
AMFArrayValue args;
auto* titleValue = new AMFStringValue();
titleValue->SetStringValue(title);
auto* messageValue = new AMFStringValue();
messageValue->SetStringValue(message);
args.InsertValue("title", titleValue);
args.InsertValue("message", messageValue);
args.Insert("title", title);
args.Insert("message", message);
GameMessages::SendUIMessageServerToAllClients("ToggleAnnounce", &args);
GameMessages::SendUIMessageServerToAllClients("ToggleAnnounce", args);
//Notify chat about it
CBITSTREAM;

View File

@ -335,7 +335,6 @@ int main(int argc, char** argv) {
Game::im->GetInstance(0, false, 0);
Game::im->GetInstance(1000, false, 0);
StartAuthServer();
}

View File

@ -1,6 +1,6 @@
#include "AmConsoleTeleportServer.h"
#include "ChooseYourDestinationNsToNt.h"
#include "AMFFormat.h"
#include "Amf3.h"
void AmConsoleTeleportServer::OnStartup(Entity* self) {
self->SetVar(u"teleportAnim", m_TeleportAnim);

View File

@ -1,24 +1,24 @@
#include "BankInteractServer.h"
#include "GameMessages.h"
#include "Entity.h"
#include "AMFFormat.h"
#include "Amf3.h"
void BankInteractServer::OnUse(Entity* self, Entity* user) {
AMFArrayValue args;
AMFStringValue* bank = new AMFStringValue();
bank->SetStringValue("bank");
args.InsertValue("state", bank);
GameMessages::SendUIMessageServerToSingleClient(user, user->GetSystemAddress(), "pushGameState", &args);
args.Insert("state", "bank");
GameMessages::SendUIMessageServerToSingleClient(user, user->GetSystemAddress(), "pushGameState", args);
}
void BankInteractServer::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1,
int32_t param2, int32_t param3) {
if (args == "ToggleBank") {
AMFArrayValue args;
args.InsertValue("visible", new AMFFalseValue());
GameMessages::SendUIMessageServerToSingleClient(sender, sender->GetSystemAddress(), "ToggleBank", &args);
args.Insert("visible", false);
GameMessages::SendUIMessageServerToSingleClient(sender, sender->GetSystemAddress(), "ToggleBank", args);
GameMessages::SendNotifyClientObject(self->GetObjectID(), u"CloseBank", 0, 0, LWOOBJID_EMPTY, "", sender->GetSystemAddress());
}

View File

@ -1,19 +1,20 @@
#include "MailBoxServer.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "GameMessages.h"
#include "Entity.h"
void MailBoxServer::OnUse(Entity* self, Entity* user) {
AMFStringValue* value = new AMFStringValue();
value->SetStringValue("Mail");
AMFArrayValue args;
args.InsertValue("state", value);
GameMessages::SendUIMessageServerToSingleClient(user, user->GetSystemAddress(), "pushGameState", &args);
args.Insert("state", "Mail");
GameMessages::SendUIMessageServerToSingleClient(user, user->GetSystemAddress(), "pushGameState", args);
}
void MailBoxServer::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) {
if (args == "toggleMail") {
AMFArrayValue args;
args.InsertValue("visible", new AMFFalseValue());
GameMessages::SendUIMessageServerToSingleClient(sender, sender->GetSystemAddress(), "ToggleMail", &args);
args.Insert("visible", false);
GameMessages::SendUIMessageServerToSingleClient(sender, sender->GetSystemAddress(), "ToggleMail", args);
}
}

View File

@ -2,7 +2,8 @@
#include "Character.h"
#include "GameMessages.h"
#include "dServer.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "Entity.h"
void StoryBoxInteractServer::OnUse(Entity* self, Entity* user) {
if (self->GetVar<bool>(u"hasCustomText")) {
@ -11,24 +12,18 @@ void StoryBoxInteractServer::OnUse(Entity* self, Entity* user) {
{
AMFArrayValue args;
auto* state = new AMFStringValue();
state->SetStringValue("Story");
args.Insert("state", "Story");
args.InsertValue("state", state);
GameMessages::SendUIMessageServerToSingleClient(user, user->GetSystemAddress(), "pushGameState", &args);
GameMessages::SendUIMessageServerToSingleClient(user, user->GetSystemAddress(), "pushGameState", args);
}
user->AddCallbackTimer(0.1f, [user, customText]() {
AMFArrayValue args;
auto* text = new AMFStringValue();
text->SetStringValue(customText);
args.Insert("visible", true);
args.Insert("text", customText);
args.InsertValue("visible", new AMFTrueValue());
args.InsertValue("text", text);
GameMessages::SendUIMessageServerToSingleClient(user, user->GetSystemAddress(), "ToggleStoryBox", &args);
GameMessages::SendUIMessageServerToSingleClient(user, user->GetSystemAddress(), "ToggleStoryBox", args);
});
return;

View File

@ -1,7 +1,7 @@
#include "NsLegoClubDoor.h"
#include "dZoneManager.h"
#include "GameMessages.h"
#include "AMFFormat.h"
#include "Amf3.h"
void NsLegoClubDoor::OnStartup(Entity* self) {
self->SetVar(u"currentZone", (int32_t)dZoneManager::Instance()->GetZoneID().GetMapID());
@ -12,116 +12,56 @@ void NsLegoClubDoor::OnStartup(Entity* self) {
args = {};
AMFStringValue* callbackClient = new AMFStringValue();
callbackClient->SetStringValue(std::to_string(self->GetObjectID()));
args.InsertValue("callbackClient", callbackClient);
args.Insert("callbackClient", std::to_string(self->GetObjectID()));
args.Insert("strIdentifier", "choiceDoor");
args.Insert("title", "%[UI_CHOICE_DESTINATION]");
AMFStringValue* strIdentifier = new AMFStringValue();
strIdentifier->SetStringValue("choiceDoor");
args.InsertValue("strIdentifier", strIdentifier);
AMFStringValue* title = new AMFStringValue();
title->SetStringValue("%[UI_CHOICE_DESTINATION]");
args.InsertValue("title", title);
AMFArrayValue* choiceOptions = new AMFArrayValue();
AMFArrayValue* choiceOptions = args.InsertArray("options");
{
AMFArrayValue* nsArgs = new AMFArrayValue();
AMFArrayValue* nsArgs = choiceOptions->PushArray();
AMFStringValue* image = new AMFStringValue();
image->SetStringValue("textures/ui/zone_thumnails/Nimbus_Station.dds");
nsArgs->InsertValue("image", image);
AMFStringValue* caption = new AMFStringValue();
caption->SetStringValue("%[UI_CHOICE_NS]");
nsArgs->InsertValue("caption", caption);
AMFStringValue* identifier = new AMFStringValue();
identifier->SetStringValue("zoneID_1200");
nsArgs->InsertValue("identifier", identifier);
AMFStringValue* tooltipText = new AMFStringValue();
tooltipText->SetStringValue("%[UI_CHOICE_NS_HOVER]");
nsArgs->InsertValue("tooltipText", tooltipText);
choiceOptions->PushBackValue(nsArgs);
nsArgs->Insert("image", "textures/ui/zone_thumnails/Nimbus_Station.dds");
nsArgs->Insert("caption", "%[UI_CHOICE_NS]");
nsArgs->Insert("identifier", "zoneID_1200");
nsArgs->Insert("tooltipText", "%[UI_CHOICE_NS_HOVER]");
}
{
AMFArrayValue* ntArgs = new AMFArrayValue();
AMFArrayValue* ntArgs = choiceOptions->PushArray();
AMFStringValue* image = new AMFStringValue();
image->SetStringValue("textures/ui/zone_thumnails/Nexus_Tower.dds");
ntArgs->InsertValue("image", image);
AMFStringValue* caption = new AMFStringValue();
caption->SetStringValue("%[UI_CHOICE_NT]");
ntArgs->InsertValue("caption", caption);
AMFStringValue* identifier = new AMFStringValue();
identifier->SetStringValue("zoneID_1900");
ntArgs->InsertValue("identifier", identifier);
AMFStringValue* tooltipText = new AMFStringValue();
tooltipText->SetStringValue("%[UI_CHOICE_NT_HOVER]");
ntArgs->InsertValue("tooltipText", tooltipText);
choiceOptions->PushBackValue(ntArgs);
ntArgs->Insert("image", "textures/ui/zone_thumnails/Nexus_Tower.dds");
ntArgs->Insert("caption", "%[UI_CHOICE_NT]");
ntArgs->Insert("identifier", "zoneID_1900");
ntArgs->Insert("tooltipText", "%[UI_CHOICE_NT_HOVER]");
}
options = choiceOptions;
args.InsertValue("options", choiceOptions);
}
void NsLegoClubDoor::OnUse(Entity* self, Entity* user) {
auto* player = user;
if (CheckChoice(self, player)) {
AMFArrayValue* multiArgs = new AMFArrayValue();
AMFArrayValue multiArgs;
AMFStringValue* callbackClient = new AMFStringValue();
callbackClient->SetStringValue(std::to_string(self->GetObjectID()));
multiArgs->InsertValue("callbackClient", callbackClient);
AMFStringValue* strIdentifier = new AMFStringValue();
strIdentifier->SetStringValue("choiceDoor");
multiArgs->InsertValue("strIdentifier", strIdentifier);
AMFStringValue* title = new AMFStringValue();
title->SetStringValue("%[UI_CHOICE_DESTINATION]");
multiArgs->InsertValue("title", title);
multiArgs->InsertValue("options", options);
multiArgs.Insert("callbackClient", std::to_string(self->GetObjectID()));
multiArgs.Insert("strIdentifier", "choiceDoor");
multiArgs.Insert("title", "%[UI_CHOICE_DESTINATION]");
multiArgs.Insert("options", static_cast<AMFBaseValue*>(options));
GameMessages::SendUIMessageServerToSingleClient(player, player->GetSystemAddress(), "QueueChoiceBox", multiArgs);
multiArgs.Remove("options", false); // We do not want the local amf to delete the options!
} else if (self->GetVar<int32_t>(u"currentZone") != m_ChoiceZoneID) {
AMFArrayValue* multiArgs = new AMFArrayValue();
AMFArrayValue multiArgs;
multiArgs.Insert("state", "Lobby");
AMFStringValue* state = new AMFStringValue();
state->SetStringValue("Lobby");
multiArgs->InsertValue("state", state);
AMFArrayValue* context = new AMFArrayValue();
AMFStringValue* user = new AMFStringValue();
user->SetStringValue(std::to_string(player->GetObjectID()));
context->InsertValue("user", user);
AMFStringValue* callbackObj = new AMFStringValue();
callbackObj->SetStringValue(std::to_string(self->GetObjectID()));
context->InsertValue("callbackObj", callbackObj);
AMFStringValue* helpVisible = new AMFStringValue();
helpVisible->SetStringValue("show");
context->InsertValue("HelpVisible", helpVisible);
AMFStringValue* type = new AMFStringValue();
type->SetStringValue("Lego_Club_Valid");
context->InsertValue("type", type);
multiArgs->InsertValue("context", context);
AMFArrayValue* context = multiArgs.InsertArray("context");
context->Insert("user", std::to_string(player->GetObjectID()));
context->Insert("callbackObj", std::to_string(self->GetObjectID()));
context->Insert("HelpVisible", "show");
context->Insert("type", "Lego_Club_Valid");
GameMessages::SendUIMessageServerToSingleClient(player, player->GetSystemAddress(), "pushGameState", multiArgs);
} else {

View File

@ -2,7 +2,7 @@
#include "CppScripts.h"
#include "ChooseYourDestinationNsToNt.h"
#include "BaseConsoleTeleportServer.h"
#include "AMFFormat.h"
#include "Amf3.h"
class NsLegoClubDoor : public CppScripts::Script, ChooseYourDestinationNsToNt, BaseConsoleTeleportServer
{

View File

@ -1,7 +1,7 @@
#include "NsLupTeleport.h"
#include "dZoneManager.h"
#include "GameMessages.h"
#include "AMFFormat.h"
#include "Amf3.h"
void NsLupTeleport::OnStartup(Entity* self) {
self->SetVar(u"currentZone", (int32_t)dZoneManager::Instance()->GetZoneID().GetMapID());
@ -12,72 +12,36 @@ void NsLupTeleport::OnStartup(Entity* self) {
args = {};
AMFStringValue* callbackClient = new AMFStringValue();
callbackClient->SetStringValue(std::to_string(self->GetObjectID()));
args.InsertValue("callbackClient", callbackClient);
args.Insert("callbackClient", std::to_string(self->GetObjectID()));
args.Insert("strIdentifier", "choiceDoor");
args.Insert("title", "%[UI_CHOICE_DESTINATION]");
AMFStringValue* strIdentifier = new AMFStringValue();
strIdentifier->SetStringValue("choiceDoor");
args.InsertValue("strIdentifier", strIdentifier);
AMFStringValue* title = new AMFStringValue();
title->SetStringValue("%[UI_CHOICE_DESTINATION]");
args.InsertValue("title", title);
AMFArrayValue* choiceOptions = new AMFArrayValue();
AMFArrayValue* choiceOptions = args.InsertArray("options");
{
AMFArrayValue* nsArgs = new AMFArrayValue();
AMFArrayValue* nsArgs = choiceOptions->PushArray();
AMFStringValue* image = new AMFStringValue();
image->SetStringValue("textures/ui/zone_thumnails/Nimbus_Station.dds");
nsArgs->InsertValue("image", image);
AMFStringValue* caption = new AMFStringValue();
caption->SetStringValue("%[UI_CHOICE_NS]");
nsArgs->InsertValue("caption", caption);
AMFStringValue* identifier = new AMFStringValue();
identifier->SetStringValue("zoneID_1200");
nsArgs->InsertValue("identifier", identifier);
AMFStringValue* tooltipText = new AMFStringValue();
tooltipText->SetStringValue("%[UI_CHOICE_NS_HOVER]");
nsArgs->InsertValue("tooltipText", tooltipText);
choiceOptions->PushBackValue(nsArgs);
nsArgs->Insert("image", "textures/ui/zone_thumnails/Nimbus_Station.dds");
nsArgs->Insert("caption", "%[UI_CHOICE_NS]");
nsArgs->Insert("identifier", "zoneID_1200");
nsArgs->Insert("tooltipText", "%[UI_CHOICE_NS_HOVER]");
}
{
AMFArrayValue* ntArgs = new AMFArrayValue();
AMFArrayValue* ntArgs = choiceOptions->PushArray();
AMFStringValue* image = new AMFStringValue();
image->SetStringValue("textures/ui/zone_thumnails/Nexus_Tower.dds");
ntArgs->InsertValue("image", image);
AMFStringValue* caption = new AMFStringValue();
caption->SetStringValue("%[UI_CHOICE_NT]");
ntArgs->InsertValue("caption", caption);
AMFStringValue* identifier = new AMFStringValue();
identifier->SetStringValue("zoneID_1900");
ntArgs->InsertValue("identifier", identifier);
AMFStringValue* tooltipText = new AMFStringValue();
tooltipText->SetStringValue("%[UI_CHOICE_NT_HOVER]");
ntArgs->InsertValue("tooltipText", tooltipText);
choiceOptions->PushBackValue(ntArgs);
ntArgs->Insert("image", "textures/ui/zone_thumnails/Nexus_Tower.dds");
ntArgs->Insert("caption", "%[UI_CHOICE_NT]");
ntArgs->Insert("identifier", "zoneID_1900");
ntArgs->Insert("tooltipText", "%[UI_CHOICE_NT_HOVER]");
}
args.InsertValue("options", choiceOptions);
}
void NsLupTeleport::OnUse(Entity* self, Entity* user) {
auto* player = user;
if (CheckChoice(self, player)) {
GameMessages::SendUIMessageServerToSingleClient(player, player->GetSystemAddress(), "QueueChoiceBox", &args);
GameMessages::SendUIMessageServerToSingleClient(player, player->GetSystemAddress(), "QueueChoiceBox", args);
} else {
BaseOnUse(self, player);
}

View File

@ -2,7 +2,7 @@
#include "CppScripts.h"
#include "ChooseYourDestinationNsToNt.h"
#include "BaseConsoleTeleportServer.h"
#include "AMFFormat.h"
#include "Amf3.h"
class NsLupTeleport : public CppScripts::Script, ChooseYourDestinationNsToNt, BaseConsoleTeleportServer
{

View File

@ -1,6 +1,6 @@
#include "NtConsoleTeleportServer.h"
#include "Entity.h"
#include "AMFFormat.h"
#include "Amf3.h"
void NtConsoleTeleportServer::OnStartup(Entity* self) {
self->SetVar(u"teleportAnim", m_TeleportAnim);

View File

@ -1,7 +1,8 @@
#include "PropertyBankInteract.h"
#include "EntityManager.h"
#include "GameMessages.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "Entity.h"
void PropertyBankInteract::OnStartup(Entity* self) {
auto* zoneControl = EntityManager::Instance()->GetZoneControlEntity();
@ -20,11 +21,10 @@ void PropertyBankInteract::OnPlayerLoaded(Entity* self, Entity* player) {
void PropertyBankInteract::OnUse(Entity* self, Entity* user) {
AMFArrayValue args;
auto* value = new AMFStringValue();
value->SetStringValue("bank");
args.InsertValue("state", value);
GameMessages::SendUIMessageServerToSingleClient(user, user->GetSystemAddress(), "pushGameState", &args);
args.Insert("state", "bank");
GameMessages::SendUIMessageServerToSingleClient(user, user->GetSystemAddress(), "pushGameState", args);
GameMessages::SendNotifyClientObject(self->GetObjectID(), u"OpenBank", 0, 0, LWOOBJID_EMPTY,
"", user->GetSystemAddress());
@ -34,9 +34,10 @@ void PropertyBankInteract::OnFireEventServerSide(Entity* self, Entity* sender, s
int32_t param2, int32_t param3) {
if (args == "ToggleBank") {
AMFArrayValue amfArgs;
amfArgs.InsertValue("visible", new AMFFalseValue());
GameMessages::SendUIMessageServerToSingleClient(sender, sender->GetSystemAddress(), "ToggleBank", &amfArgs);
amfArgs.Insert("visible", false);
GameMessages::SendUIMessageServerToSingleClient(sender, sender->GetSystemAddress(), "ToggleBank", amfArgs);
GameMessages::SendNotifyClientObject(self->GetObjectID(), u"CloseBank", 0, 0, LWOOBJID_EMPTY,
"", sender->GetSystemAddress());

View File

@ -60,7 +60,7 @@
#include "AssetManager.h"
#include "LevelProgressionComponent.h"
#include "eBlueprintSaveResponseType.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "NiPoint3.h"
#include "eServerDisconnectIdentifiers.h"
#include "eObjectBits.h"
@ -598,15 +598,11 @@ void HandlePacketChat(Packet* packet) {
//Send to our clients:
AMFArrayValue args;
auto* titleValue = new AMFStringValue();
titleValue->SetStringValue(title.c_str());
auto* messageValue = new AMFStringValue();
messageValue->SetStringValue(msg.c_str());
args.InsertValue("title", titleValue);
args.InsertValue("message", messageValue);
args.Insert("title", title);
args.Insert("message", msg);
GameMessages::SendUIMessageServerToAllClients("ToggleAnnounce", &args);
GameMessages::SendUIMessageServerToAllClients("ToggleAnnounce", args);
break;
}

View File

@ -3,14 +3,17 @@
#include <gtest/gtest.h>
#include "AMFDeserialize.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "Game.h"
#include "dLogger.h"
/**
* Helper method that all tests use to get their respective AMF.
*/
std::unique_ptr<AMFValue> ReadFromBitStream(RakNet::BitStream* bitStream) {
AMFBaseValue* ReadFromBitStream(RakNet::BitStream* bitStream) {
AMFDeserialize deserializer;
std::unique_ptr<AMFValue> returnValue(deserializer.Read(bitStream));
AMFBaseValue* returnValue(deserializer.Read(bitStream));
return returnValue;
}
@ -18,10 +21,10 @@ std::unique_ptr<AMFValue> ReadFromBitStream(RakNet::BitStream* bitStream) {
* @brief Test reading an AMFUndefined value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFUndefinedTest) {
CBITSTREAM
CBITSTREAM;
bitStream.Write<uint8_t>(0x00);
std::unique_ptr<AMFValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), AMFValueType::AMFUndefined);
std::unique_ptr<AMFBaseValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::Undefined);
}
/**
@ -29,54 +32,54 @@ TEST(dCommonTests, AMFDeserializeAMFUndefinedTest) {
*
*/
TEST(dCommonTests, AMFDeserializeAMFNullTest) {
CBITSTREAM
CBITSTREAM;
bitStream.Write<uint8_t>(0x01);
std::unique_ptr<AMFValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), AMFValueType::AMFNull);
std::unique_ptr<AMFBaseValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::Null);
}
/**
* @brief Test reading an AMFFalse value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFFalseTest) {
CBITSTREAM
CBITSTREAM;
bitStream.Write<uint8_t>(0x02);
std::unique_ptr<AMFValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), AMFValueType::AMFFalse);
std::unique_ptr<AMFBaseValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::False);
}
/**
* @brief Test reading an AMFTrue value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFTrueTest) {
CBITSTREAM
CBITSTREAM;
bitStream.Write<uint8_t>(0x03);
std::unique_ptr<AMFValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), AMFValueType::AMFTrue);
std::unique_ptr<AMFBaseValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::True);
}
/**
* @brief Test reading an AMFInteger value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFIntegerTest) {
CBITSTREAM
CBITSTREAM;
{
bitStream.Write<uint8_t>(0x04);
// 127 == 01111111
bitStream.Write<uint8_t>(127);
std::unique_ptr<AMFValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), AMFValueType::AMFInteger);
std::unique_ptr<AMFBaseValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::Integer);
// Check that the max value of a byte can be read correctly
ASSERT_EQ(static_cast<AMFIntegerValue*>(res.get())->GetIntegerValue(), 127);
ASSERT_EQ(static_cast<AMFIntValue*>(res.get())->GetValue(), 127);
}
bitStream.Reset();
{
bitStream.Write<uint8_t>(0x04);
bitStream.Write<uint32_t>(UINT32_MAX);
std::unique_ptr<AMFValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), AMFValueType::AMFInteger);
std::unique_ptr<AMFBaseValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::Integer);
// Check that we can read the maximum value correctly
ASSERT_EQ(static_cast<AMFIntegerValue*>(res.get())->GetIntegerValue(), 536870911);
ASSERT_EQ(static_cast<AMFIntValue*>(res.get())->GetValue(), 536870911);
}
bitStream.Reset();
{
@ -87,10 +90,10 @@ TEST(dCommonTests, AMFDeserializeAMFIntegerTest) {
bitStream.Write<uint8_t>(255);
// 127 == 01111111
bitStream.Write<uint8_t>(127);
std::unique_ptr<AMFValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), AMFValueType::AMFInteger);
std::unique_ptr<AMFBaseValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::Integer);
// Check that short max can be read correctly
ASSERT_EQ(static_cast<AMFIntegerValue*>(res.get())->GetIntegerValue(), UINT16_MAX);
ASSERT_EQ(static_cast<AMFIntValue*>(res.get())->GetValue(), UINT16_MAX);
}
bitStream.Reset();
{
@ -99,10 +102,10 @@ TEST(dCommonTests, AMFDeserializeAMFIntegerTest) {
bitStream.Write<uint8_t>(255);
// 127 == 01111111
bitStream.Write<uint8_t>(127);
std::unique_ptr<AMFValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), AMFValueType::AMFInteger);
std::unique_ptr<AMFBaseValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::Integer);
// Check that 2 byte max can be read correctly
ASSERT_EQ(static_cast<AMFIntegerValue*>(res.get())->GetIntegerValue(), 16383);
ASSERT_EQ(static_cast<AMFIntValue*>(res.get())->GetValue(), 16383);
}
}
@ -110,42 +113,42 @@ TEST(dCommonTests, AMFDeserializeAMFIntegerTest) {
* @brief Test reading an AMFDouble value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFDoubleTest) {
CBITSTREAM
CBITSTREAM;
bitStream.Write<uint8_t>(0x05);
bitStream.Write<double>(25346.4f);
std::unique_ptr<AMFValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), AMFValueType::AMFDouble);
ASSERT_EQ(static_cast<AMFDoubleValue*>(res.get())->GetDoubleValue(), 25346.4f);
std::unique_ptr<AMFBaseValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::Double);
ASSERT_EQ(static_cast<AMFDoubleValue*>(res.get())->GetValue(), 25346.4f);
}
/**
* @brief Test reading an AMFString value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFStringTest) {
CBITSTREAM
CBITSTREAM;
bitStream.Write<uint8_t>(0x06);
bitStream.Write<uint8_t>(0x0F);
std::string toWrite = "stateID";
for (auto e : toWrite) bitStream.Write<char>(e);
std::unique_ptr<AMFValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), AMFValueType::AMFString);
ASSERT_EQ(static_cast<AMFStringValue*>(res.get())->GetStringValue(), "stateID");
std::unique_ptr<AMFBaseValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::String);
ASSERT_EQ(static_cast<AMFStringValue*>(res.get())->GetValue(), "stateID");
}
/**
* @brief Test reading an AMFArray value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFArrayTest) {
CBITSTREAM
CBITSTREAM;
// Test empty AMFArray
bitStream.Write<uint8_t>(0x09);
bitStream.Write<uint8_t>(0x01);
bitStream.Write<uint8_t>(0x01);
{
std::unique_ptr<AMFValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), AMFValueType::AMFArray);
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->GetAssociativeMap().size(), 0);
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->GetDenseArray().size(), 0);
std::unique_ptr<AMFBaseValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::Array);
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->GetAssociative().size(), 0);
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->GetDense().size(), 0);
}
bitStream.Reset();
// Test a key'd value and dense value
@ -161,32 +164,32 @@ TEST(dCommonTests, AMFDeserializeAMFArrayTest) {
bitStream.Write<uint8_t>(0x0B);
for (auto e : "10447") if (e != '\0') bitStream.Write<char>(e);
{
std::unique_ptr<AMFValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), AMFValueType::AMFArray);
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->GetAssociativeMap().size(), 1);
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->GetDenseArray().size(), 1);
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->FindValue<AMFStringValue>("BehaviorID")->GetStringValue(), "10447");
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->GetValueAt<AMFStringValue>(0)->GetStringValue(), "10447");
std::unique_ptr<AMFBaseValue> res(ReadFromBitStream(&bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::Array);
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->GetAssociative().size(), 1);
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->GetDense().size(), 1);
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->Get<std::string>("BehaviorID")->GetValue(), "10447");
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->Get<std::string>(0)->GetValue(), "10447");
}
}
/**
* @brief This test checks that if we recieve an unimplemented AMFValueType
* @brief This test checks that if we recieve an unimplemented eAmf
* we correctly throw an error and can actch it.
* Yes this leaks memory.
*/
TEST(dCommonTests, AMFDeserializeUnimplementedValuesTest) {
std::vector<AMFValueType> unimplementedValues = {
AMFValueType::AMFXMLDoc,
AMFValueType::AMFDate,
AMFValueType::AMFObject,
AMFValueType::AMFXML,
AMFValueType::AMFByteArray,
AMFValueType::AMFVectorInt,
AMFValueType::AMFVectorUInt,
AMFValueType::AMFVectorDouble,
AMFValueType::AMFVectorObject,
AMFValueType::AMFDictionary
std::vector<eAmf> unimplementedValues = {
eAmf::XMLDoc,
eAmf::Date,
eAmf::Object,
eAmf::XML,
eAmf::ByteArray,
eAmf::VectorInt,
eAmf::VectorUInt,
eAmf::VectorDouble,
eAmf::VectorObject,
eAmf::Dictionary
};
// Run unimplemented tests to check that errors are thrown if
// unimplemented AMF values are attempted to be parsed.
@ -202,16 +205,16 @@ TEST(dCommonTests, AMFDeserializeUnimplementedValuesTest) {
fileStream.close();
for (auto amfValueType : unimplementedValues) {
for (auto value : unimplementedValues) {
RakNet::BitStream testBitStream;
for (auto element : baseBitStream) {
testBitStream.Write(element);
}
testBitStream.Write(amfValueType);
testBitStream.Write(value);
bool caughtException = false;
try {
ReadFromBitStream(&testBitStream);
} catch (AMFValueType unimplementedValueType) {
} catch (eAmf unimplementedValueType) {
caughtException = true;
}
@ -235,116 +238,116 @@ TEST(dCommonTests, AMFDeserializeLivePacketTest) {
testFileStream.close();
auto resultFromFn = ReadFromBitStream(&testBitStream);
std::unique_ptr<AMFBaseValue> resultFromFn(ReadFromBitStream(&testBitStream));
auto result = static_cast<AMFArrayValue*>(resultFromFn.get());
// Test the outermost array
ASSERT_EQ(result->FindValue<AMFStringValue>("BehaviorID")->GetStringValue(), "10447");
ASSERT_EQ(result->FindValue<AMFStringValue>("objectID")->GetStringValue(), "288300744895913279");
ASSERT_EQ(result->Get<std::string>("BehaviorID")->GetValue(), "10447");
ASSERT_EQ(result->Get<std::string>("objectID")->GetValue(), "288300744895913279");
// Test the execution state array
auto executionState = result->FindValue<AMFArrayValue>("executionState");
auto executionState = result->GetArray("executionState");
ASSERT_NE(executionState, nullptr);
auto strips = executionState->FindValue<AMFArrayValue>("strips")->GetDenseArray();
auto strips = executionState->GetArray("strips")->GetDense();
ASSERT_EQ(strips.size(), 1);
auto stripsPosition0 = dynamic_cast<AMFArrayValue*>(strips[0]);
auto actionIndex = stripsPosition0->FindValue<AMFDoubleValue>("actionIndex");
auto actionIndex = stripsPosition0->Get<double>("actionIndex");
ASSERT_EQ(actionIndex->GetDoubleValue(), 0.0f);
ASSERT_EQ(actionIndex->GetValue(), 0.0f);
auto stripIdExecution = stripsPosition0->FindValue<AMFDoubleValue>("id");
auto stripIdExecution = stripsPosition0->Get<double>("id");
ASSERT_EQ(stripIdExecution->GetDoubleValue(), 0.0f);
ASSERT_EQ(stripIdExecution->GetValue(), 0.0f);
auto stateIDExecution = executionState->FindValue<AMFDoubleValue>("stateID");
auto stateIdExecution = executionState->Get<double>("stateID");
ASSERT_EQ(stateIDExecution->GetDoubleValue(), 0.0f);
ASSERT_EQ(stateIdExecution->GetValue(), 0.0f);
auto states = result->FindValue<AMFArrayValue>("states")->GetDenseArray();
auto states = result->GetArray("states")->GetDense();
ASSERT_EQ(states.size(), 1);
auto firstState = dynamic_cast<AMFArrayValue*>(states[0]);
auto stateID = firstState->FindValue<AMFDoubleValue>("id");
auto stateID = firstState->Get<double>("id");
ASSERT_EQ(stateID->GetDoubleValue(), 0.0f);
ASSERT_EQ(stateID->GetValue(), 0.0f);
auto stripsInState = firstState->FindValue<AMFArrayValue>("strips")->GetDenseArray();
auto stripsInState = firstState->GetArray("strips")->GetDense();
ASSERT_EQ(stripsInState.size(), 1);
auto firstStrip = dynamic_cast<AMFArrayValue*>(stripsInState[0]);
auto actionsInFirstStrip = firstStrip->FindValue<AMFArrayValue>("actions")->GetDenseArray();
auto actionsInFirstStrip = firstStrip->GetArray("actions")->GetDense();
ASSERT_EQ(actionsInFirstStrip.size(), 3);
auto actionID = firstStrip->FindValue<AMFDoubleValue>("id");
auto actionID = firstStrip->Get<double>("id");
ASSERT_EQ(actionID->GetDoubleValue(), 0.0f);
ASSERT_EQ(actionID->GetValue(), 0.0f);
auto uiArray = firstStrip->FindValue<AMFArrayValue>("ui");
auto uiArray = firstStrip->GetArray("ui");
auto xPos = uiArray->FindValue<AMFDoubleValue>("x");
auto yPos = uiArray->FindValue<AMFDoubleValue>("y");
auto xPos = uiArray->Get<double>("x");
auto yPos = uiArray->Get<double>("y");
ASSERT_EQ(xPos->GetDoubleValue(), 103.0f);
ASSERT_EQ(yPos->GetDoubleValue(), 82.0f);
ASSERT_EQ(xPos->GetValue(), 103.0f);
ASSERT_EQ(yPos->GetValue(), 82.0f);
auto stripId = firstStrip->FindValue<AMFDoubleValue>("id");
auto stripId = firstStrip->Get<double>("id");
ASSERT_EQ(stripId->GetDoubleValue(), 0.0f);
ASSERT_EQ(stripId->GetValue(), 0.0f);
auto firstAction = dynamic_cast<AMFArrayValue*>(actionsInFirstStrip[0]);
auto firstType = firstAction->FindValue<AMFStringValue>("Type");
auto firstType = firstAction->Get<std::string>("Type");
ASSERT_EQ(firstType->GetStringValue(), "OnInteract");
ASSERT_EQ(firstType->GetValue(), "OnInteract");
auto firstCallback = firstAction->FindValue<AMFStringValue>("__callbackID__");
auto firstCallback = firstAction->Get<std::string>("__callbackID__");
ASSERT_EQ(firstCallback->GetStringValue(), "");
ASSERT_EQ(firstCallback->GetValue(), "");
auto secondAction = dynamic_cast<AMFArrayValue*>(actionsInFirstStrip[1]);
auto secondType = secondAction->FindValue<AMFStringValue>("Type");
auto secondType = secondAction->Get<std::string>("Type");
ASSERT_EQ(secondType->GetStringValue(), "FlyUp");
ASSERT_EQ(secondType->GetValue(), "FlyUp");
auto secondCallback = secondAction->FindValue<AMFStringValue>("__callbackID__");
auto secondCallback = secondAction->Get<std::string>("__callbackID__");
ASSERT_EQ(secondCallback->GetStringValue(), "");
ASSERT_EQ(secondCallback->GetValue(), "");
auto secondDistance = secondAction->FindValue<AMFDoubleValue>("Distance");
auto secondDistance = secondAction->Get<double>("Distance");
ASSERT_EQ(secondDistance->GetDoubleValue(), 25.0f);
ASSERT_EQ(secondDistance->GetValue(), 25.0f);
auto thirdAction = dynamic_cast<AMFArrayValue*>(actionsInFirstStrip[2]);
auto thirdType = thirdAction->FindValue<AMFStringValue>("Type");
auto thirdType = thirdAction->Get<std::string>("Type");
ASSERT_EQ(thirdType->GetStringValue(), "FlyDown");
ASSERT_EQ(thirdType->GetValue(), "FlyDown");
auto thirdCallback = thirdAction->FindValue<AMFStringValue>("__callbackID__");
auto thirdCallback = thirdAction->Get<std::string>("__callbackID__");
ASSERT_EQ(thirdCallback->GetStringValue(), "");
ASSERT_EQ(thirdCallback->GetValue(), "");
auto thirdDistance = thirdAction->FindValue<AMFDoubleValue>("Distance");
auto thirdDistance = thirdAction->Get<double>("Distance");
ASSERT_EQ(thirdDistance->GetDoubleValue(), 25.0f);
ASSERT_EQ(thirdDistance->GetValue(), 25.0f);
}
/**
* @brief Tests that having no BitStream returns a nullptr.
*/
TEST(dCommonTests, AMFDeserializeNullTest) {
auto result = ReadFromBitStream(nullptr);
std::unique_ptr<AMFBaseValue> result(ReadFromBitStream(nullptr));
ASSERT_EQ(result.get(), nullptr);
}
@ -361,25 +364,25 @@ TEST(dCommonTests, AMFBadConversionTest) {
testFileStream.close();
auto resultFromFn = ReadFromBitStream(&testBitStream);
std::unique_ptr<AMFBaseValue> resultFromFn(ReadFromBitStream(&testBitStream));
auto result = static_cast<AMFArrayValue*>(resultFromFn.get());
// Actually a string value.
ASSERT_EQ(result->FindValue<AMFDoubleValue>("BehaviorID"), nullptr);
ASSERT_EQ(result->Get<double>("BehaviorID"), nullptr);
// Does not exist in the associative portion
ASSERT_EQ(result->FindValue<AMFNullValue>("DOES_NOT_EXIST"), nullptr);
ASSERT_EQ(result->Get<nullptr_t>("DOES_NOT_EXIST"), nullptr);
result->PushBackValue(new AMFTrueValue());
result->Push(true);
// Exists and is correct type
ASSERT_NE(result->GetValueAt<AMFTrueValue>(0), nullptr);
ASSERT_NE(result->Get<bool>(0), nullptr);
// Value exists but is wrong typing
ASSERT_EQ(result->GetValueAt<AMFFalseValue>(0), nullptr);
ASSERT_EQ(result->Get<std::string>(0), nullptr);
// Value is out of bounds
ASSERT_EQ(result->GetValueAt<AMFTrueValue>(1), nullptr);
ASSERT_EQ(result->Get<bool>(1), nullptr);
}
/**

View File

@ -0,0 +1,116 @@
#include <gtest/gtest.h>
#include <vector>
#include "Amf3.h"
TEST(dCommonTests, AMF3AssociativeArrayTest) {
AMFArrayValue array;
array.Insert("true", true);
array.Insert("false", false);
// test associative can insert values
ASSERT_EQ(array.GetAssociative().size(), 2);
ASSERT_EQ(array.Get<bool>("true")->GetValueType(), eAmf::True);
ASSERT_EQ(array.Get<bool>("false")->GetValueType(), eAmf::False);
// Test associative can remove values
array.Remove("true");
ASSERT_EQ(array.GetAssociative().size(), 1);
ASSERT_EQ(array.Get<bool>("true"), nullptr);
ASSERT_EQ(array.Get<bool>("false")->GetValueType(), eAmf::False);
array.Remove("false");
ASSERT_EQ(array.GetAssociative().size(), 0);
ASSERT_EQ(array.Get<bool>("true"), nullptr);
ASSERT_EQ(array.Get<bool>("false"), nullptr);
// Test that multiple of the same key respect only the first element of that key
array.Insert("true", true);
array.Insert("true", false);
ASSERT_EQ(array.GetAssociative().size(), 1);
ASSERT_EQ(array.Get<bool>("true")->GetValueType(), eAmf::True);
array.Remove("true");
// Now test the dense portion
// Get some out of bounds values and cast to incorrect template types
array.Push(true);
array.Push(false);
ASSERT_EQ(array.GetDense().size(), 2);
ASSERT_EQ(array.Get<bool>(0)->GetValueType(), eAmf::True);
ASSERT_EQ(array.Get<std::string>(0), nullptr);
ASSERT_EQ(array.Get<bool>(1)->GetValueType(), eAmf::False);
ASSERT_EQ(array.Get<bool>(155), nullptr);
array.Pop();
ASSERT_EQ(array.GetDense().size(), 1);
ASSERT_EQ(array.Get<bool>(0)->GetValueType(), eAmf::True);
ASSERT_EQ(array.Get<std::string>(0), nullptr);
ASSERT_EQ(array.Get<bool>(1), nullptr);
array.Pop();
ASSERT_EQ(array.GetDense().size(), 0);
ASSERT_EQ(array.Get<bool>(0), nullptr);
ASSERT_EQ(array.Get<std::string>(0), nullptr);
ASSERT_EQ(array.Get<bool>(1), nullptr);
}
TEST(dCommonTests, AMF3InsertionAssociativeTest) {
AMFArrayValue array;
array.Insert("CString", "string");
array.Insert("String", std::string("string"));
array.Insert("False", false);
array.Insert("True", true);
array.Insert<int32_t>("Integer", 42U);
array.Insert("Double", 42.0);
array.InsertArray("Array");
array.Insert<std::vector<uint32_t>>("Undefined", {});
array.Insert("Null", nullptr);
std::cout << "test" << std::endl;
ASSERT_EQ(array.Get<const char*>("CString")->GetValueType(), eAmf::String);
std::cout << "test" << std::endl;
ASSERT_EQ(array.Get<std::string>("String")->GetValueType(), eAmf::String);
std::cout << "test" << std::endl;
ASSERT_EQ(array.Get<bool>("False")->GetValueType(), eAmf::False);
std::cout << "test" << std::endl;
ASSERT_EQ(array.Get<bool>("True")->GetValueType(), eAmf::True);
std::cout << "test" << std::endl;
ASSERT_EQ(array.Get<int32_t>("Integer")->GetValueType(), eAmf::Integer);
std::cout << "test" << std::endl;
ASSERT_EQ(array.Get<double>("Double")->GetValueType(), eAmf::Double);
std::cout << "test" << std::endl;
ASSERT_EQ(array.GetArray("Array")->GetValueType(), eAmf::Array);
std::cout << "test" << std::endl;
ASSERT_EQ(array.Get<nullptr_t>("Null")->GetValueType(), eAmf::Null);
std::cout << "test" << std::endl;
ASSERT_EQ(array.Get<std::vector<uint32_t>>("Undefined")->GetValueType(), eAmf::Undefined);
std::cout << "test" << std::endl;
}
TEST(dCommonTests, AMF3InsertionDenseTest) {
AMFArrayValue array;
array.Push<std::string>("string");
array.Push("CString");
array.Push(false);
array.Push(true);
array.Push<int32_t>(42U);
array.Push(42.0);
array.PushArray();
array.Push(nullptr);
array.Push<std::vector<uint32_t>>({});
ASSERT_EQ(array.Get<std::string>(0)->GetValueType(), eAmf::String);
ASSERT_EQ(array.Get<const char*>(1)->GetValueType(), eAmf::String);
ASSERT_EQ(array.Get<bool>(2)->GetValueType(), eAmf::False);
ASSERT_EQ(array.Get<bool>(3)->GetValueType(), eAmf::True);
ASSERT_EQ(array.Get<int32_t>(4)->GetValueType(), eAmf::Integer);
ASSERT_EQ(array.Get<double>(5)->GetValueType(), eAmf::Double);
ASSERT_EQ(array.GetArray(6)->GetValueType(), eAmf::Array);
ASSERT_EQ(array.Get<nullptr_t>(7)->GetValueType(), eAmf::Null);
ASSERT_EQ(array.Get<std::vector<uint32_t>>(8)->GetValueType(), eAmf::Undefined);
}

View File

@ -1,5 +1,6 @@
set(DCOMMONTEST_SOURCES
"AMFDeserializeTests.cpp"
"Amf3Tests.cpp"
"HeaderSkipTest.cpp"
"TestLDFFormat.cpp"
"TestNiPoint3.cpp"

View File

@ -1,5 +1,5 @@
#include "Action.h"
#include "AMFFormat.h"
#include "Amf3.h"
#include "AMFDeserialize.h"
#include "GameMessages.h"
#include "GameDependencies.h"
@ -40,8 +40,8 @@ protected:
}
AMFArrayValue* ReadArrayFromBitStream(RakNet::BitStream* inStream) {
AMFDeserialize des;
AMFValue* readArray = des.Read(inStream);
EXPECT_EQ(readArray->GetValueType(), AMFValueType::AMFArray);
AMFBaseValue* readArray = des.Read(inStream);
EXPECT_EQ(readArray->GetValueType(), eAmf::Array);
return static_cast<AMFArrayValue*>(readArray);
}
};