Added 64-bit networking support

Mainly for use with doubles as ZScript can now take advantage of it. Enforced sizing on ints passed to and read from net functions.
This commit is contained in:
Boondorl 2024-01-05 15:08:49 -05:00 committed by Christoph Oelckers
parent a8e350aed8
commit df9b2cd9bf
7 changed files with 243 additions and 63 deletions

View file

@ -259,7 +259,7 @@ static struct TicSpecial
streamoffs = 0; streamoffs = 0;
} }
TicSpecial &operator << (uint8_t it) TicSpecial &operator << (int8_t it)
{ {
if (streamptr) if (streamptr)
{ {
@ -269,7 +269,7 @@ static struct TicSpecial
return *this; return *this;
} }
TicSpecial &operator << (short it) TicSpecial &operator << (int16_t it)
{ {
if (streamptr) if (streamptr)
{ {
@ -279,7 +279,7 @@ static struct TicSpecial
return *this; return *this;
} }
TicSpecial &operator << (int it) TicSpecial &operator << (int32_t it)
{ {
if (streamptr) if (streamptr)
{ {
@ -289,6 +289,16 @@ static struct TicSpecial
return *this; return *this;
} }
TicSpecial& operator << (int64_t it)
{
if (streamptr)
{
CheckSpace(8);
WriteInt64(it, &streamptr);
}
return *this;
}
TicSpecial &operator << (float it) TicSpecial &operator << (float it)
{ {
if (streamptr) if (streamptr)
@ -299,6 +309,16 @@ static struct TicSpecial
return *this; return *this;
} }
TicSpecial& operator << (double it)
{
if (streamptr)
{
CheckSpace(8);
WriteDouble(it, &streamptr);
}
return *this;
}
TicSpecial &operator << (const char *it) TicSpecial &operator << (const char *it)
{ {
if (streamptr) if (streamptr)
@ -2047,17 +2067,22 @@ void Net_NewMakeTic (void)
specials.NewMakeTic (); specials.NewMakeTic ();
} }
void Net_WriteInt8 (uint8_t it) void Net_WriteInt8 (int8_t it)
{ {
specials << it; specials << it;
} }
void Net_WriteInt16 (short it) void Net_WriteInt16 (int16_t it)
{ {
specials << it; specials << it;
} }
void Net_WriteInt32 (int it) void Net_WriteInt32 (int32_t it)
{
specials << it;
}
void Net_WriteInt64(int64_t it)
{ {
specials << it; specials << it;
} }
@ -2067,6 +2092,11 @@ void Net_WriteFloat (float it)
specials << it; specials << it;
} }
void Net_WriteDouble(double it)
{
specials << it;
}
void Net_WriteString (const char *it) void Net_WriteString (const char *it)
{ {
specials << it; specials << it;

View file

@ -64,10 +64,12 @@ void Net_CheckLastReceived(int);
// [RH] Functions for making and using special "ticcmds" // [RH] Functions for making and using special "ticcmds"
void Net_NewMakeTic (); void Net_NewMakeTic ();
void Net_WriteInt8 (uint8_t); void Net_WriteInt8 (int8_t);
void Net_WriteInt16 (short); void Net_WriteInt16 (int16_t);
void Net_WriteInt32 (int); void Net_WriteInt32 (int32_t);
void Net_WriteInt64(int64_t);
void Net_WriteFloat (float); void Net_WriteFloat (float);
void Net_WriteDouble(double);
void Net_WriteString (const char *); void Net_WriteString (const char *);
void Net_WriteBytes (const uint8_t *, int len); void Net_WriteBytes (const uint8_t *, int len);

View file

@ -55,38 +55,57 @@ const char *ReadStringConst(uint8_t **stream)
return string; return string;
} }
int ReadInt8 (uint8_t **stream) int8_t ReadInt8 (uint8_t **stream)
{ {
uint8_t v = **stream; int8_t v = **stream;
*stream += 1; *stream += 1;
return v; return v;
} }
int ReadInt16 (uint8_t **stream) int16_t ReadInt16 (uint8_t **stream)
{ {
short v = (((*stream)[0]) << 8) | (((*stream)[1])); int16_t v = (((*stream)[0]) << 8) | (((*stream)[1]));
*stream += 2; *stream += 2;
return v; return v;
} }
int ReadInt32 (uint8_t **stream) int32_t ReadInt32 (uint8_t **stream)
{ {
int v = (((*stream)[0]) << 24) | (((*stream)[1]) << 16) | (((*stream)[2]) << 8) | (((*stream)[3])); int32_t v = (((*stream)[0]) << 24) | (((*stream)[1]) << 16) | (((*stream)[2]) << 8) | (((*stream)[3]));
*stream += 4; *stream += 4;
return v; return v;
} }
int64_t ReadInt64(uint8_t** stream)
{
int64_t v = (int64_t((*stream)[0]) << 56) | (int64_t((*stream)[1]) << 48) | (int64_t((*stream)[2]) << 40) | (int64_t((*stream)[3]) << 32)
| (int64_t((*stream)[4]) << 24) | (int64_t((*stream)[5]) << 16) | (int64_t((*stream)[6]) << 8) | (int64_t((*stream)[7]));
*stream += 8;
return v;
}
float ReadFloat (uint8_t **stream) float ReadFloat (uint8_t **stream)
{ {
union union
{ {
int i; int32_t i;
float f; float f;
} fakeint; } fakeint;
fakeint.i = ReadInt32 (stream); fakeint.i = ReadInt32 (stream);
return fakeint.f; return fakeint.f;
} }
double ReadDouble(uint8_t** stream)
{
union
{
int64_t i;
double f;
} fakeint;
fakeint.i = ReadInt64(stream);
return fakeint.f;
}
void WriteString (const char *string, uint8_t **stream) void WriteString (const char *string, uint8_t **stream)
{ {
char *p = *((char **)stream); char *p = *((char **)stream);
@ -100,20 +119,20 @@ void WriteString (const char *string, uint8_t **stream)
} }
void WriteInt8 (uint8_t v, uint8_t **stream) void WriteInt8 (int8_t v, uint8_t **stream)
{ {
**stream = v; **stream = v;
*stream += 1; *stream += 1;
} }
void WriteInt16 (short v, uint8_t **stream) void WriteInt16 (int16_t v, uint8_t **stream)
{ {
(*stream)[0] = v >> 8; (*stream)[0] = v >> 8;
(*stream)[1] = v & 255; (*stream)[1] = v & 255;
*stream += 2; *stream += 2;
} }
void WriteInt32 (int v, uint8_t **stream) void WriteInt32 (int32_t v, uint8_t **stream)
{ {
(*stream)[0] = v >> 24; (*stream)[0] = v >> 24;
(*stream)[1] = (v >> 16) & 255; (*stream)[1] = (v >> 16) & 255;
@ -122,17 +141,41 @@ void WriteInt32 (int v, uint8_t **stream)
*stream += 4; *stream += 4;
} }
void WriteInt64(int64_t v, uint8_t** stream)
{
(*stream)[0] = v >> 56;
(*stream)[1] = (v >> 48) & 255;
(*stream)[2] = (v >> 40) & 255;
(*stream)[3] = (v >> 32) & 255;
(*stream)[4] = (v >> 24) & 255;
(*stream)[5] = (v >> 16) & 255;
(*stream)[6] = (v >> 8) & 255;
(*stream)[7] = v & 255;
*stream += 8;
}
void WriteFloat (float v, uint8_t **stream) void WriteFloat (float v, uint8_t **stream)
{ {
union union
{ {
int i; int32_t i;
float f; float f;
} fakeint; } fakeint;
fakeint.f = v; fakeint.f = v;
WriteInt32 (fakeint.i, stream); WriteInt32 (fakeint.i, stream);
} }
void WriteDouble(double v, uint8_t** stream)
{
union
{
int64_t i;
double f;
} fakeint;
fakeint.f = v;
WriteInt64(fakeint.i, stream);
}
// Returns the number of bytes read // Returns the number of bytes read
int UnpackUserCmd (usercmd_t *ucmd, const usercmd_t *basis, uint8_t **stream) int UnpackUserCmd (usercmd_t *ucmd, const usercmd_t *basis, uint8_t **stream)
{ {

View file

@ -247,16 +247,20 @@ int SkipTicCmd (uint8_t **stream, int count);
void ReadTicCmd (uint8_t **stream, int player, int tic); void ReadTicCmd (uint8_t **stream, int player, int tic);
void RunNetSpecs (int player, int buf); void RunNetSpecs (int player, int buf);
int ReadInt8 (uint8_t **stream); int8_t ReadInt8 (uint8_t **stream);
int ReadInt16 (uint8_t **stream); int16_t ReadInt16 (uint8_t **stream);
int ReadInt32 (uint8_t **stream); int32_t ReadInt32 (uint8_t **stream);
int64_t ReadInt64(uint8_t** stream);
float ReadFloat (uint8_t **stream); float ReadFloat (uint8_t **stream);
double ReadDouble(uint8_t** stream);
char *ReadString (uint8_t **stream); char *ReadString (uint8_t **stream);
const char *ReadStringConst(uint8_t **stream); const char *ReadStringConst(uint8_t **stream);
void WriteInt8 (uint8_t val, uint8_t **stream); void WriteInt8 (int8_t val, uint8_t **stream);
void WriteInt16 (short val, uint8_t **stream); void WriteInt16 (int16_t val, uint8_t **stream);
void WriteInt32 (int val, uint8_t **stream); void WriteInt32 (int32_t val, uint8_t **stream);
void WriteInt64(int64_t val, uint8_t** stream);
void WriteFloat (float val, uint8_t **stream); void WriteFloat (float val, uint8_t **stream);
void WriteDouble(double val, uint8_t** stream);
void WriteString (const char *string, uint8_t **stream); void WriteString (const char *string, uint8_t **stream);
#endif //__D_PROTOCOL_H__ #endif //__D_PROTOCOL_H__

View file

@ -115,6 +115,12 @@ void DNetworkBuffer::AddFloat(double msg)
_buffer.Push({ NET_FLOAT, msg }); _buffer.Push({ NET_FLOAT, msg });
} }
void DNetworkBuffer::AddDouble(double msg)
{
_size += 8u;
_buffer.Push({ NET_DOUBLE, msg });
}
void DNetworkBuffer::AddString(const FString& msg) void DNetworkBuffer::AddString(const FString& msg)
{ {
_size += msg.Len() + 1u; _size += msg.Len() + 1u;
@ -154,9 +160,10 @@ void DNetworkBuffer::Serialize(FSerializer& arc)
{ {
switch (value.GetType()) switch (value.GetType())
{ {
case NET_DOUBLE:
case NET_FLOAT: case NET_FLOAT:
{ {
double f = value.GetFloat(); double f = value.GetDouble();
arc(nullptr, f); arc(nullptr, f);
break; break;
} }
@ -233,6 +240,14 @@ void DNetworkBuffer::Serialize(FSerializer& arc)
break; break;
} }
case NET_DOUBLE:
{
double f = 0.0;
arc(nullptr, f);
AddDouble(f);
break;
}
case NET_STRING: case NET_STRING:
{ {
FString s = {}; FString s = {};
@ -404,6 +419,10 @@ bool EventManager::SendNetworkCommand(const FName& cmd, VMVa_List& args)
bytes += 4u; bytes += 4u;
break; break;
case NET_DOUBLE:
bytes += 8u;
break;
case NET_STRING: case NET_STRING:
{ {
++bytes; // Strings will always consume at least one byte. ++bytes; // Strings will always consume at least one byte.
@ -452,6 +471,10 @@ bool EventManager::SendNetworkCommand(const FName& cmd, VMVa_List& args)
Net_WriteFloat(ListGetDouble(args)); Net_WriteFloat(ListGetDouble(args));
break; break;
case NET_DOUBLE:
Net_WriteDouble(ListGetDouble(args));
break;
case NET_STRING: case NET_STRING:
{ {
const FString* str = ListGetString(args); const FString* str = ListGetString(args);
@ -498,7 +521,11 @@ bool EventManager::SendNetworkBuffer(const FName& cmd, const DNetworkBuffer* buf
break; break;
case NET_FLOAT: case NET_FLOAT:
Net_WriteFloat(value.GetFloat()); Net_WriteFloat(value.GetDouble());
break;
case NET_DOUBLE:
Net_WriteDouble(value.GetDouble());
break; break;
case NET_STRING: case NET_STRING:
@ -1047,6 +1074,13 @@ DEFINE_ACTION_FUNCTION(FNetworkCommand, ReadFloat)
ACTION_RETURN_FLOAT(self->ReadFloat()); ACTION_RETURN_FLOAT(self->ReadFloat());
} }
DEFINE_ACTION_FUNCTION(FNetworkCommand, ReadDouble)
{
PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand);
ACTION_RETURN_FLOAT(self->ReadDouble());
}
DEFINE_ACTION_FUNCTION(FNetworkCommand, ReadString) DEFINE_ACTION_FUNCTION(FNetworkCommand, ReadString)
{ {
PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand); PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand);
@ -1092,8 +1126,8 @@ DEFINE_ACTION_FUNCTION(FNetworkCommand, ReadVector2)
PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand); PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand);
DVector2 vec = {}; DVector2 vec = {};
vec.X = self->ReadFloat(); vec.X = self->ReadDouble();
vec.Y = self->ReadFloat(); vec.Y = self->ReadDouble();
ACTION_RETURN_VEC2(vec); ACTION_RETURN_VEC2(vec);
} }
@ -1102,9 +1136,9 @@ DEFINE_ACTION_FUNCTION(FNetworkCommand, ReadVector3)
PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand); PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand);
DVector3 vec = {}; DVector3 vec = {};
vec.X = self->ReadFloat(); vec.X = self->ReadDouble();
vec.Y = self->ReadFloat(); vec.Y = self->ReadDouble();
vec.Z = self->ReadFloat(); vec.Z = self->ReadDouble();
ACTION_RETURN_VEC3(vec); ACTION_RETURN_VEC3(vec);
} }
@ -1113,10 +1147,10 @@ DEFINE_ACTION_FUNCTION(FNetworkCommand, ReadVector4)
PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand); PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand);
DVector4 vec = {}; DVector4 vec = {};
vec.X = self->ReadFloat(); vec.X = self->ReadDouble();
vec.Y = self->ReadFloat(); vec.Y = self->ReadDouble();
vec.Z = self->ReadFloat(); vec.Z = self->ReadDouble();
vec.W = self->ReadFloat(); vec.W = self->ReadDouble();
ACTION_RETURN_VEC4(vec); ACTION_RETURN_VEC4(vec);
} }
@ -1125,10 +1159,10 @@ DEFINE_ACTION_FUNCTION(FNetworkCommand, ReadQuat)
PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand); PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand);
DQuaternion quat = {}; DQuaternion quat = {};
quat.X = self->ReadFloat(); quat.X = self->ReadDouble();
quat.Y = self->ReadFloat(); quat.Y = self->ReadDouble();
quat.Z = self->ReadFloat(); quat.Z = self->ReadDouble();
quat.W = self->ReadFloat(); quat.W = self->ReadDouble();
ACTION_RETURN_QUAT(quat); ACTION_RETURN_QUAT(quat);
} }
@ -1160,14 +1194,20 @@ DEFINE_ACTION_FUNCTION(FNetworkCommand, ReadIntArray)
return 0; return 0;
} }
DEFINE_ACTION_FUNCTION(FNetworkCommand, ReadFloatArray) DEFINE_ACTION_FUNCTION(FNetworkCommand, ReadDoubleArray)
{ {
PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand); PARAM_SELF_STRUCT_PROLOGUE(FNetworkCommand);
PARAM_OUTPOINTER(values, TArray<double>); PARAM_OUTPOINTER(values, TArray<double>);
PARAM_BOOL(doublePrecision);
unsigned int size = self->ReadInt(); unsigned int size = self->ReadInt();
for (unsigned int i = 0u; i < size; ++i) for (unsigned int i = 0u; i < size; ++i)
values->Push(self->ReadFloat()); {
if (doublePrecision)
values->Push(self->ReadDouble());
else
values->Push(self->ReadFloat());
}
return 0; return 0;
} }
@ -1225,6 +1265,14 @@ DEFINE_ACTION_FUNCTION(DNetworkBuffer, AddFloat)
return 0; return 0;
} }
DEFINE_ACTION_FUNCTION(DNetworkBuffer, AddDouble)
{
PARAM_SELF_PROLOGUE(DNetworkBuffer);
PARAM_FLOAT(value);
self->AddDouble(value);
return 0;
}
DEFINE_ACTION_FUNCTION(DNetworkBuffer, AddString) DEFINE_ACTION_FUNCTION(DNetworkBuffer, AddString)
{ {
PARAM_SELF_PROLOGUE(DNetworkBuffer); PARAM_SELF_PROLOGUE(DNetworkBuffer);
@ -1264,8 +1312,8 @@ DEFINE_ACTION_FUNCTION(DNetworkBuffer, AddVector2)
PARAM_SELF_PROLOGUE(DNetworkBuffer); PARAM_SELF_PROLOGUE(DNetworkBuffer);
PARAM_FLOAT(x); PARAM_FLOAT(x);
PARAM_FLOAT(y); PARAM_FLOAT(y);
self->AddFloat(x); self->AddDouble(x);
self->AddFloat(y); self->AddDouble(y);
return 0; return 0;
} }
@ -1275,9 +1323,9 @@ DEFINE_ACTION_FUNCTION(DNetworkBuffer, AddVector3)
PARAM_FLOAT(x); PARAM_FLOAT(x);
PARAM_FLOAT(y); PARAM_FLOAT(y);
PARAM_FLOAT(z); PARAM_FLOAT(z);
self->AddFloat(x); self->AddDouble(x);
self->AddFloat(y); self->AddDouble(y);
self->AddFloat(z); self->AddDouble(z);
return 0; return 0;
} }
@ -1288,10 +1336,10 @@ DEFINE_ACTION_FUNCTION(DNetworkBuffer, AddVector4)
PARAM_FLOAT(y); PARAM_FLOAT(y);
PARAM_FLOAT(z); PARAM_FLOAT(z);
PARAM_FLOAT(w); PARAM_FLOAT(w);
self->AddFloat(x); self->AddDouble(x);
self->AddFloat(y); self->AddDouble(y);
self->AddFloat(z); self->AddDouble(z);
self->AddFloat(w); self->AddDouble(w);
return 0; return 0;
} }
@ -1302,10 +1350,10 @@ DEFINE_ACTION_FUNCTION(DNetworkBuffer, AddQuat)
PARAM_FLOAT(y); PARAM_FLOAT(y);
PARAM_FLOAT(z); PARAM_FLOAT(z);
PARAM_FLOAT(w); PARAM_FLOAT(w);
self->AddFloat(x); self->AddDouble(x);
self->AddFloat(y); self->AddDouble(y);
self->AddFloat(z); self->AddDouble(z);
self->AddFloat(w); self->AddDouble(w);
return 0; return 0;
} }
@ -1338,15 +1386,21 @@ DEFINE_ACTION_FUNCTION(DNetworkBuffer, AddIntArray)
return 0; return 0;
} }
DEFINE_ACTION_FUNCTION(DNetworkBuffer, AddFloatArray) DEFINE_ACTION_FUNCTION(DNetworkBuffer, AddDoubleArray)
{ {
PARAM_SELF_PROLOGUE(DNetworkBuffer); PARAM_SELF_PROLOGUE(DNetworkBuffer);
PARAM_POINTER(values, TArray<double>); PARAM_POINTER(values, TArray<double>);
PARAM_BOOL(doublePrecision);
unsigned int size = values->Size(); unsigned int size = values->Size();
self->AddInt(size); self->AddInt(size);
for (unsigned int i = 0u; i < size; ++i) for (unsigned int i = 0u; i < size; ++i)
self->AddFloat((*values)[i]); {
if (doublePrecision)
self->AddDouble((*values)[i]);
else
self->AddFloat((*values)[i]);
}
return 0; return 0;
} }

