diff --git a/src/ReplayReader.cpp b/src/ReplayReader.cpp index 7a0200861..17f8a2a15 100644 --- a/src/ReplayReader.cpp +++ b/src/ReplayReader.cpp @@ -22,6 +22,7 @@ ReplayReader::ReplayReader() numOrders = 0; stepsUntilNextOrder = -1; wideStepCounter = false; + versionMinor = VERSION_MINOR; checksum = 0; } @@ -89,6 +90,8 @@ bool ReplayReader::loadReplay(GAGCore::InputStream *inputStream, bool skipToOrde // Replays written before version 87 store step counters as Uint16 wideStepCounter = (version_minor >= REPLAY_UINT32_STEP_COUNTER_VERSION_MINOR); + versionMinor = version_minor; + // If there are no orders, this is also not a valid replay (there should be at least a NullOrder) if (stream->isEndOfStream()) { @@ -112,6 +115,7 @@ bool ReplayReader::loadReplay(GAGCore::InputStream *inputStream, bool skipToOrde { // Read an order from the stream NetSendOrder msg; + msg.setDecodeVersionMinor(versionMinor); msg.decodeData(stream); order = msg.getOrder(); @@ -213,6 +217,7 @@ std::shared_ptr ReplayReader::retrieveOrder() { // Read the order from the stream NetSendOrder msg; + msg.setDecodeVersionMinor(versionMinor); msg.decodeData(stream); order = msg.getOrder(); diff --git a/src/ReplayReader.h b/src/ReplayReader.h index 8ac4d41a4..57c83a30c 100644 --- a/src/ReplayReader.h +++ b/src/ReplayReader.h @@ -114,6 +114,9 @@ class ReplayReader /// (format version >= REPLAY_UINT32_STEP_COUNTER_VERSION_MINOR) bool wideStepCounter; + /// Format version from the replay header, used to decode orders. + Uint32 versionMinor; + /// The game's current checksum (or 0 if it's not given) Uint32 checksum; }; diff --git a/src/net/message/OrderMessages.cpp b/src/net/message/OrderMessages.cpp index d3fc34487..08a200a17 100644 --- a/src/net/message/OrderMessages.cpp +++ b/src/net/message/OrderMessages.cpp @@ -11,11 +11,18 @@ using namespace GAGCore; NetSendOrder::NetSendOrder() { + decodeVersionMinor=VERSION_MINOR; } NetSendOrder::NetSendOrder(std::shared_ptr newOrder) { order=newOrder; + decodeVersionMinor=VERSION_MINOR; +} + +void NetSendOrder::setDecodeVersionMinor(Uint32 newVersionMinor) +{ + decodeVersionMinor=newVersionMinor; } void NetSendOrder::changeOrder(std::shared_ptr newOrder) @@ -66,7 +73,7 @@ void NetSendOrder::decodeData(GAGCore::InputStream* stream) stream->read(buffer.data(), size, "data"); stream->readLeaveSection(); - order = Order::getOrder(buffer.data(), size, VERSION_MINOR); + order = Order::getOrder(buffer.data(), size, decodeVersionMinor); // If this couldn't be interpreted return it returned a NULL order, so we throw. if (order == std::shared_ptr()) diff --git a/src/net/message/OrderMessages.h b/src/net/message/OrderMessages.h index 9d2a8d251..752b4d288 100644 --- a/src/net/message/OrderMessages.h +++ b/src/net/message/OrderMessages.h @@ -37,6 +37,11 @@ class NetSendOrder : public NetMessage void changeOrder(std::shared_ptr newOrder); + /// Sets the version used by subsequent decodeData() calls. + /// Defaults to VERSION_MINOR; replay readers supply their header version. + /// Does not affect encoding. + void setDecodeVersionMinor(Uint32 newVersionMinor); + Uint8 getMessageType() const; void encodeData(GAGCore::OutputStream* stream) const; /// Wire format: Uint32 size | size bytes payload | Uint8 sender | Uint32 checksum. @@ -49,6 +54,8 @@ class NetSendOrder : public NetMessage bool operator==(const NetMessage& rhs) const; private: std::shared_ptr order; + + Uint32 decodeVersionMinor; }; /// Latency probe sent periodically to measure round-trip time. diff --git a/test/ReplayStepCounterTest.cpp b/test/ReplayStepCounterTest.cpp index e5682d4ab..889408ab1 100644 --- a/test/ReplayStepCounterTest.cpp +++ b/test/ReplayStepCounterTest.cpp @@ -81,8 +81,13 @@ class StepTestOrder : public MiscOrder Uint8 getOrderType(void) { return ORDER_DELETE; } }; -std::shared_ptr Order::getOrder(const Uint8 *netData, int netDataLength, Uint32 /*versionMinor*/) +// Record the requested version because supported replay versions currently +// decode identically. +Uint32 lastDecodeVersionMinor = 0; + +std::shared_ptr Order::getOrder(const Uint8 *netData, int netDataLength, Uint32 versionMinor) { + lastDecodeVersionMinor = versionMinor; if (netDataLength < 1 || netData == NULL) return std::shared_ptr(); if (netData[0] == ORDER_NULL) @@ -257,6 +262,37 @@ void testVersionBounds() } } +// 4. Both the initial scan and playback use the replay header version. +void testDecodeVersionPlumbing() +{ + const Uint16 oldVersion = REPLAY_MINIMUM_VERSION_MINOR; + + // Require an older version so a hardcoded VERSION_MINOR cannot pass. + check(oldVersion != VERSION_MINOR, + "decodeVersion: replay floor is below the current build version"); + + ReplayReader reader; + const bool wideCounters = oldVersion >= REPLAY_UINT32_STEP_COUNTER_VERSION_MINOR; + lastDecodeVersionMinor = 0; + bool loaded = reader.loadReplay(writeReplayBody(oldVersion, wideCounters, 7), false); + check(loaded, "decodeVersion: old-but-supported replay loads"); + if (!loaded) + return; + + check(lastDecodeVersionMinor == oldVersion, + "decodeVersion: initial scan uses the replay header version"); + + // Reset the recorder to check playback independently of the initial scan. + lastDecodeVersionMinor = 0; + + for (Uint32 i = 0; i < 7; i++) + reader.advanceStep(); + std::shared_ptr order = reader.retrieveOrder(); + check(order && order->getOrderType() == ORDER_DELETE, "decodeVersion: order read back"); + check(lastDecodeVersionMinor == oldVersion, + "decodeVersion: order decoded against the replay's version, not VERSION_MINOR"); +} + } // namespace int main(int /*argc*/, char* /*argv*/[]) @@ -264,6 +300,7 @@ int main(int /*argc*/, char* /*argv*/[]) testWideRoundTrip(); testOldFormatUint16(); testVersionBounds(); + testDecodeVersionPlumbing(); std::printf(failures == 0 ? "ALL PASS\n" : "FAILURES: %d\n", failures); return failures == 0 ? 0 : 1; }