From 314edd70998a549b15b74c38ef3d2cd999644698 Mon Sep 17 00:00:00 2001 From: Tuomo Kriikkula Date: Wed, 13 Dec 2023 18:21:34 +0200 Subject: [PATCH] Float encoding and networking fixes and debugging --- CMakeLists.txt | 5 + include/umb/coding.hpp | 4 +- templates/uscript_encode_dynamic_float.jinja | 4 + templates/uscript_test_mutator.jinja | 111 +++++-- tests/UDKTests/UMBTestsMacros.uci | 3 + tests/UDKTests/UMBTestsTcpLink.uc | 318 +++++++++++++++---- tests/test_coding.cpp | 64 ++++ tests/umb_echo_server.cpp | 52 +-- 8 files changed, 459 insertions(+), 102 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fb30b5..0990e2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,11 @@ set(Boost_USE_DEBUG_LIBS $<$:ON,OFF>) set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_LIBS ON) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + message(STATUS "adding compile definition: BOOST_ASIO_ENABLE_BUFFER_DEBUGGING") + add_compile_definitions("BOOST_ASIO_ENABLE_BUFFER_DEBUGGING") +endif () + find_package( inja CONFIG diff --git a/include/umb/coding.hpp b/include/umb/coding.hpp index 37f9865..2e56569 100644 --- a/include/umb/coding.hpp +++ b/include/umb/coding.hpp @@ -281,7 +281,9 @@ decode_float( else { throw std::runtime_error( - std::format("TODO: better handling: {}", std::make_error_condition(ec).message())); + std::format("TODO: decode_float: better handling: {}: '{}'", + std::make_error_condition(ec).message(), + float_str)); } } diff --git a/templates/uscript_encode_dynamic_float.jinja b/templates/uscript_encode_dynamic_float.jinja index 456f44d..fae5d39 100644 --- a/templates/uscript_encode_dynamic_float.jinja +++ b/templates/uscript_encode_dynamic_float.jinja @@ -17,6 +17,10 @@ FloatStr $= (int(Msg.{{ field.name }}) * 10000000); } StrLen = Len(FloatStr); + + // TODO: debugging. + `log("{{ message.name }}.{{ field.name }} StrLen:" @ StrLen @ "FloatStr:" @ FloatStr); + Bytes.Length = Bytes.Length + StrLen + 1; Bytes[I++] = StrLen; for (StrIdx = 0; StrIdx < StrLen; ++StrIdx) diff --git a/templates/uscript_test_mutator.jinja b/templates/uscript_test_mutator.jinja index c9b8725..b44c969 100644 --- a/templates/uscript_test_mutator.jinja +++ b/templates/uscript_test_mutator.jinja @@ -23,6 +23,9 @@ class {{ uscript_test_mutator }} extends Mutator const FLOAT_MAX = 34028237000000000000000000000.f; const FLOAT_MIN = 0.0000000000000000000000000001175494; +const FLOAT_MAX_SMALL = 50000.0; +const FLOAT_MIN_SMALL = 0.00005; + const PACKET_SIZE = {{ packet_size }}; var UMBTestsTcpLink Link; @@ -47,6 +50,7 @@ var int TotalFailures; var int TotalMessagesToSend; var int TotalMessagesReceived; var int NumTestRounds; +var int LastLinkNumMsgs; var bool bDone; var bool bLocalDone; var array BytesToSend; @@ -55,7 +59,10 @@ event Tick(float DeltaTime) { super.Tick(DeltaTime); - `ulog("TestState:" @ TestState, TestState != ETS_None); + `ulog("TestState:" @ TestState + @ "LastLinkNumMsgs:" @ LastLinkNumMsgs + @ "Link.NumMessagesReceived:" @ Link.NumMessagesReceived, + TestState != ETS_None); switch (TestState) { @@ -69,6 +76,7 @@ event Tick(float DeltaTime) if (MessageReady_{{ message.name }}() || bClientServerDone_{{ message.name }}) { TotalFailures += CheckMessage_{{ message.name }}(); + LastLinkNumMsgs = Link.NumMessagesReceived; } break; {% endfor %} @@ -104,7 +112,28 @@ final static function string BytesToString(const out array Bytes) Str $= ","; } } - Str = "]"; + Str $= "]"; + + return Str; +} + +final static function string StaticBytesToString( + const out byte Bytes[PACKET_SIZE], + int Count) +{ + local string Str; + local int I; + + Str = "["; + for (I = 0; I < Count; ++I) + { + Str $= string(int(Bytes[I])); + if (I < (Count - 1)) + { + Str $= ","; + } + } + Str $= "]"; return Str; } @@ -137,12 +166,38 @@ final static function string RandomString(int Size) return Str; } +final static function float RandomFloat() +{ + local float MaxFloat; + local float MinFloat; + local float Ret; + + if (FRand() > 0.5) + { + MaxFloat = FLOAT_MAX; + MinFloat = FLOAT_MIN; + } + else + { + MaxFloat = FLOAT_MAX_SMALL; + MinFloat = FLOAT_MIN_SMALL; + } + + Ret = RandRange(MinFloat, MaxFloat); + if (FRand() < 0.5) + { + Ret *= -1.0; + } + + return Ret; +} + `define CHECK(Field, Value, Type, Msg1, Msg2, Failures, Bytes) \n\ `Msg1.`Field = `Value; \n\ Bytes.Length = 0; \n\ - class'TestMessages'.static.`{Type}_ToMultiBytes(`Msg1, `Bytes); \n\ - class'TestMessages'.static.`{Type}_FromMultiBytes(`Msg2, `Bytes); \n\ - if (class'TestMessages'.static.`{Type}_NEQ(`Msg1, `Msg2)) \n\ + class'{{ class_name }}'.static.`{Type}_ToMultiBytes(`Msg1, `Bytes); \n\ + class'{{ class_name }}'.static.`{Type}_FromMultiBytes(`Msg2, `Bytes); \n\ + if (class'{{ class_name }}'.static.`{Type}_NEQ(`Msg1, `Msg2)) \n\ { \n\ `ulog("##CHECK FAILED##:" @ `Msg1.`Field @ "???" @ `Msg2.`Field); \n\ `ulog("~=" @ `Msg1.`Field ~= `Msg2.`Field); \n\ @@ -162,8 +217,8 @@ final function int Test_StaticTests() Failures = 0; // Check empty message. - class'TestMessages'.static.JustAnotherTestMessage_ToMultiBytes(Msg1, Bytes); - class'TestMessages'.static.JustAnotherTestMessage_FromMultiBytes(Msg2, Bytes); + class'{{ class_name }}'.static.JustAnotherTestMessage_ToMultiBytes(Msg1, Bytes); + class'{{ class_name }}'.static.JustAnotherTestMessage_FromMultiBytes(Msg2, Bytes); if (Msg1 != Msg2) { `ulog("##CHECK FAILED##: Empty JustAnotherTestMessage"); @@ -182,6 +237,8 @@ final function int Test_StaticTests() `CHECK(Some_floatVAR, -1.00000118, JustAnotherTestMessage, Msg1, Msg2, Failures, Bytes) `CHECK(Some_floatVAR, 0.00000118, JustAnotherTestMessage, Msg1, Msg2, Failures, Bytes) `CHECK(Some_floatVAR, 1.00000118, JustAnotherTestMessage, Msg1, Msg2, Failures, Bytes) + `CHECK(Some_floatVAR, 0.0005, JustAnotherTestMessage, Msg1, Msg2, Failures, Bytes) + `CHECK(Some_floatVAR, -0.0005, JustAnotherTestMessage, Msg1, Msg2, Failures, Bytes) return Failures; } @@ -211,7 +268,7 @@ final function int Test_{{ message.name }}() {{ msg1 }}.{{ field.name }} = Rand(MaxInt); if (FRand() > 0.5) {{ msg1 }}.{{ field.name }} *= -1; {% else if field.type == "float" %} - {{ msg1 }}.{{ field.name }} = RandRange(FLOAT_MIN, FLOAT_MAX); + {{ msg1 }}.{{ field.name }} = RandomFloat(); {% else if field.type == "byte" %} {{ msg1 }}.{{ field.name }} = byte(Rand(256)); {% else if field.type == "bool" %} @@ -281,7 +338,11 @@ final function int Test_{{ message.name }}() ++Failures; } {{ cls }}{{ message.name }}_FromMultiBytes({{ msg2 }}, DynamicBytes); + {% if message.has_float_fields %} + if (class'{{ class_name }}'.static.{{ message.name }}_NEQ({{ msg1 }}, {{ msg2 }})) + {% else %} if ({{ msg1 }} != {{ msg2 }}) + {% endif %} { `ulog("##CHECK FAILED##: DYNAMIC CODING: {{ msg1 }} != {{ msg2 }}"); {% for field in message.fields %} @@ -321,7 +382,6 @@ function PreBeginPlay() { local int Failures; local int Round; - local int I; `ulog(self @ "initialized"); @@ -398,6 +458,16 @@ final function SendMsg_{{ message.name }}() class'{{ class_name }}'.static.{{ message.name }}_ToMultiBytes(Msg, BytesToSend); MsgQueue_{{ message.name }}.Remove(MsgQueue_{{ message.name }}.Length - 1, 1); CmpMsg_{{ message.name }} = Msg; + + {% if message.name == "testmsg" %} + {% for field in message.fields %} + {% if field.type == "float" %} + // Debugging float decoding issue on C++ side. + `ulog("### FLOAT DEBUG ###: Msg.{{ field.name }}:" @ Msg.{{ field.name }}); + {% endif %} + {% endfor %} + {% endif %} + Link.SendBytes(BytesToSend); } else @@ -411,7 +481,8 @@ final function SendMsg_{{ message.name }}() {% set mt = "class'" + class_name + "'.const." + uscript_message_type_prefix + "_" + message.name %} final function bool MessageReady_{{ message.name }}() { - return Link.MessageType == {{ mt }}; + return (Link.In_MessageType == {{ mt }}); + // && (Link.NumMessagesReceived == LastLinkNumMsgs + 1); } /* @@ -448,9 +519,9 @@ final function int CheckMessage_{{ message.name }}() if (bClientServerDone_{{ message.name }}) { - Link.MessageType = class'{{ class_name }}'.const.EMT_None; - Link.Size = 0; - Link.Part = 0; + Link.In_MessageType = class'{{ class_name }}'.const.EMT_None; + Link.In_Size = 0; + Link.In_Part = 0; {% if loop.is_last %} // Done with all message types, exit. TODO: move exit away from here. @@ -471,23 +542,23 @@ final function int CheckMessage_{{ message.name }}() // TODO: read from link and compare. - if (Link.MessageType != {{ mt }}) + if (Link.In_MessageType != {{ mt }}) { - `ulog("##ERROR##: wrong MessageType, expected {{ mt }}, got" @ Link.MessageType); + `ulog("##ERROR##: wrong MessageType, expected {{ mt }}, got" @ Link.In_MessageType); ++Failures; } // TODO: do both, FromBytes and FromMultiBytes for static messages. // Only do FromMultiBytes for dynamic messages. {% if message.has_static_size %} - class'{{ class_name }}'.static.{{ message.name }}_FromBytes(Msg, Link.RecvMsgBufStatic); + class'{{ class_name }}'.static.{{ message.name }}_FromBytes(Msg, Link.RecvMsgBufSingle); {% endif %} // TODO: (also) read multi bytes. // TODO: MAKE MESSAGE COMPARISON INTO A MACRO AND/OR FUNCTION! // TODO: CHECK EARLIER COMPARISON IMPL. ABOVE! // TODO: CURRENTLY ONLY CHECKING STATIC MESSAGES!!! - if (Msg != CmpMsg_{{ message.name }} && Link.bIsStatic) + if (Msg != CmpMsg_{{ message.name }} && Link.In_bIsStatic) { `ulog("##ERROR##: FAILED CMP, TODO: BETTER MESSAGE"); {% if message.has_static_size %} @@ -511,9 +582,9 @@ final function int CheckMessage_{{ message.name }}() ++Failures; } - Link.MessageType = class'{{ class_name }}'.const.EMT_None; - Link.Size = 0; - Link.Part = 0; + Link.In_MessageType = class'{{ class_name }}'.const.EMT_None; + Link.In_Size = 0; + Link.In_Part = 0; ++TotalMessagesReceived; diff --git a/tests/UDKTests/UMBTestsMacros.uci b/tests/UDKTests/UMBTestsMacros.uci index cce4bfa..e4c278d 100644 --- a/tests/UDKTests/UMBTestsMacros.uci +++ b/tests/UDKTests/UMBTestsMacros.uci @@ -2,3 +2,6 @@ `log(self.class.name $ "::" $ GetFuncName() $ "(): " $ `msg, \ `cond, \ `if(`tag) name("UMBTests-" $ string(`tag)) `else 'UMBTests' `endif) + +`define uerror(msg, cond) \ + `log(self.class.name $ "::" $ GetFuncName() $ "(): ##ERROR##:" @ `msg, `cond, 'ERROR') diff --git a/tests/UDKTests/UMBTestsTcpLink.uc b/tests/UDKTests/UMBTestsTcpLink.uc index 4addc5f..e0edc33 100644 --- a/tests/UDKTests/UMBTestsTcpLink.uc +++ b/tests/UDKTests/UMBTestsTcpLink.uc @@ -20,6 +20,7 @@ class UMBTestsTcpLink extends TcpLink; const PACKET_SIZE = 255; const HEADER_SIZE = 4; +const SINGLE_PART = 255; const MULTI_PART_END = 254; var private int ClientPort; @@ -38,15 +39,40 @@ var private int Idx; var private int BufIdx; var private int NumSent; var private int NumToSend; +var private int NumToTakeFromBuffer; var private byte OutBuf[PACKET_SIZE]; // Received message. -var private int ReadCount; -var byte Size; -var byte Part; -var int MessageType; -var bool bIsStatic; -var byte RecvMsgBufStatic[PACKET_SIZE]; +var private int In_RecvBufIdx; +var private int In_MsgBufIdx; +var private int In_ReadCount; +var byte In_NumToRead; +var byte In_Size; +var byte In_Part; +var int In_MessageType; +var bool In_bIsStatic; + +var int NumMessagesReceived; + +// TODO: Should have event based system for indicating when a message of +// certain type has been received. Currently relying on the user +// manually checking the link's internal variable states. + +enum EReadState +{ + ERS_WaitingForHeader, // Default state. + ERS_WaitingForMultiHeader, // Received multipart payload earlier, waiting for next header. + ERS_WaitingForSingleBytes, // Header parsed, expecting single-part payload. + ERS_WaitingForMultiBytes, // Header parsed, expecting multipart payload. +}; +var EReadState ReadState; + +// Reusable buffer for receiving bytes. +var byte RecvMsgBuf[PACKET_SIZE]; + +// Final message bytes that can be fed to UMB +// X_FromBytes and X_FromMultiBytes functions. +var byte RecvMsgBufSingle[PACKET_SIZE]; var array RecvMsgBufMulti; // TODO: Should messages be classes instead of structs? @@ -71,20 +97,210 @@ event Tick(float DeltaTime) } // TODO: limit the time we can spend here in a single tick? - do + // E.g., max iteration count? + while (True) { - `ulog("DataPending:" @ DataPending); + `ulog("ReadState :" @ ReadState); + + switch (ReadState) + { + case ERS_WaitingForHeader: + + In_ReadCount = ReadBinary(HEADER_SIZE, RecvMsgBuf); + `ulog("In_ReadCount :" @ In_ReadCount); + + // TCP buffer empty, try again next tick. + if (In_ReadCount == 0) + { + return; + } + + if (In_ReadCount < HEADER_SIZE) + { + `uerror("Expected" @ HEADER_SIZE @ "header bytes, got" @ In_ReadCount); + break; + } + + In_Size = RecvMsgBuf[0]; + In_Part = RecvMsgBuf[1]; + In_MessageType = RecvMsgBuf[2] | (RecvMsgBuf[3] << 8); + + `ulog("In_Size :" @ In_Size); + `ulog("In_Part :" @ In_Part); + `ulog("In_MessageType :" @ In_MessageType); + + if (In_Part == SINGLE_PART) + { + RecvMsgBufSingle[0] = RecvMsgBuf[0]; + RecvMsgBufSingle[1] = RecvMsgBuf[1]; + RecvMsgBufSingle[2] = RecvMsgBuf[2]; + RecvMsgBufSingle[3] = RecvMsgBuf[3]; + ReadState = ERS_WaitingForSingleBytes; + } + else if (In_Part < MULTI_PART_END) + { + // Pre-allocate at least In_Size bytes. Final size not known yet. + RecvMsgBufMulti.Length = In_Size; + RecvMsgBufMulti[0] = RecvMsgBuf[0]; + RecvMsgBufMulti[1] = RecvMsgBuf[1]; + RecvMsgBufMulti[2] = RecvMsgBuf[2]; + RecvMsgBufMulti[3] = RecvMsgBuf[3]; + In_MsgBufIdx = HEADER_SIZE; + ReadState = ERS_WaitingForMultiBytes; + } + else + { + `uerror("unexpected part:" @ In_Part @ "in state:" @ ReadState); + return; + } + + break; + + case ERS_WaitingForMultiHeader: + + In_ReadCount = ReadBinary(HEADER_SIZE, RecvMsgBuf); + `ulog("In_ReadCount :" @ In_ReadCount); + + // TCP buffer empty, try again next tick. + if (In_ReadCount == 0) + { + return; + } + + if (In_ReadCount < HEADER_SIZE) + { + `uerror("Expected" @ HEADER_SIZE @ "header bytes, got" @ In_ReadCount); + break; + } + + In_Size = RecvMsgBuf[0]; + In_Part = RecvMsgBuf[1]; + In_MessageType = RecvMsgBuf[2] | (RecvMsgBuf[3] << 8); + + `ulog("In_Size :" @ In_Size); + `ulog("In_Part :" @ In_Part); + `ulog("In_MessageType :" @ In_MessageType); + + if (In_Part <= MULTI_PART_END) + { + RecvMsgBufMulti.Length = RecvMsgBufMulti.Length + (In_Size - HEADER_SIZE); + ReadState = ERS_WaitingForMultiBytes; + } + else + { + `uerror("unexpected part:" @ In_Part @ "in state:" @ ReadState); + ReadState = ERS_WaitingForHeader; + } + + break; + + case ERS_WaitingForSingleBytes: + + // TODO: what if there's not enough bytes read yet? + // Is that possible? Seems unlikely but need to + // check at some point whether it can actually happen. + In_NumToRead = In_Size - HEADER_SIZE; + In_ReadCount = ReadBinary(In_NumToRead, RecvMsgBuf); + `ulog("In_ReadCount :" @ In_ReadCount); + + // TCP buffer empty, try again next tick. + if (In_ReadCount == 0) + { + return; + } + + if (In_ReadCount != In_NumToRead) + { + `uerror("Expected" @ In_NumToRead @ "bytes, got" @ In_ReadCount); + ReadState = ERS_WaitingForHeader; + break; + } - // Header. - ReadCount = ReadBinary(HEADER_SIZE, RecvMsgBufStatic); + // TODO: this check is unnecessary. Does the test mutator code using + // this actually need it for anything? + In_bIsStatic = class'TestMessages'.static.IsStaticMessage(In_MessageType); + // Header has been read already. Only read payload bytes here. + In_MsgBufIdx = HEADER_SIZE; + if (In_bIsStatic) + { + for (In_RecvBufIdx = 0; In_RecvBufIdx < In_ReadCount; ++In_RecvBufIdx) + { + RecvMsgBufSingle[In_MsgBufIdx++] = RecvMsgBuf[In_RecvBufIdx]; + } + } + else + { + for (In_RecvBufIdx = 0; In_RecvBufIdx < In_ReadCount; ++In_RecvBufIdx) + { + RecvMsgBufMulti[In_MsgBufIdx++] = RecvMsgBuf[In_RecvBufIdx]; + } + } + + ++NumMessagesReceived; + ReadState = ERS_WaitingForHeader; + + break; + + case ERS_WaitingForMultiBytes: + + In_NumToRead = In_Size - HEADER_SIZE; + In_ReadCount = ReadBinary(In_NumToRead, RecvMsgBuf); + `ulog("In_ReadCount :" @ In_ReadCount); + + // TCP buffer empty, try again next tick. + if (In_ReadCount == 0) + { + return; + } + + if (In_ReadCount != In_NumToRead) + { + `uerror("Expected" @ In_NumToRead @ "bytes, got" @ In_ReadCount); + ReadState = ERS_WaitingForHeader; + break; + } + + for (In_RecvBufIdx = 0; In_RecvBufIdx < In_ReadCount; ++In_RecvBufIdx) + { + RecvMsgBufMulti[In_MsgBufIdx++] = RecvMsgBuf[In_RecvBufIdx]; + } + + if (In_Part < MULTI_PART_END) + { + ReadState = ERS_WaitingForMultiHeader; + } + else if (In_Part == MULTI_PART_END) + { + ++NumMessagesReceived; + ReadState = ERS_WaitingForHeader; + } + else + { + `uerror("unexpected part:" @ In_Part @ "in state:" @ ReadState); + ReadState = ERS_WaitingForHeader; + } + + break; + default: + `uerror("invalid ReadState:" @ ReadState); + return; + } } - until (ReadCount == 0); - // Read size byte. - // Read desired byte count. - // Parse message. If multipart, go into state to wait for more bytes. - // If single part, just wait for next size byte. + // If multi-msg flag not set: + // 1. read header bytes + // 2. parse header + // 2.1 if single/static msg + // 2.1.1 read message bytes + // 2.2 else if multipart msg + // 2.2.2 set multi-msg flag + // 2.2.3 read message bytes + // Else if multi-msg flag set: + // 1. read header bytes // TODO: can we get non-header bytes here? + // 2. parse header + // 3. read message bytes + // 4. if no more expected (part end marker), set multi-msg flag off } final function ConnectToServer() @@ -116,8 +332,7 @@ final function SendBytes(const out array Bytes) if (NumPartsOut == 0) { - `ulog("ERROR: invalid NumPartsOut:" @ NumPartsOut); - `log("invalid NumPartsOut:" @ NumPartsOut,, 'ERROR'); + `uerror("invalid NumPartsOut:" @ NumPartsOut); return; } @@ -131,7 +346,7 @@ final function SendBytes(const out array Bytes) OutBuf[Idx] = Bytes[Idx]; } - OutBuf[1] = 255; // TODO: maybe document this a bit. + OutBuf[1] = SINGLE_PART; NumToSend = Bytes.Length; NumSent = SendBinary(NumToSend, OutBuf); @@ -142,11 +357,13 @@ final function SendBytes(const out array Bytes) // Multipart messages. // Skip first header. Idx = 4; - BytesSent = 4; + // Cache MessageType, should be re-used as such in + // all multipart message packets. MTOut0 = Bytes[2]; MTOut1 = Bytes[3]; + BytesSent = 0; for (PartOut = 0; PartOut < NumPartsOut; ++PartOut) { if (PartOut == (NumPartsOut - 1)) @@ -156,20 +373,27 @@ final function SendBytes(const out array Bytes) BufIdx = 0; NumToSend = Min(PACKET_SIZE, TotalBytesToSend - BytesSent); + NumToTakeFromBuffer = NumToSend - HEADER_SIZE; // TODO: this goes negative sometimes. OutBuf[BufIdx++] = NumToSend; OutBuf[BufIdx++] = PartOut; OutBuf[BufIdx++] = MTOut0; OutBuf[BufIdx++] = MTOut1; - // TODO: does this error with the first header? - while (BufIdx < NumToSend) + while (BufIdx < (NumToTakeFromBuffer + HEADER_SIZE)) { OutBuf[BufIdx++] = Bytes[Idx++]; } NumSent = SendBinary(NumToSend, OutBuf); - BytesSent += NumToSend; - `ulog("Sent" @ NumSent @ "bytes," @ "part:" @ PartOut @ "NumToSend:" @ NumToSend @ "BytesSent:" @ BytesSent); + BytesSent += NumSent; + `ulog( + "Sent" @ NumSent @ "bytes," + @ "part:" @ PartOut + @ "NumToSend:" @ NumToSend + @ "NumToTakeFromBuffer:" @ NumToTakeFromBuffer + @ "BytesSent:" @ BytesSent); + + `ulog("Buffer:" @ class'UMBTestsMutator'.static.StaticBytesToString(OutBuf, NumToSend)); } } @@ -205,50 +429,20 @@ event Closed() `ulog("closed"); } -// TODO: this gives garbage if packet size >= 255. -event ReceivedBinary(int Count, byte B[PACKET_SIZE]) -{ - local int I; - local int J; - - `ulog("Count :" @ Count); - - Size = B[0]; - Part = B[1]; - MessageType = B[2] | (B[3] << 8); - - `ulog("Size :" @ Size); - `ulog("Part :" @ Part); - `ulog("MessageType :" @ MessageType); - - bIsStatic = class'TestMessages'.static.IsStaticMessage(MessageType); - if (bIsStatic) - { - // The easy case. - if (Size <= PACKET_SIZE) - { - for (J = 0; J < Size; ++J) - { - RecvMsgBufStatic[J] = B[J]; - } - } - // TODO: this check makes no fucking sense. - else - { - `ulog("##ERROR##: message too large to be static!"); - `log("##ERROR##: message too large to be static!",, 'ERROR'); - } - - return; - } - - I = 4; -} +// WARNING: this gives garbage if packet size == 255. OR DOES IT? +// TODO: C++ server side had some bugs, should try out how this +// works now that they are fixed. +// event ReceivedBinary(int Count, byte B[PACKET_SIZE]) +// { +// } DefaultProperties { TargetHost="127.0.0.1" TargetPort=55555 + NumMessagesReceived=0 + ReadState=ERS_WaitingForHeader + TickGroup=TG_DuringAsyncWork } diff --git a/tests/test_coding.cpp b/tests/test_coding.cpp index b9b9af0..504becb 100644 --- a/tests/test_coding.cpp +++ b/tests/test_coding.cpp @@ -421,3 +421,67 @@ TEST_CASE("encode decode float inf/nan fields") // return true if both messages have the field as nan! CHECK_EQ(jatm1, jatm2); } + +TEST_CASE("shared pointer testmsg") +{ + std::vector msg_buf; + // Empty buffer. + auto msg = std::make_shared(); + bool ok = msg->from_bytes(msg_buf); + // Should fail. + CHECK_FALSE(ok); + + msg_buf = {33, 54, 52, 56, 53, 51, 55, 54, 52, 49, 54, 51, 53, 54, 53, 50, 53, 50, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 46, 48, 48, 48, 48, 34, 49, 49, 53, 49, 48, 54, 51, + 52, 56, 57, 53, 49, 52, 49, 54, 49, 57, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 46, 48, 48, 48, 48, 34, 50, 56, 53, 49, 54, 57, 54, 51, 57, 50, 52, 56, 51, 55, + 56, 57, 50, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 46, 48, 48, 48, 48, 34, + 49, 54, 50, 56, 53, 54, 49, 54, 56, 48, 52, 55, 55, 49, 55, 55, 55, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 46, 48, 48, 48, 48, 34, 50, 52, 57, 51, 50, 48, 57, + 50, 55, 52, 54, 49, 54, 50, 57, 57, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 46, 48, 48, 48, 48, 33, 53, 51, 53, 54, 53, 51, 54, 54, 49, 57, 54, 50, 57, 48, + 55, 50, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 46, 48, 48, 48, 48, 34, 50, + 51, 49, 49, 48, 53, 56, 49, 53, 52, 54, 51, 57, 48, 53, 49, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 46, 48, 48, 48, 48, 33, 53, 50, 56, 0, 0, 0, 0, 54, 57, + 53, 55, 56, 54, 50, 49, 54, 54, 50, 56, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 46, 48, 48, 48, 48, 34, 50, 55, 55, 50, 55, 55, 49, 50, 52, 53, 51, 51, 55, + 50, 54, 52, 55, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 46, 48, 48, 48, 48, + 34, 49, 52, 55, 57, 51, 51, 48, 53, 51, 49, 52, 50, 57, 53, 48, 48, 51, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 46, 48, 48, 48, 48, 34, 50, 51, 53, 51, 56, 52, + 51, 55, 51, 57, 52, 52, 55, 49, 52, 54, 54, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 46, 48, 48, 48, 48, 34, 49, 49, 52, 50, 56, 53, 57, 52, 52, 48, 50, 56, 50, + 54, 51, 52, 54, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 46, 48, 48, 48, 48, + 92, 0, 0, 32, 37, 190, 234, 124, 58, 11, 142, 39, 170, 140, 112, 127, 184, 207, 104, + 56, 162, 247, 126, 60, 48, 27, 162, 88, 154, 239, 36, 41, 18, 60, 2, 20, 224, 235, + 152, 15, 90, 195, 188, 104, 126, 94, 226, 211, 144, 214, 86, 241, 124, 214, 4, 45, + 84, 164, 18, 167, 176, 88, 158, 186, 96, 195, 220, 228, 2, 247, 26, 95, 26, 196, 168, + 210, 52, 0, 0, 0, 046, 80, 70, 148, 188, 92, 225, 42, 18, 182, 128, 24, 23, 64, 127, + 178, 208, 250, 154, 44, 107, 222, 172, 208, 146, 60, 26, 28, 169, 110, 154, 126, 68, + 164, 68, 8, 96, 184, 245, 252, 74, 154, 30, 96, 102, 132, 16, 144, 177, 96, 4, 246, + 172, 130, 135, 218, 212, 70, 200, 132, 132, 166, 145, 140, 20, 72, 189, 172, 29, 32, + 160, 194, 147, 60, 150, 142, 139, 186, 118, 188, 143, 124, 129, 196, 16, 192, 100, + 50, 85, 4, 105, 240, 254, 110, 198, 20, 92, 38, 191, 194, 240, 210, 118, 210, 145, + 168, 191, 130, 166, 56, 90, 42, 90, 118, 141, 20, 77, 18, 169, 108, 78, 192, 20, 132, + 119, 92, 35, 66, 67, 166, 25, 14, 147, 132, 212, 250, 156, 102, 91, 160, 29, 220, + 143, 72, 122, 248, 51, 234, 102, 116, 205, 152, 189, 66, 156, 228, 35, 140, 15, 206, + 124, 182, 252, 208, 156, 116, 120, 144, 223, 128, 178, 200, 189, 254, 184, 12, 244, + 46, 177, 200, 209, 96, 255, 74, 130, 214, 40, 46, 48, 150, 14, 230, 101, 188, 152, + 202, 164, 90, 38, 98, 118, 128, 54, 94, 218, 122, 227, 230, 133, 192, 17, 176, 143, + 206, 192, 148, 126, 68, 220, 8, 223, 152, 96, 62, 38, 164, 190, 62, 96, 110, 70, 196, + 144, 112, 33, 68, 109, 166, 41, 202, 200, 218, 210, 34, 81, 56, 136, 0, 0, 0, 0, 4, + 183, 218, 112, 144, 169, 40, 162, 8, 208, 216, 127, 240, 194, 110, 133, 138, 144, + 220, 21, 218, 54, 96, 82, 38, 80, 36, 130, 36, 149, 154, 96, 176, 114, 92, 217, 108, + 95, 98, 108, 244, 127, 66, 206, 32, 31, 146, 116, 2, 42, 202, 190, 2, 222, 2, 113, + 240, 201, 200, 93, 52, 201, 137, 91, 225, 70, 244, 125, 28, 69, 41, 132, 111, 16, + 133, 30, 244, 105, 22, 156, 203, 60, 173, 89, 252, 165, 222, 87, 196, 161, 27, 145, + 197, 32, 144, 5, 245, 210, 229, 17, 195, 108, 146, 17, 66, 171, 237, 222, 223, 230, + 148, 38, 116, 116, 103, 42, 35, 167, 168, 238, 245, 175, 127, 95, 132, 35, 116, 115, + 179, 128, 27, 214, 210, 96, 136, 33, 20, 181, 194, 38, 196, 206, 212, 99, 26, 110, + 164, 18, 36, 193, 62, 235, 156, 106, 183, 31, 31, 138, 81, 141, 166, 235, 4, 194, + 174, 48, 26, 195, 244, 67, 7, 50, 39, 203, 24, 205, 142, 250, 55, 216, 231, 118, 82, + 114, 130, 214, 40, 46}; + msg = std::make_shared(); + ok = msg->from_bytes(msg_buf); + // Should fail. + CHECK(ok); +} diff --git a/tests/umb_echo_server.cpp b/tests/umb_echo_server.cpp index 7e1b164..8a46501 100644 --- a/tests/umb_echo_server.cpp +++ b/tests/umb_echo_server.cpp @@ -279,40 +279,41 @@ handle_multi_part_msg( } std::shared_ptr msg; + bool ok = false; switch (type) { case testmessages::umb::MessageType::GetSomeStuff: msg = std::make_shared(); - msg->from_bytes(msg_buf); + ok = msg->from_bytes(msg_buf); break; case testmessages::umb::MessageType::GetSomeStuffResp: msg = std::make_shared(); - msg->from_bytes(msg_buf); + ok = msg->from_bytes(msg_buf); break; case testmessages::umb::MessageType::JustAnotherTestMessage: msg = std::make_shared(); - msg->from_bytes(msg_buf); + ok = msg->from_bytes(msg_buf); break; case testmessages::umb::MessageType::testmsg: msg = std::make_shared(); - msg->from_bytes(msg_buf); + ok = msg->from_bytes(msg_buf); break; case testmessages::umb::MessageType::BoolPackingMessage: msg = std::make_shared(); - msg->from_bytes(msg_buf); + ok = msg->from_bytes(msg_buf); break; case testmessages::umb::MessageType::STATIC_BoolPackingMessage: msg = std::make_shared(); - msg->from_bytes(msg_buf); + ok = msg->from_bytes(msg_buf); break; case testmessages::umb::MessageType::MultiStringMessage: msg = std::make_shared(); - msg->from_bytes(msg_buf); + ok = msg->from_bytes(msg_buf); break; case testmessages::umb::MessageType::DualStringMessage: msg = std::make_shared(); - msg->from_bytes(msg_buf); + ok = msg->from_bytes(msg_buf); break; case testmessages::umb::MessageType::None: @@ -324,6 +325,13 @@ handle_multi_part_msg( // std::to_string(static_cast(type)))); } + if (!ok) + { + std::cout + << std::format("umb_echo_server ERROR: msg->from_bytes failed for MessageType {}\n", + static_cast(type)); + } + const auto bytes_out = msg->to_bytes(); const auto num_hdr_bytes = static_cast(std::ceil( @@ -338,8 +346,7 @@ handle_multi_part_msg( const auto mt1 = bytes_out[3]; std::array send_buf{}; - // Skip header from bytes_out. It will be written in the loop below. - unsigned bytes_sent = umb::g_header_size; + unsigned bytes_sent = 0; unsigned offset = umb::g_header_size; std::cout << std::format( @@ -355,25 +362,32 @@ handle_multi_part_msg( } const auto num_to_send = std::min(umb::g_packet_size, total_bytes_to_send - bytes_sent); - send_buf[0] = num_to_send; - send_buf[1] = part_out; - send_buf[2] = mt0; - send_buf[3] = mt1; - + const auto num_to_take_from_buffer = num_to_send - umb::g_header_size; + std::cout << std::format( + "sending {} bytes, offset: {}, part_out: {}, num_to_take_from_buffer: {}\n", + num_to_send, offset, part_out, num_to_take_from_buffer); + + // Header. + auto it_send_buf = send_buf.begin(); + *it_send_buf++ = num_to_send; + *it_send_buf++ = part_out; + *it_send_buf++ = mt0; + *it_send_buf++ = mt1; + + // Grab the bytes we need for this part from the buffer containing + // all message bytes for the multipart message. const std::span send_data{bytes_out.cbegin() + offset, bytes_out.cend()}; - std::copy_n(send_data.cbegin(), num_to_send - umb::g_header_size, send_buf.begin()); + std::copy_n(send_data.cbegin(), num_to_take_from_buffer, it_send_buf); // TODO: check ec. - std::cout << std::format("sending {} bytes, offset: {}, part_out: {}\n", - num_to_send, offset, part_out); co_await async_write( socket, boost::asio::buffer(send_buf, num_to_send), deferred); bytes_sent += num_to_send; - offset = bytes_sent - umb::g_header_size; + offset += num_to_send - umb::g_header_size; } }