View file

@ -26,6 +26,7 @@ enum ENetCmd
NET_INT16, NET_INT16,
NET_INT, NET_INT,
NET_FLOAT, NET_FLOAT,
NET_DOUBLE,
NET_STRING, NET_STRING,
}; };
@ -95,7 +96,7 @@ public:
return value; return value;
} }
// Floats without their first 9 bits are pretty meaningless so those are done first. // Floats without their first bits are pretty meaningless so those are done first.
double ReadFloat() double ReadFloat()
{ {
if (!IsValid()) if (!IsValid())
@ -115,13 +116,55 @@ public:
union union
{ {
int i; int32_t i;
float f; float f;
} floatCaster; } floatCaster;
floatCaster.i = value; floatCaster.i = value;
return floatCaster.f; return floatCaster.f;
} }
double ReadDouble()
{
if (!IsValid())
return 0.0;
int64_t value = int64_t(_stream[_index++]) << 56;
if (IsValid())
{
value |= int64_t(_stream[_index++]) << 48;
if (IsValid())
{
value |= int64_t(_stream[_index++]) << 40;
if (IsValid())
{
value |= int64_t(_stream[_index++]) << 32;
if (IsValid())
{
value |= int64_t(_stream[_index++]) << 24;
if (IsValid())
{
value |= int64_t(_stream[_index++]) << 16;
if (IsValid())
{
value |= int64_t(_stream[_index++]) << 8;
if (IsValid())
value |= int64_t(_stream[_index++]);
}
}
}
}
}
}
union
{
int64_t i;
double f;
} floatCaster;
floatCaster.i = value;
return floatCaster.f;
}
const char* ReadString() const char* ReadString()
{ {
if (!IsValid()) if (!IsValid())
@ -159,7 +202,7 @@ public:
return std::get<int>(_message); return std::get<int>(_message);
} }
inline double GetFloat() const inline double GetDouble() const
{ {
return std::get<double>(_message); return std::get<double>(_message);
} }
@ -194,6 +237,7 @@ public:
void AddInt16(int word); void AddInt16(int word);
void AddInt(int msg); void AddInt(int msg);
void AddFloat(double msg); void AddFloat(double msg);
void AddDouble(double msg);
void AddString(const FString& msg); void AddString(const FString& msg);
void OnDestroy() override; void OnDestroy() override;
void Serialize(FSerializer& arc) override; void Serialize(FSerializer& arc) override;

