Network serialiser for entities (#14541)

* Start a network serialiser for entities

will be used only for checksums and replay diffs

* Continue work

* Use the new serailser for checksums

* Use new serialiser for replays

* keep compilers happy

* Try create checksum stream

* Fix compiling

* Split off class into seperate file

* Update Xcode project

* Increment network version

* Fix pragma mistake

* Fix none network builds

* Update replays

* Improve ChecksumStream and use FNV internally

* Small cleanups

* satisfy compilers

* Revert change of checksum size to simplfy rerecording

* Zero initialise data

* Fix serialiser

* Update replays again

Co-authored-by: Michael Steenbeek <m.o.steenbeek@gmail.com>
Co-authored-by: Matt <m.moninger.h@gmail.com>
This commit is contained in:
Duncan 2021-05-09 19:12:44 +01:00 committed by GitHub
parent c63e072974
commit d46e4a9bb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 602 additions and 71 deletions

View File

@ -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")

View File

@ -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 = "<group>"; };
4C93F1B81F8E185600A9330D /* Research.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Research.cpp; sourceTree = "<group>"; };
4C93F1B91F8E185600A9330D /* Research.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Research.h; sourceTree = "<group>"; };
4CA23D62263C91D700077AA1 /* ChecksumStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ChecksumStream.cpp; sourceTree = "<group>"; };
4CA23D63263C91D700077AA1 /* ChecksumStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChecksumStream.h; sourceTree = "<group>"; };
4CA23DAF263C920900077AA1 /* Entity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Entity.h; sourceTree = "<group>"; };
4CA23DB0263C920900077AA1 /* EntityList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EntityList.h; sourceTree = "<group>"; };
4CA23DB1263C920900077AA1 /* Entity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Entity.cpp; sourceTree = "<group>"; };
4CA39E4E2513F8A00094066B /* RTL.ICU.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RTL.ICU.cpp; sourceTree = "<group>"; };
4CA39E4F2513F8A00094066B /* RTL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RTL.h; sourceTree = "<group>"; };
4CA39E502513F8A00094066B /* RTL.FriBidi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RTL.FriBidi.cpp; sourceTree = "<group>"; };
@ -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 */,

View File

@ -48,8 +48,8 @@
<TitleSequencesSha1>304d13a126c15bf2c86ff13b81a2f2cc1856ac8d</TitleSequencesSha1>
<ObjectsUrl>https://github.com/OpenRCT2/objects/releases/download/v1.0.21/objects.zip</ObjectsUrl>
<ObjectsSha1>c38af45d51a6e440386180feacf76c64720b6ac5</ObjectsSha1>
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.39/replays.zip</ReplaysUrl>
<ReplaysSha1>8AF797661D87394FBE1A059375D82632094290FB</ReplaysSha1>
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.42/replays.zip</ReplaysUrl>
<ReplaysSha1>C60FC1D6526DB8EDDF4AFBB0EE52A4A0D561646E</ReplaysSha1>
</PropertyGroup>
<ItemGroup>

View File

@ -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.

View File

