diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a8cfb4a72..8ac35254b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,9 +50,9 @@ set(OBJECTS_VERSION "1.0.21") set(OBJECTS_URL "https://github.com/OpenRCT2/objects/releases/download/v${OBJECTS_VERSION}/objects.zip") set(OBJECTS_SHA1 "c38af45d51a6e440386180feacf76c64720b6ac5") -set(REPLAYS_VERSION "0.0.39") +set(REPLAYS_VERSION "0.0.42") set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v${REPLAYS_VERSION}/replays.zip") -set(REPLAYS_SHA1 "8AF797661D87394FBE1A059375D82632094290FB") +set(REPLAYS_SHA1 "C60FC1D6526DB8EDDF4AFBB0EE52A4A0D561646E") option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.") option(WITH_TESTS "Build tests") diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index 5e7d975780..eee378d2f2 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -60,6 +60,8 @@ 4C8BB68525533DB9005C8830 /* ZoomLevel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C8BB68425533DB9005C8830 /* ZoomLevel.cpp */; }; 4C91FD5F25AE476700CA5DA4 /* MusicObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C91FD5D25AE476700CA5DA4 /* MusicObject.cpp */; }; 4C91FD6225AE483700CA5DA4 /* RideAudio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C91FD6025AE483600CA5DA4 /* RideAudio.cpp */; }; + 4CA23D64263C91D800077AA1 /* ChecksumStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CA23D62263C91D700077AA1 /* ChecksumStream.cpp */; }; + 4CA23DB2263C920900077AA1 /* Entity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CA23DB1263C920900077AA1 /* Entity.cpp */; }; 4CA39E512513F8A00094066B /* RTL.ICU.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CA39E4E2513F8A00094066B /* RTL.ICU.cpp */; }; 4CA39E522513F8A00094066B /* RTL.FriBidi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CA39E502513F8A00094066B /* RTL.FriBidi.cpp */; }; 4CB1375621C2E9F80029FCDA /* SimulateCommands.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CB1375521C2E9F80029FCDA /* SimulateCommands.cpp */; }; @@ -1152,6 +1154,11 @@ 4C93F1B71F8E185600A9330D /* NewsItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NewsItem.h; sourceTree = ""; }; 4C93F1B81F8E185600A9330D /* Research.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Research.cpp; sourceTree = ""; }; 4C93F1B91F8E185600A9330D /* Research.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Research.h; sourceTree = ""; }; + 4CA23D62263C91D700077AA1 /* ChecksumStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ChecksumStream.cpp; sourceTree = ""; }; + 4CA23D63263C91D700077AA1 /* ChecksumStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChecksumStream.h; sourceTree = ""; }; + 4CA23DAF263C920900077AA1 /* Entity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Entity.h; sourceTree = ""; }; + 4CA23DB0263C920900077AA1 /* EntityList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EntityList.h; sourceTree = ""; }; + 4CA23DB1263C920900077AA1 /* Entity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Entity.cpp; sourceTree = ""; }; 4CA39E4E2513F8A00094066B /* RTL.ICU.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RTL.ICU.cpp; sourceTree = ""; }; 4CA39E4F2513F8A00094066B /* RTL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RTL.h; sourceTree = ""; }; 4CA39E502513F8A00094066B /* RTL.FriBidi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RTL.FriBidi.cpp; sourceTree = ""; }; @@ -2433,6 +2440,8 @@ F76C83781EC4E7CC00FA49E2 /* core */ = { isa = PBXGroup; children = ( + 4CA23D62263C91D700077AA1 /* ChecksumStream.cpp */, + 4CA23D63263C91D700077AA1 /* ChecksumStream.h */, 2A5354EA22099C7200A5440F /* CircularBuffer.h */, F76C83791EC4E7CC00FA49E2 /* Collections.hpp */, F76C837A1EC4E7CC00FA49E2 /* Console.cpp */, @@ -3066,6 +3075,9 @@ 4C7B54202007646A00A52E21 /* Climate.cpp */, 4C7B54212007646A00A52E21 /* Climate.h */, 4C7B54222007646A00A52E21 /* Duck.cpp */, + 4CA23DB1263C920900077AA1 /* Entity.cpp */, + 4CA23DAF263C920900077AA1 /* Entity.h */, + 4CA23DB0263C920900077AA1 /* EntityList.h */, 4C7B54232007646A00A52E21 /* Entrance.cpp */, 4C7B54242007646A00A52E21 /* Entrance.h */, 4C7B54252007646A00A52E21 /* Footpath.cpp */, @@ -3774,6 +3786,7 @@ 4C8BB67925533D4C005C8830 /* FileStream.cpp in Sources */, 933CBDBD20CB1BA900134678 /* ViewportInteraction.cpp in Sources */, C685E51B1F8907850090598F /* Guest.cpp in Sources */, + 4CA23DB2263C920900077AA1 /* Entity.cpp in Sources */, C64644F91F3FA4120026AC2D /* EditorInventionsList.cpp in Sources */, C68878C720289B710084B384 /* OpenGLShaderProgram.cpp in Sources */, 4CA39E512513F8A00094066B /* RTL.ICU.cpp in Sources */, @@ -3839,6 +3852,7 @@ 4C8BB67C25533D59005C8830 /* JobPool.cpp in Sources */, 01C6F0C222FD519E0057E2F7 /* TrackImporter.cpp in Sources */, 4C8BB68125533D65005C8830 /* StringBuilder.cpp in Sources */, + 4CA23D64263C91D800077AA1 /* ChecksumStream.cpp in Sources */, C666EE761F37ACB10061AA04 /* Options.cpp in Sources */, 4CB991CC25CEE54500C692B4 /* Shortcuts.cpp in Sources */, C666EE6E1F37ACB10061AA04 /* CustomCurrency.cpp in Sources */, diff --git a/openrct2.proj b/openrct2.proj index 11be434fc9..9b65ce4632 100644 --- a/openrct2.proj +++ b/openrct2.proj @@ -48,8 +48,8 @@ 304d13a126c15bf2c86ff13b81a2f2cc1856ac8d https://github.com/OpenRCT2/objects/releases/download/v1.0.21/objects.zip c38af45d51a6e440386180feacf76c64720b6ac5 - https://github.com/OpenRCT2/replays/releases/download/v0.0.39/replays.zip - 8AF797661D87394FBE1A059375D82632094290FB + https://github.com/OpenRCT2/replays/releases/download/v0.0.42/replays.zip + C60FC1D6526DB8EDDF4AFBB0EE52A4A0D561646E diff --git a/src/openrct2-ui/windows/Guest.cpp b/src/openrct2-ui/windows/Guest.cpp index 4345fcab8e..27eb71af23 100644 --- a/src/openrct2-ui/windows/Guest.cpp +++ b/src/openrct2-ui/windows/Guest.cpp @@ -1795,18 +1795,18 @@ void window_guest_thoughts_paint(rct_window* w, rct_drawpixelinfo* dpi) DrawTextBasic(dpi, screenCoords, STR_GUEST_RECENT_THOUGHTS_LABEL); screenCoords.y += 10; - for (rct_peep_thought* thought = peep->Thoughts; thought < &peep->Thoughts[PEEP_MAX_THOUGHTS]; ++thought) + for (const auto& thought : peep->Thoughts) { - if (thought->type == PeepThoughtType::None) + if (thought.type == PeepThoughtType::None) return; - if (thought->freshness == 0) + if (thought.freshness == 0) continue; int32_t width = window_guest_thoughts_widgets[WIDX_PAGE_BACKGROUND].right - window_guest_thoughts_widgets[WIDX_PAGE_BACKGROUND].left - 8; auto ft = Formatter(); - peep_thought_set_format_args(thought, ft); + peep_thought_set_format_args(&thought, ft); screenCoords.y += DrawTextWrapped(dpi, screenCoords, width, STR_BLACK_STRING, ft, { FontSpriteBase::SMALL }); // If this is the last visible line end drawing. diff --git a/src/openrct2/GameStateSnapshots.cpp b/src/openrct2/GameStateSnapshots.cpp index 69c065d405..5170dadf66 100644 --- a/src/openrct2/GameStateSnapshots.cpp +++ b/src/openrct2/GameStateSnapshots.cpp @@ -82,30 +82,31 @@ struct GameStateSnapshot_t switch (sprite.misc.Type) { case EntityType::Vehicle: - ds << reinterpret_cast(sprite.vehicle); + reinterpret_cast(sprite).Serialise(ds); break; case EntityType::Guest: - ds << reinterpret_cast(sprite.peep); + reinterpret_cast(sprite).Serialise(ds); break; case EntityType::Staff: - ds << reinterpret_cast(sprite.peep); + reinterpret_cast(sprite).Serialise(ds); break; case EntityType::Litter: - ds << reinterpret_cast(sprite.litter); + reinterpret_cast(sprite).Serialise(ds); break; case EntityType::MoneyEffect: - ds << reinterpret_cast(sprite.money_effect); + reinterpret_cast(sprite).Serialise(ds); break; case EntityType::Balloon: - ds << reinterpret_cast(sprite.balloon); + reinterpret_cast(sprite).Serialise(ds); break; case EntityType::Duck: - ds << reinterpret_cast(sprite.duck); + reinterpret_cast(sprite).Serialise(ds); break; case EntityType::JumpingFountain: - ds << reinterpret_cast(sprite.jumping_fountain); + reinterpret_cast(sprite).Serialise(ds); break; case EntityType::SteamParticle: + reinterpret_cast(sprite).Serialise(ds); ds << reinterpret_cast(sprite.steam_particle); break; case EntityType::Null: diff --git a/src/openrct2/core/ChecksumStream.cpp b/src/openrct2/core/ChecksumStream.cpp new file mode 100644 index 0000000000..b54f29bc90 --- /dev/null +++ b/src/openrct2/core/ChecksumStream.cpp @@ -0,0 +1,47 @@ +/***************************************************************************** + * Copyright (c) 2014-2021 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#include "ChecksumStream.h" + +#include "Endianness.h" + +#include + +namespace OpenRCT2 +{ +#ifndef DISABLE_NETWORK + ChecksumStream::ChecksumStream(std::array& buf) + : _checksum(buf) + { + uint64_t* hash = reinterpret_cast(_checksum.data()); + *hash = Seed; + } + + void ChecksumStream::Write(const void* buffer, uint64_t length) + { + uint64_t* hash = reinterpret_cast(_checksum.data()); + for (size_t i = 0; i < length; i += sizeof(uint64_t)) + { + const auto maxLen = std::min(sizeof(uint64_t), length - i); + + uint64_t temp{}; + std::memcpy(&temp, reinterpret_cast(buffer) + i, maxLen); + + // Always use value as little endian, most common systems are little. +# if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + temp = ByteSwapBE(temp); +# endif + + *hash ^= temp; + *hash *= Prime; + } + } + +#endif +} // namespace OpenRCT2 diff --git a/src/openrct2/core/ChecksumStream.h b/src/openrct2/core/ChecksumStream.h new file mode 100644 index 0000000000..fac658b914 --- /dev/null +++ b/src/openrct2/core/ChecksumStream.h @@ -0,0 +1,110 @@ +/***************************************************************************** + * Copyright (c) 2014-2021 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../common.h" +#include "IStream.hpp" + +namespace OpenRCT2 +{ + /** + * A stream for checksumming a stream of data + */ + class ChecksumStream final : public IStream + { + // FIXME: Move the checksum implementation out. + std::array& _checksum; + + static constexpr uint64_t Seed = 0xcbf29ce484222325ULL; + static constexpr uint64_t Prime = 0x00000100000001B3ULL; + + public: + ChecksumStream(std::array& buf); + + virtual ~ChecksumStream() = default; + + const void* GetData() const override + { + return _checksum.data(); + }; + + /////////////////////////////////////////////////////////////////////////// + // ISteam methods + /////////////////////////////////////////////////////////////////////////// + bool CanRead() const override + { + return false; + } + bool CanWrite() const override + { + return true; + } + + uint64_t GetLength() const override + { + return _checksum.size(); + } + + uint64_t GetPosition() const override + { + return 0; + } + + void SetPosition(uint64_t position) override + { + } + + void Seek(int64_t offset, int32_t origin) override + { + } + + void Read(void* buffer, uint64_t length) override + { + } + + void Write(const void* buffer, uint64_t length) override; + + void Write1(const void* buffer) override + { + Write<1>(buffer); + } + + void Write2(const void* buffer) override + { + Write<2>(buffer); + } + + void Write4(const void* buffer) override + { + Write<4>(buffer); + } + + void Write8(const void* buffer) override + { + Write<8>(buffer); + } + + void Write16(const void* buffer) override + { + Write<16>(buffer); + } + + template void Write(const void* buffer) + { + Write(buffer, N); + } + + uint64_t TryRead(void* buffer, uint64_t length) override + { + return 0; + } + }; + +} // namespace OpenRCT2 diff --git a/src/openrct2/core/DataSerialiserTraits.h b/src/openrct2/core/DataSerialiserTraits.h index 6128404106..f4c9266fc5 100644 --- a/src/openrct2/core/DataSerialiserTraits.h +++ b/src/openrct2/core/DataSerialiserTraits.h @@ -302,7 +302,7 @@ template struct DataSerializerTraitsPODArray uint16_t swapped = ByteSwapBE(len); stream->Write(&swapped); - DataSerializerTraits s; + DataSerializerTraits<_Ty> s; for (auto&& sub : val) { s.encode(stream, sub); @@ -584,6 +584,34 @@ template<> struct DataSerializerTraits_t stream->Write(msg, strlen(msg)); } }; +template<> struct DataSerializerTraits_t +{ + static void encode(OpenRCT2::IStream* stream, const rct12_xyzd8& coord) + { + stream->WriteValue(ByteSwapBE(coord.x)); + stream->WriteValue(ByteSwapBE(coord.y)); + stream->WriteValue(ByteSwapBE(coord.z)); + stream->WriteValue(ByteSwapBE(coord.direction)); + } + + static void decode(OpenRCT2::IStream* stream, rct12_xyzd8& coord) + { + auto x = ByteSwapBE(stream->ReadValue()); + auto y = ByteSwapBE(stream->ReadValue()); + auto z = ByteSwapBE(stream->ReadValue()); + auto d = ByteSwapBE(stream->ReadValue()); + coord = rct12_xyzd8{ x, y, z, d }; + } + + static void log(OpenRCT2::IStream* stream, const rct12_xyzd8& coord) + { + char msg[128] = {}; + snprintf( + msg, sizeof(msg), "rct12_xyzd8(x = %d, y = %d, z = %d, direction = %d)", coord.x, coord.y, coord.z, + coord.direction); + stream->Write(msg, strlen(msg)); + } +}; template<> struct DataSerializerTraits_t { @@ -793,3 +821,49 @@ template<> struct DataSerializerTraits_t stream->Write(msg, strlen(msg)); } }; + +template<> struct DataSerializerTraits_t +{ + static void encode(OpenRCT2::IStream* stream, const IntensityRange& val) + { + uint8_t temp = uint8_t(val); + stream->Write(&temp); + } + static void decode(OpenRCT2::IStream* stream, IntensityRange& val) + { + auto temp = stream->ReadValue(); + val = IntensityRange(temp); + } + static void log(OpenRCT2::IStream* stream, const IntensityRange& val) + { + char msg[128] = {}; + snprintf(msg, sizeof(msg), "IntensityRange(min = %d, max = %d)", val.GetMinimum(), val.GetMaximum()); + stream->Write(msg, strlen(msg)); + } +}; + +template<> struct DataSerializerTraits_t +{ + static void encode(OpenRCT2::IStream* stream, const rct_peep_thought& val) + { + stream->Write(&val.type); + stream->Write(&val.item); + stream->Write(&val.freshness); + stream->Write(&val.fresh_timeout); + } + static void decode(OpenRCT2::IStream* stream, rct_peep_thought& val) + { + stream->Read(&val.type); + stream->Read(&val.item); + stream->Read(&val.freshness); + stream->Read(&val.fresh_timeout); + } + static void log(OpenRCT2::IStream* stream, const rct_peep_thought& val) + { + char msg[128] = {}; + snprintf( + msg, sizeof(msg), "rct_peep_thought(type = %d, item = %d, freshness = %d, freshtimeout = %d)", + static_cast(val.type), val.item, val.freshness, val.fresh_timeout); + stream->Write(msg, strlen(msg)); + } +}; diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index bc136f1e1a..af5d57dde2 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -147,6 +147,7 @@ + @@ -437,6 +438,8 @@ + + @@ -569,6 +572,7 @@ + @@ -854,6 +858,7 @@ + @@ -875,4 +880,4 @@ - + \ No newline at end of file diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 2ffb098c20..a80167a71f 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -36,7 +36,7 @@ // This string specifies which version of network stream current build uses. // It is used for making sure only compatible builds get connected, even within // single OpenRCT2 version. -#define NETWORK_STREAM_VERSION "13" +#define NETWORK_STREAM_VERSION "14" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION static Peep* _pickup_peep = nullptr; diff --git a/src/openrct2/peep/Guest.cpp b/src/openrct2/peep/Guest.cpp index 4c808e1045..42be2a9684 100644 --- a/src/openrct2/peep/Guest.cpp +++ b/src/openrct2/peep/Guest.cpp @@ -6950,7 +6950,7 @@ Guest* Guest::Generate(const CoordsXYZ& coords) peep->PathCheckOptimisation = 0; peep->InteractionRideIndex = RIDE_ID_NULL; peep->PreviousRide = RIDE_ID_NULL; - peep->Thoughts->type = PeepThoughtType::None; + peep->Thoughts[0].type = PeepThoughtType::None; peep->WindowInvalidateFlags = 0; uint8_t intensityHighest = (scenario_rand() & 0x7) + 3; diff --git a/src/openrct2/peep/GuestPathfinding.cpp b/src/openrct2/peep/GuestPathfinding.cpp index 57ac7c0b09..3f76b06e05 100644 --- a/src/openrct2/peep/GuestPathfinding.cpp +++ b/src/openrct2/peep/GuestPathfinding.cpp @@ -1410,7 +1410,7 @@ Direction peep_pathfind_choose_direction(const TileCoordsXYZ& loc, Peep* peep) peep->PathfindGoal.direction = 0; // Clear pathfinding history - std::fill_n(reinterpret_cast(peep->PathfindHistory), sizeof(peep->PathfindHistory), 0xFF); + std::fill(std::begin(peep->PathfindHistory), std::end(peep->PathfindHistory), rct12_xyzd8{ 0xFF, 0xFF, 0xFF, 0xFF }); #if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1 if (_pathFindDebug) { diff --git a/src/openrct2/peep/Peep.h b/src/openrct2/peep/Peep.h index d104c20dab..fe48f849bb 100644 --- a/src/openrct2/peep/Peep.h +++ b/src/openrct2/peep/Peep.h @@ -50,6 +50,8 @@ constexpr auto PEEP_CLEARANCE_HEIGHT = 4 * COORDS_Z_STEP; class Formatter; struct TileElement; struct Ride; +class DataSerialiser; + namespace GameActions { class Result; @@ -649,7 +651,7 @@ struct Peep : SpriteBase int8_t RejoinQueueTimeout; // whilst waiting for a free vehicle (or pair) in the entrance ride_id_t PreviousRide; uint16_t PreviousRideTimeOut; - rct_peep_thought Thoughts[PEEP_MAX_THOUGHTS]; + std::array Thoughts; uint8_t PathCheckOptimisation; // see peep.checkForPath union { @@ -664,7 +666,7 @@ struct Peep : SpriteBase ride_id_t Photo1RideRef; uint32_t PeepFlags; rct12_xyzd8 PathfindGoal; - rct12_xyzd8 PathfindHistory[4]; + std::array PathfindHistory; uint8_t WalkingFrameNum; // 0x3F Litter Count split into lots of 3 with time, 0xC0 Time since last recalc uint8_t LitterCount; @@ -818,6 +820,7 @@ public: void RemoveItem(ShopItem item); void GiveItem(ShopItem item); bool HasItem(ShopItem peepItem) const; + void Serialise(DataSerialiser& stream); private: void UpdateRide(); @@ -885,6 +888,7 @@ public: bool CanIgnoreWideFlag(const CoordsXYZ& staffPos, TileElement* path) const; static void ResetStats(); + void Serialise(DataSerialiser& stream); private: void UpdatePatrolling(); diff --git a/src/openrct2/ride/Vehicle.h b/src/openrct2/ride/Vehicle.h index a21814b8b4..4847cfe8c1 100644 --- a/src/openrct2/ride/Vehicle.h +++ b/src/openrct2/ride/Vehicle.h @@ -28,6 +28,7 @@ using track_type_t = uint16_t; struct Ride; struct rct_ride_entry; +class DataSerialiser; struct GForces { @@ -266,6 +267,7 @@ struct Vehicle : SpriteBase update_flags |= flag; } void ApplyMass(int16_t appliedMass); + void Serialise(DataSerialiser& stream); private: bool SoundCanPlay() const; diff --git a/src/openrct2/world/Entity.cpp b/src/openrct2/world/Entity.cpp new file mode 100644 index 0000000000..2c9255b601 --- /dev/null +++ b/src/openrct2/world/Entity.cpp @@ -0,0 +1,296 @@ +/***************************************************************************** + * Copyright (c) 2014-2021 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#include "Entity.h" + +#include "../core/DataSerialiser.h" +#include "../peep/Peep.h" +#include "../ride/Vehicle.h" +#include "Sprite.h" + +static void EntityBaseSerialise(SpriteBase& base, DataSerialiser& stream) +{ + stream << base.Type; + stream << base.sprite_index; + stream << base.x; + stream << base.y; + stream << base.z; + stream << base.sprite_direction; +} + +void Litter::Serialise(DataSerialiser& stream) +{ + EntityBaseSerialise(*this, stream); + stream << SubType; + stream << creationTick; +} + +void Balloon::Serialise(DataSerialiser& stream) +{ + EntityBaseSerialise(*this, stream); + stream << frame; + stream << popped; + stream << time_to_move; + stream << colour; +} + +void Duck::Serialise(DataSerialiser& stream) +{ + EntityBaseSerialise(*this, stream); + stream << frame; + stream << target_x; + stream << target_y; + stream << state; +} + +void MoneyEffect::Serialise(DataSerialiser& stream) +{ + EntityBaseSerialise(*this, stream); + stream << frame; + stream << MoveDelay; + stream << NumMovements; + stream << Vertical; + stream << Value; + stream << OffsetX; + stream << Wiggle; +} + +void VehicleCrashParticle::Serialise(DataSerialiser& stream) +{ + EntityBaseSerialise(*this, stream); + stream << frame; + stream << time_to_live; + stream << colour; + stream << crashed_sprite_base; + stream << velocity_x; + stream << velocity_y; + stream << velocity_z; + stream << acceleration_x; + stream << acceleration_y; + stream << acceleration_z; +} + +void ExplosionFlare::Serialise(DataSerialiser& stream) +{ + EntityBaseSerialise(*this, stream); + stream << frame; +} + +void ExplosionCloud::Serialise(DataSerialiser& stream) +{ + EntityBaseSerialise(*this, stream); + stream << frame; +} + +void CrashSplashParticle::Serialise(DataSerialiser& stream) +{ + EntityBaseSerialise(*this, stream); + stream << frame; +} + +void SteamParticle::Serialise(DataSerialiser& stream) +{ + EntityBaseSerialise(*this, stream); + stream << frame; + stream << time_to_move; +} + +void JumpingFountain::Serialise(DataSerialiser& stream) +{ + EntityBaseSerialise(*this, stream); + stream << frame; + stream << FountainType; + stream << NumTicksAlive; + stream << FountainFlags; + stream << TargetX; + stream << TargetY; + stream << Iteration; +} + +static void PeepBaseSerialise(Peep& base, DataSerialiser& stream) +{ + EntityBaseSerialise(base, stream); + if (stream.IsLoading()) + { + base.Name = nullptr; + } + stream << base.NextLoc; + stream << base.NextFlags; + stream << base.State; + stream << base.SubState; + stream << base.SpriteType; + stream << base.TshirtColour; + stream << base.TrousersColour; + stream << base.DestinationX; + stream << base.DestinationY; + stream << base.DestinationTolerance; + stream << base.Var37; + stream << base.Energy; + stream << base.EnergyTarget; + stream << base.Mass; + // stream << base.WindowInvalidateFlags; + stream << base.CurrentRide; + stream << base.CurrentRideStation; + stream << base.CurrentTrain; + stream << base.CurrentCar; + stream << base.CurrentSeat; + stream << base.SpecialSprite; + stream << base.ActionSpriteType; + stream << base.NextActionSpriteType; + stream << base.ActionSpriteImageOffset; + stream << base.Action; + stream << base.ActionFrame; + stream << base.StepProgress; + stream << base.PeepDirection; + stream << base.InteractionRideIndex; + stream << base.Id; + stream << base.PathCheckOptimisation; + stream << base.PathfindGoal; + stream << base.PathfindHistory; + stream << base.WalkingFrameNum; + stream << base.PeepFlags; +} + +void Guest::Serialise(DataSerialiser& stream) +{ + PeepBaseSerialise(*this, stream); + stream << GuestNumRides; + stream << GuestNextInQueue; + stream << ParkEntryTime; + stream << GuestHeadingToRideId; + stream << GuestIsLostCountdown; + stream << GuestTimeOnRide; + stream << PaidToEnter; + stream << PaidOnRides; + stream << PaidOnFood; + stream << PaidOnDrink; + stream << PaidOnSouvenirs; + stream << OutsideOfPark; + stream << Happiness; + stream << HappinessTarget; + stream << Nausea; + stream << NauseaTarget; + stream << Hunger; + stream << Thirst; + stream << Toilet; + stream << TimeToConsume; + stream << Intensity; + stream << NauseaTolerance; + stream << RideTypesBeenOn; + stream << TimeInQueue; + stream << RidesBeenOn; + stream << CashInPocket; + stream << CashSpent; + stream << Photo1RideRef; + stream << Photo2RideRef; + stream << Photo3RideRef; + stream << Photo4RideRef; + stream << RejoinQueueTimeout; + stream << PreviousRide; + stream << PreviousRideTimeOut; + stream << Thoughts; + stream << LitterCount; + stream << DisgustingCount; + stream << AmountOfFood; + stream << AmountOfDrinks; + stream << AmountOfSouvenirs; + stream << VandalismSeen; + stream << VoucherType; + stream << VoucherRideId; + stream << SurroundingsThoughtTimeout; + stream << Angriness; + stream << TimeLost; + stream << DaysInQueue; + stream << BalloonColour; + stream << UmbrellaColour; + stream << HatColour; + stream << FavouriteRide; + stream << FavouriteRideRating; + stream << ItemFlags; +} + +void Staff::Serialise(DataSerialiser& stream) +{ + PeepBaseSerialise(*this, stream); + stream << AssignedStaffType; + stream << MechanicTimeSinceCall; + stream << HireDate; + stream << StaffId; + stream << StaffOrders; + stream << StaffMowingTimeout; + stream << StaffLawnsMown; + stream << StaffGardensWatered; + stream << StaffLitterSwept; + stream << StaffBinsEmptied; +} + +void Vehicle::Serialise(DataSerialiser& stream) +{ + EntityBaseSerialise(*this, stream); + stream << SubType; + stream << vehicle_sprite_type; + stream << bank_rotation; + stream << remaining_distance; + stream << velocity; + stream << acceleration; + stream << ride; + stream << vehicle_type; + stream << colours; + stream << track_progress; + stream << TrackTypeAndDirection; + stream << TrackLocation; + stream << next_vehicle_on_train; + stream << prev_vehicle_on_ride; + stream << next_vehicle_on_ride; + stream << var_44; + stream << mass; + stream << update_flags; + stream << SwingSprite; + stream << current_station; + stream << SwingPosition; + stream << SwingSpeed; + stream << status; + stream << sub_state; + stream << peep; + stream << peep_tshirt_colours; + stream << num_seats; + stream << num_peeps; + stream << next_free_seat; + stream << restraints_position; + stream << spin_speed; + stream << sound2_flags; + stream << spin_sprite; + stream << sound1_id; + stream << sound1_volume; + stream << sound2_id; + stream << sound2_volume; + stream << sound_vector_factor; + stream << var_C0; + stream << speed; + stream << powered_acceleration; + stream << dodgems_collision_direction; + stream << animation_frame; + stream << var_C8; + stream << var_CA; + stream << scream_sound_id; + stream << TrackSubposition; + stream << var_CE; + stream << var_CF; + stream << lost_time_out; + stream << vertical_drop_countdown; + stream << var_D3; + stream << mini_golf_current_animation; + stream << mini_golf_flags; + stream << ride_subtype; + stream << colours_extended; + stream << seat_rotation; + stream << target_seat_rotation; + stream << BoatLocation; + stream << IsCrashedVehicle; +} diff --git a/src/openrct2/world/Fountain.h b/src/openrct2/world/Fountain.h index 5744817d89..068c9a0dcc 100644 --- a/src/openrct2/world/Fountain.h +++ b/src/openrct2/world/Fountain.h @@ -13,6 +13,8 @@ #include "Map.h" #include "SpriteBase.h" +class DataSerialiser; + enum class JumpingFountainType : uint8_t { Water, @@ -31,6 +33,7 @@ struct JumpingFountain : MiscEntity uint16_t Iteration; void Update(); static void StartAnimation(JumpingFountainType newType, const CoordsXY& newLoc, const TileElement* tileElement); + void Serialise(DataSerialiser& stream); private: JumpingFountainType GetType() const; diff --git a/src/openrct2/world/Sprite.cpp b/src/openrct2/world/Sprite.cpp index 233c015fbc..3880de3c20 100644 --- a/src/openrct2/world/Sprite.cpp +++ b/src/openrct2/world/Sprite.cpp @@ -13,8 +13,11 @@ #include "../Game.h" #include "../OpenRCT2.h" #include "../audio/audio.h" +#include "../core/ChecksumStream.h" #include "../core/Crypt.h" +#include "../core/DataSerialiser.h" #include "../core/Guard.hpp" +#include "../core/MemoryStream.h" #include "../interface/Viewport.h" #include "../localisation/Date.h" #include "../localisation/Localisation.h" @@ -136,7 +139,7 @@ std::string rct_sprite_checksum::ToString() const for (auto b : raw) { char buf[3]; - snprintf(buf, 3, "%02x", b); + snprintf(buf, 3, "%02x", static_cast(b)); result.append(buf); } @@ -275,64 +278,26 @@ void reset_sprite_spatial_index() #ifndef DISABLE_NETWORK -template void ComputeChecksumForEntityType(Crypt::HashAlgorithm<20>* _entityHashAlg) +template void NetworkSerialseEntityType(DataSerialiser& ds) { for (auto* ent : EntityList()) { - T copy = *ent; - - // Only required for rendering/invalidation, has no meaning to the game state. - copy.sprite_left = copy.sprite_right = copy.sprite_top = copy.sprite_bottom = 0; - copy.sprite_width = copy.sprite_height_negative = copy.sprite_height_positive = 0; - - if constexpr (std::is_base_of_v) - { - // Name is pointer and will not be the same across clients - copy.Name = {}; - - // We set this to 0 because as soon the client selects a guest the window will remove the - // invalidation flags causing the sprite checksum to be different than on server, the flag does not - // affect game state. - copy.WindowInvalidateFlags = 0; - } - - _entityHashAlg->Update(©, sizeof(copy)); + ent->Serialise(ds); } } -template void ComputeChecksumForEntityTypes(Crypt::HashAlgorithm<20>* _entityHashAlg) +template void NetworkSerialiseEntityTypes(DataSerialiser& ds) { - (ComputeChecksumForEntityType(_entityHashAlg), ...); + (NetworkSerialseEntityType(ds), ...); } rct_sprite_checksum sprite_checksum() { - using namespace Crypt; + rct_sprite_checksum checksum{}; - // TODO Remove statics, should be one of these per sprite manager / OpenRCT2 context. - // Alternatively, make a new class for this functionality. - static std::unique_ptr> _spriteHashAlg; - - rct_sprite_checksum checksum; - - try - { - if (_spriteHashAlg == nullptr) - { - _spriteHashAlg = CreateSHA1(); - } - - _spriteHashAlg->Clear(); - - ComputeChecksumForEntityTypes(_spriteHashAlg.get()); - - checksum.raw = _spriteHashAlg->Finish(); - } - catch (std::exception& e) - { - log_error("sprite_checksum failed: %s", e.what()); - throw; - } + OpenRCT2::ChecksumStream ms(checksum.raw); + DataSerialiser ds(true, ms); + NetworkSerialiseEntityTypes(ds); return checksum; } diff --git a/src/openrct2/world/Sprite.h b/src/openrct2/world/Sprite.h index 1d94d4b1c9..7e25723e56 100644 --- a/src/openrct2/world/Sprite.h +++ b/src/openrct2/world/Sprite.h @@ -18,12 +18,14 @@ #include "SpriteBase.h" enum LitterType : uint8_t; +class DataSerialiser; struct Litter : SpriteBase { static constexpr auto cEntityType = EntityType::Litter; LitterType SubType; uint32_t creationTick; + void Serialise(DataSerialiser& stream); }; struct Balloon : MiscEntity @@ -36,6 +38,7 @@ struct Balloon : MiscEntity void Update(); void Pop(); void Press(); + void Serialise(DataSerialiser& stream); }; struct Duck : MiscEntity @@ -57,6 +60,7 @@ struct Duck : MiscEntity uint32_t GetFrameImage(int32_t direction) const; bool IsFlying(); void Remove(); + void Serialise(DataSerialiser& stream); private: void UpdateFlyToWater(); @@ -80,6 +84,7 @@ struct MoneyEffect : MiscEntity static void Create(money32 value, const CoordsXYZ& loc); void Update(); std::pair GetStringId() const; + void Serialise(DataSerialiser& stream); }; struct VehicleCrashParticle : MiscEntity @@ -96,24 +101,28 @@ struct VehicleCrashParticle : MiscEntity int32_t acceleration_z; void Update(); + void Serialise(DataSerialiser& stream); }; struct ExplosionFlare : MiscEntity { static constexpr auto cEntityType = EntityType::ExplosionFlare; void Update(); + void Serialise(DataSerialiser& stream); }; struct ExplosionCloud : MiscEntity { static constexpr auto cEntityType = EntityType::ExplosionCloud; void Update(); + void Serialise(DataSerialiser& stream); }; struct CrashSplashParticle : MiscEntity { static constexpr auto cEntityType = EntityType::CrashSplash; void Update(); + void Serialise(DataSerialiser& stream); }; struct SteamParticle : MiscEntity @@ -122,6 +131,7 @@ struct SteamParticle : MiscEntity uint16_t time_to_move; void Update(); + void Serialise(DataSerialiser& stream); }; #pragma pack(push, 1) @@ -154,7 +164,7 @@ assert_struct_size(rct_sprite, 0x200); struct rct_sprite_checksum { - std::array raw; + std::array raw; std::string ToString() const; };