View file

@ -4,6 +4,7 @@ enum ENetCmd
NET_INT16, NET_INT16,
NET_INT, NET_INT,
NET_FLOAT, NET_FLOAT,
NET_DOUBLE,
NET_STRING, NET_STRING,
} }
@ -16,6 +17,7 @@ struct NetworkCommand native play version("4.12")
native int ReadInt16(); native int ReadInt16();
native int ReadInt(); native int ReadInt();
native double ReadFloat(); native double ReadFloat();
native double ReadDouble();
native string ReadString(); native string ReadString();
// Wrappers // Wrappers
@ -27,7 +29,7 @@ struct NetworkCommand native play version("4.12")
native Vector4 ReadVector4(); native Vector4 ReadVector4();
native Quat ReadQuat(); native Quat ReadQuat();
native void ReadIntArray(out Array<int> values, ENetCmd intSize = NET_INT); native void ReadIntArray(out Array<int> values, ENetCmd intSize = NET_INT);
native void ReadFloatArray(out Array<double> values); native void ReadDoubleArray(out Array<double> values, bool doublePrecision = true);
native void ReadStringArray(out Array<string> values, bool skipEmpty = false); native void ReadStringArray(out Array<string> values, bool skipEmpty = false);
} }
@ -37,6 +39,7 @@ class NetworkBuffer native version("4.12")
native void AddInt16(int value); native void AddInt16(int value);
native void AddInt(int value); native void AddInt(int value);
native void AddFloat(double value); native void AddFloat(double value);
native void AddDouble(double value);
native void AddString(string value); native void AddString(string value);
// Wrappers // Wrappers
@ -48,7 +51,7 @@ class NetworkBuffer native version("4.12")
native void AddVector4(Vector4 value); native void AddVector4(Vector4 value);
native void AddQuat(Quat value); native void AddQuat(Quat value);
native void AddIntArray(Array<int> values, ENetCmd intSize = NET_INT); native void AddIntArray(Array<int> values, ENetCmd intSize = NET_INT);
native void AddFloatArray(Array<double> values); native void AddDoubleArray(Array<double> values, bool doublePrecision = true);
native void AddStringArray(Array<string> values); native void AddStringArray(Array<string> values);
} }