@ -82,30 +82,31 @@ struct GameStateSnapshot_t
switch (sprite.misc.Type)
{
case EntityType::Vehicle:
ds << reinterpret_cast<uint8_t(&)[sizeof(Vehicle)]>(sprite.vehicle);
reinterpret_cast<Vehicle&>(sprite).Serialise(ds);
break;
case EntityType::Guest:
ds << reinterpret_cast<uint8_t(&)[sizeof(Guest)]>(sprite.peep);
reinterpret_cast<Guest&>(sprite).Serialise(ds);
break;
case EntityType::Staff:
ds << reinterpret_cast<uint8_t(&)[sizeof(Staff)]>(sprite.peep);
reinterpret_cast<Staff&>(sprite).Serialise(ds);
break;
case EntityType::Litter:
ds << reinterpret_cast<uint8_t(&)[sizeof(Litter)]>(sprite.litter);
reinterpret_cast<Litter&>(sprite).Serialise(ds);
break;
case EntityType::MoneyEffect:
ds << reinterpret_cast<uint8_t(&)[sizeof(MoneyEffect)]>(sprite.money_effect);
reinterpret_cast<MoneyEffect&>(sprite).Serialise(ds);
break;
case EntityType::Balloon:
ds << reinterpret_cast<uint8_t(&)[sizeof(Balloon)]>(sprite.balloon);
reinterpret_cast<Balloon&>(sprite).Serialise(ds);
break;
case EntityType::Duck:
ds << reinterpret_cast<uint8_t(&)[sizeof(Duck)]>(sprite.duck);
reinterpret_cast<Duck&>(sprite).Serialise(ds);
break;
case EntityType::JumpingFountain:
ds << reinterpret_cast<uint8_t(&)[sizeof(JumpingFountain)]>(sprite.jumping_fountain);
reinterpret_cast<JumpingFountain&>(sprite).Serialise(ds);
break;
case EntityType::SteamParticle:
reinterpret_cast<SteamParticle&>(sprite).Serialise(ds);
ds << reinterpret_cast<uint8_t(&)[sizeof(SteamParticle)]>(sprite.steam_particle);
break;
case EntityType::Null:

View File

@ -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 <cstddef>
namespace OpenRCT2
{
#ifndef DISABLE_NETWORK
ChecksumStream::ChecksumStream(std::array<std::byte, 20>& buf)
: _checksum(buf)
{
uint64_t* hash = reinterpret_cast<uint64_t*>(_checksum.data());
*hash = Seed;
}
void ChecksumStream::Write(const void* buffer, uint64_t length)
{
uint64_t* hash = reinterpret_cast<uint64_t*>(_checksum.data());
for (size_t i = 0; i < length; i += sizeof(uint64_t))
{
const auto maxLen = std::min<size_t>(sizeof(uint64_t), length - i);
uint64_t temp{};
std::memcpy(&temp, reinterpret_cast<const std::byte*>(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

View File

@ -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<std::byte, 20>& _checksum;
static constexpr uint64_t Seed = 0xcbf29ce484222325ULL;
static constexpr uint64_t Prime = 0x00000100000001B3ULL;
public:
ChecksumStream(std::array<std::byte, 20>& 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<size_t N> void Write(const void* buffer)
{
Write(buffer, N);
}
uint64_t TryRead(void* buffer, uint64_t length) override
{
return 0;
}
};
} // namespace OpenRCT2

View File

@ -302,7 +302,7 @@ template<typename _Ty, size_t _Size> struct DataSerializerTraitsPODArray
uint16_t swapped = ByteSwapBE(len);
stream->Write(&swapped);
DataSerializerTraits<uint8_t> s;
DataSerializerTraits<_Ty> s;
for (auto&& sub : val)
{
s.encode(stream, sub);
@ -584,6 +584,34 @@ template<> struct DataSerializerTraits_t<CoordsXYZD>
stream->Write(msg, strlen(msg));
}
};
template<> struct DataSerializerTraits_t<rct12_xyzd8>
{
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<uint8_t>());
auto y = ByteSwapBE(stream->ReadValue<uint8_t>());
auto z = ByteSwapBE(stream->ReadValue<uint8_t>());
auto d = ByteSwapBE(stream->ReadValue<uint8_t>());
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<NetworkCheatType_t>
{
@ -793,3 +821,49 @@ template<> struct DataSerializerTraits_t<ObjectEntryDescriptor>
stream->Write(msg, strlen(msg));
}
};
template<> struct DataSerializerTraits_t<IntensityRange>
{
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<uint8_t>();
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<rct_peep_thought>
{
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<int32_t>(val.type), val.item, val.freshness, val.fresh_timeout);
stream->Write(msg, strlen(msg));
}
};

View File

@ -147,6 +147,7 @@
<ClInclude Include="config\IniReader.hpp" />
<ClInclude Include="config\IniWriter.hpp" />
<ClInclude Include="Context.h" />
<ClInclude Include="core\ChecksumStream.h" />
<ClInclude Include="core\CircularBuffer.h" />
<ClInclude Include="core\Collections.hpp" />
<ClInclude Include="core\Console.hpp" />
@ -437,6 +438,8 @@
<ClInclude Include="windows\tile_inspector.h" />
<ClInclude Include="world\Banner.h" />
<ClInclude Include="world\Climate.h" />
<ClInclude Include="world\Entity.h" />
<ClInclude Include="world\EntityList.h" />
<ClInclude Include="world\Entrance.h" />
<ClInclude Include="world\Footpath.h" />
<ClInclude Include="world\Fountain.h" />
@ -569,6 +572,7 @@
<ClCompile Include="config\IniReader.cpp" />
<ClCompile Include="config\IniWriter.cpp" />
<ClCompile Include="Context.cpp" />
<ClCompile Include="core\ChecksumStream.cpp" />
<ClCompile Include="core\Console.cpp" />
<ClCompile Include="core\Crypt.CNG.cpp" />
<ClCompile Include="core\Crypt.OpenSSL.cpp" />
@ -854,6 +858,7 @@
<ClCompile Include="world\Banner.cpp" />
<ClCompile Include="world\Climate.cpp" />
<ClCompile Include="world\Duck.cpp" />
<ClCompile Include="world\Entity.cpp" />
<ClCompile Include="world\Entrance.cpp" />
<ClCompile Include="world\Footpath.cpp" />
<ClCompile Include="world\Fountain.cpp" />
@ -875,4 +880,4 @@
<ClCompile Include="world\Wall.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>
</Project>

View File

@ -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;

View File

@ -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;

View File

@ -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<uint8_t*>(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)
{

View File

@ -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<rct_peep_thought, PEEP_MAX_THOUGHTS> 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<rct12_xyzd8, 4> 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();

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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<int32_t>(b));
result.append(buf);
}
@ -275,64 +278,26 @@ void reset_sprite_spatial_index()
#ifndef DISABLE_NETWORK
template<typename T> void ComputeChecksumForEntityType(Crypt::HashAlgorithm<20>* _entityHashAlg)
template<typename T> void NetworkSerialseEntityType(DataSerialiser& ds)
{
for (auto* ent : EntityList<T>())
{
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<Peep, T>)
{
// 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(&copy, sizeof(copy));
ent->Serialise(ds);
}
}
template<typename... T> void ComputeChecksumForEntityTypes(Crypt::HashAlgorithm<20>* _entityHashAlg)
template<typename... T> void NetworkSerialiseEntityTypes(DataSerialiser& ds)
{
(ComputeChecksumForEntityType<T>(_entityHashAlg), ...);
(NetworkSerialseEntityType<T>(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<HashAlgorithm<20>> _spriteHashAlg;
rct_sprite_checksum checksum;
try
{
if (_spriteHashAlg == nullptr)
{
_spriteHashAlg = CreateSHA1();
}
_spriteHashAlg->Clear();
ComputeChecksumForEntityTypes<Guest, Staff, Vehicle, Litter>(_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<Guest, Staff, Vehicle, Litter>(ds);
return checksum;
}

View File

@ -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<rct_string_id, money32> 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<uint8_t, 20> raw;
std::array<std::byte, 20> raw;
std::string ToString() const;
};