Implement game action logging. (#8138)

This commit is contained in:
ζeh Matt 2018-11-20 06:04:42 +01:00 committed by Michał Janiszewski
parent e043af4be2
commit 1abb31a159
28 changed files with 336 additions and 69 deletions

View File

@ -44,7 +44,7 @@ public:
void Serialise(DataSerialiser& stream) override
{
GameAction::Serialise(stream);
stream << _bannerIndex << _name;
stream << DS_TAG(_bannerIndex) << DS_TAG(_name);
}
GameActionResult::Ptr Query() const override

View File

@ -35,7 +35,7 @@ public:
{
GameAction::Serialise(stream);
stream << _climate;
stream << DS_TAG(_climate);
}
GameActionResult::Ptr Query() const override

View File

@ -46,7 +46,7 @@ public:
{
GameAction::Serialise(stream);
stream << _x << _y << _z;
stream << DS_TAG(_x) << DS_TAG(_y) << DS_TAG(_z);
}
GameActionResult::Ptr Query() const override

View File

@ -140,6 +140,57 @@ namespace GameActions
return result;
}
static const char* GetRealm()
{
if (network_get_mode() == NETWORK_MODE_CLIENT)
return "cl";
else if (network_get_mode() == NETWORK_MODE_SERVER)
return "sv";
return "";
}
struct ActionLogContext_t
{
MemoryStream output;
};
static void LogActionBegin(ActionLogContext_t& ctx, const GameAction* action)
{
MemoryStream& output = ctx.output;
char temp[128] = {};
snprintf(temp, sizeof(temp), "[%s] Game Action %08X (", GetRealm(), action->GetType());
output.Write(temp, strlen(temp));
DataSerialiser ds(true, ctx.output, true); // Logging mode.
// Write all parameters into output as text.
action->Serialise(ds);
}
static void LogActionFinish(ActionLogContext_t& ctx, const GameAction* action, const GameActionResult::Ptr& result)
{
MemoryStream& output = ctx.output;
char temp[128] = {};
if (result->Error != GA_ERROR::OK)
{
snprintf(temp, sizeof(temp), ") Failed, %u", (uint32_t)result->Error);
}
else
{
snprintf(temp, sizeof(temp), ") OK");
}
output.Write(temp, strlen(temp) + 1);
const char* text = (const char*)output.GetData();
log_info(text);
network_append_server_log(text);
}
GameActionResult::Ptr Execute(const GameAction* action)
{
Guard::ArgumentNotNull(action);
@ -176,11 +227,14 @@ namespace GameActions
}
}
log_verbose("[%s] GameAction::Execute\n", "sv");
ActionLogContext_t logContext;
LogActionBegin(logContext, action);
// Execute the action, changing the game state
result = action->Execute();
LogActionFinish(logContext, action, result);
gCommandPosition.x = result->Position.x;
gCommandPosition.y = result->Position.y;
gCommandPosition.z = result->Position.z;
@ -197,13 +251,12 @@ namespace GameActions
{
if (network_get_mode() == NETWORK_MODE_SERVER && result->Error == GA_ERROR::OK)
{
const uint32_t playerId = action->GetPlayer();
const uint32_t playerIndex = network_get_player_index(playerId);
NetworkPlayerId_t playerId = action->GetPlayer();
network_set_player_last_action(playerIndex, action->GetType());
network_set_player_last_action(network_get_player_index(playerId.id), action->GetType());
if (result->Cost != 0)
{
network_add_player_money_spent(playerIndex, result->Cost);
network_add_player_money_spent(playerId.id, result->Cost);
}
}
}
@ -230,4 +283,5 @@ namespace GameActions
}
return result;
}
} // namespace GameActions

View File

@ -90,8 +90,8 @@ public:
private:
uint32_t const _type;
uint32_t _playerId = 0; // Callee
uint32_t _flags = 0; // GAME_COMMAND_FLAGS
NetworkPlayerId_t _playerId = { 0 }; // Callee
uint32_t _flags = 0; // GAME_COMMAND_FLAGS
uint32_t _networkId = 0;
Callback_t _callback;
@ -103,12 +103,12 @@ public:
virtual ~GameAction() = default;
uint32_t GetPlayer() const
NetworkPlayerId_t GetPlayer() const
{
return _playerId;
}
void SetPlayer(uint32_t playerId)
void SetPlayer(NetworkPlayerId_t playerId)
{
_playerId = playerId;
}
@ -174,9 +174,7 @@ public:
virtual void Serialise(DataSerialiser& stream)
{
stream << _networkId;
stream << _flags;
stream << _playerId;
stream << DS_TAG(_networkId) << DS_TAG(_flags) << DS_TAG(_playerId);
}
// Helper function, allows const Objects to still serialize into DataSerialiser while being const.

View File

@ -46,7 +46,7 @@ public:
{
GameAction::Serialise(stream);
stream << _spriteIndex << _name;
stream << DS_TAG(_spriteIndex) << DS_TAG(_name);
}
GameActionResult::Ptr Query() const override

View File

@ -56,7 +56,7 @@ private:
uint16_t _z;
bool _initialPlacement;
uint8_t _direction;
uint8_t _rideIndex;
NetworkRideId_t _rideIndex;
uint8_t _mode;
public:
@ -64,7 +64,7 @@ public:
{
}
MazeSetTrackAction(
uint16_t x, uint16_t y, uint16_t z, bool initialPlacement, uint8_t direction, uint8_t rideIndex, uint8_t mode)
uint16_t x, uint16_t y, uint16_t z, bool initialPlacement, uint8_t direction, NetworkRideId_t rideIndex, uint8_t mode)
: _x(x)
, _y(y)
, _z(z)
@ -79,7 +79,8 @@ public:
{
GameAction::Serialise(stream);
stream << _x << _y << _z << _initialPlacement << _direction << _rideIndex << _mode;
stream << DS_TAG(_x) << DS_TAG(_y) << DS_TAG(_z) << DS_TAG(_initialPlacement) << DS_TAG(_direction)
<< DS_TAG(_rideIndex) << DS_TAG(_mode);
}
GameActionResult::Ptr Query() const override

View File

@ -47,7 +47,7 @@ public:
void Serialise(DataSerialiser& stream) override
{
GameAction::Serialise(stream);
stream << _type << _item << _numWeeks;
stream << DS_TAG(_type) << DS_TAG(_item) << DS_TAG(_numWeeks);
}
GameActionResult::Ptr Query() const override

View File

@ -40,7 +40,7 @@ public:
void Serialise(DataSerialiser& stream) override
{
GameAction::Serialise(stream);
stream << _value;
stream << DS_TAG(_value);
}
GameActionResult::Ptr Query() const override

View File

@ -44,7 +44,7 @@ public:
void Serialise(DataSerialiser& stream) override
{
GameAction::Serialise(stream);
stream << _name;
stream << DS_TAG(_name);
}
GameActionResult::Ptr Query() const override
@ -85,13 +85,6 @@ public:
user_string_free(gParkName);
gParkName = newNameId;
// Log park rename command if we are in multiplayer and logging is enabled
if ((network_get_mode() == NETWORK_MODE_CLIENT || network_get_mode() == NETWORK_MODE_SERVER)
&& gConfigNetwork.log_server_actions)
{
LogAction(oldName);
}
scrolling_text_invalidate();
gfx_invalidate_screen();
@ -105,16 +98,4 @@ private:
format_string(buffer, sizeof(buffer), gParkName, &gParkNameArgs);
return buffer;
}
void LogAction(const std::string& oldName) const
{
// Get player name
auto playerIndex = network_get_player_index(GetPlayer());
auto playerName = network_get_player_name(playerIndex);
char logMessage[256];
const char* args[3] = { playerName, oldName.c_str(), _name.c_str() };
format_string(logMessage, sizeof(logMessage), STR_LOG_PARK_NAME, (void*)args);
network_append_server_log(logMessage);
}
};

View File

@ -43,7 +43,7 @@ public:
void Serialise(DataSerialiser& stream) override
{
GameAction::Serialise(stream);
stream << _priorities << _fundingAmount;
stream << DS_TAG(_priorities) << DS_TAG(_fundingAmount);
}
GameActionResult::Ptr Query() const override

View File

@ -51,7 +51,7 @@ public:
{
GameAction::Serialise(stream);
stream << _x << _y << _z << _direction;
stream << DS_TAG(_x) << DS_TAG(_y) << DS_TAG(_z) << DS_TAG(_direction);
}
GameActionResult::Ptr Query() const override

View File

@ -44,7 +44,7 @@ public:
{
GameAction::Serialise(stream);
stream << _location.x << _location.y << _location.z << _location.direction;
stream << DS_TAG(_location.x) << DS_TAG(_location.y) << DS_TAG(_location.z) << DS_TAG(_location.direction);
}
GameActionResult::Ptr Query() const override

View File

@ -64,14 +64,13 @@ public:
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED;
;
}
void Serialise(DataSerialiser& stream) override
{
GameAction::Serialise(stream);
stream << _rideType << _subType << _colour1 << _colour2;
stream << DS_TAG(_rideType) << DS_TAG(_subType) << DS_TAG(_colour1) << DS_TAG(_colour2);
}
GameActionResult::Ptr Query() const override

View File

@ -31,7 +31,7 @@ using namespace OpenRCT2;
struct RideDemolishAction : public GameActionBase<GAME_COMMAND_DEMOLISH_RIDE, GameActionResult>
{
private:
int32_t _rideIndex = -1;
NetworkRideId_t _rideIndex{ -1 };
uint8_t _modifyType = RIDE_MODIFY_DEMOLISH;
public:
@ -48,7 +48,7 @@ public:
{
GameAction::Serialise(stream);
stream << _rideIndex << _modifyType;
stream << DS_TAG(_rideIndex) << DS_TAG(_modifyType);
}
GameActionResult::Ptr Query() const override

View File

@ -25,7 +25,7 @@
struct RideSetNameAction : public GameActionBase<GAME_COMMAND_SET_RIDE_NAME, GameActionResult>
{
private:
int32_t _rideIndex = -1;
NetworkRideId_t _rideIndex{ -1 };
std::string _name;
public:
@ -47,7 +47,7 @@ public:
{
GameAction::Serialise(stream);
stream << _rideIndex << _name;
stream << DS_TAG(_rideIndex) << DS_TAG(_name);
}
GameActionResult::Ptr Query() const override

View File

@ -30,7 +30,7 @@ static rct_string_id _StatusErrorTitles[] = {
struct RideSetStatusAction : public GameActionBase<GAME_COMMAND_SET_RIDE_STATUS, GameActionResult>
{
private:
int32_t _rideIndex = -1;
NetworkRideId_t _rideIndex{ -1 };
uint8_t _status = RIDE_STATUS_CLOSED;
public:
@ -52,7 +52,7 @@ public:
{
GameAction::Serialise(stream);
stream << _rideIndex << _status;
stream << DS_TAG(_rideIndex) << DS_TAG(_status);
}
GameActionResult::Ptr Query() const override

View File

@ -39,7 +39,7 @@ public:
{
GameAction::Serialise(stream);
stream << _fee;
stream << DS_TAG(_fee);
}
GameActionResult::Ptr Query() const override

View File

@ -43,7 +43,7 @@ public:
void Serialise(DataSerialiser& stream) override
{
GameAction::Serialise(stream);
stream << _bannerIndex << _name;
stream << DS_TAG(_bannerIndex) << DS_TAG(_name);
}
GameActionResult::Ptr Query() const override

View File

@ -44,7 +44,7 @@ public:
void Serialise(DataSerialiser& stream) override
{
GameAction::Serialise(stream);
stream << _staffType << _colour;
stream << DS_TAG(_staffType) << DS_TAG(_colour);
}
GameActionResult::Ptr Query() const override

View File

@ -47,7 +47,7 @@ public:
{
GameAction::Serialise(stream);
stream << _spriteIndex << _name;
stream << DS_TAG(_spriteIndex) << DS_TAG(_name);
}
GameActionResult::Ptr Query() const override

View File

@ -35,7 +35,7 @@ public:
{
GameAction::Serialise(stream);
stream << _location.x << _location.y << _location.z << _location.direction;
stream << DS_TAG(_location.x) << DS_TAG(_location.y) << DS_TAG(_location.z) << DS_TAG(_location.direction);
}
GameActionResult::Ptr Query() const override

View File

@ -17,18 +17,21 @@ class DataSerialiser
{
private:
MemoryStream _stream;
MemoryStream* _activeStream;
bool _isSaving;
IStream* _activeStream = nullptr;
bool _isSaving = false;
bool _isLogging = false;
public:
DataSerialiser(bool isSaving)
: _isSaving(isSaving)
, _isLogging(false)
{
_activeStream = &_stream;
}
DataSerialiser(bool isSaving, MemoryStream& stream)
DataSerialiser(bool isSaving, IStream& stream, bool isLogging = false)
: _isSaving(isSaving)
, _isLogging(isLogging)
{
_activeStream = &stream;
}
@ -50,10 +53,35 @@ public:
template<typename T> DataSerialiser& operator<<(T& data)
{
if (_isSaving)
DataSerializerTraits<T>::encode(_activeStream, data);
if (!_isLogging)
{
if (_isSaving)
DataSerializerTraits<T>::encode(_activeStream, data);
else
DataSerializerTraits<T>::decode(_activeStream, data);
}
else
DataSerializerTraits<T>::decode(_activeStream, data);
{
DataSerializerTraits<T>::log(_activeStream, data);
}
return *this;
}
template<typename T> DataSerialiser& operator<<(DataSerialiserTag<T> data)
{
if (!_isLogging)
{
if (_isSaving)
DataSerializerTraits<DataSerialiserTag<T>>::encode(_activeStream, data);
else
DataSerializerTraits<DataSerialiserTag<T>>::decode(_activeStream, data);
}
else
{
DataSerializerTraits<DataSerialiserTag<T>>::log(_activeStream, data);
}
return *this;
}
};

View File

@ -0,0 +1,42 @@
/*****************************************************************************
* Copyright (c) 2014-2018 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
template<typename T> class DataSerialiserTag
{
public:
DataSerialiserTag(const char* name, T& data)
: _name(name)
, _data(data)
{
}
const char* Name() const
{
return _name;
}
T& Data() const
{
return _data;
}
private:
const char* _name = nullptr;
T& _data;
};
template<typename T> inline DataSerialiserTag<T> CreateDataSerialiserTag(const char* name, T& data)
{
DataSerialiserTag<T> r(name, data);
return r;
}
#define DS_TAG(var) CreateDataSerialiserTag(#var, var)

View File

@ -9,13 +9,21 @@
#pragma once
#include "../localisation/Localisation.h"
#include "../network/NetworkTypes.h"
#include "../network/network.h"
#include "../ride/Ride.h"
#include "DataSerialiserTag.h"
#include "Endianness.h"
#include "MemoryStream.h"
#include <cstdio>
template<typename T> struct DataSerializerTraits
{
static void encode(IStream* stream, const T& v) = delete;
static void decode(IStream* stream, T& val) = delete;
static void log(IStream* stream, T& val) = delete;
};
template<typename T> struct DataSerializerTraitsIntegral
@ -31,10 +39,41 @@ template<typename T> struct DataSerializerTraitsIntegral
stream->Read(&temp);
val = ByteSwapBE(temp);
}
static void log(IStream* stream, T& val)
{
char temp[32] = {};
if constexpr (sizeof(T) == 1)
snprintf(temp, sizeof(temp), "%02X", val);
else if constexpr (sizeof(T) == 2)
snprintf(temp, sizeof(temp), "%04X", val);
else if constexpr (sizeof(T) == 4)
snprintf(temp, sizeof(temp), "%08X", val);
else if constexpr (sizeof(T) == 8)
snprintf(temp, sizeof(temp), "%16X", val);
else
static_assert("Invalid size");
stream->Write(temp, strlen(temp));
}
};
template<> struct DataSerializerTraits<bool> : public DataSerializerTraitsIntegral<bool>
template<> struct DataSerializerTraits<bool>
{
static void encode(IStream* stream, const bool& val)
{
stream->Write(&val);
}
static void decode(IStream* stream, bool& val)
{
stream->Read(&val);
}
static void log(IStream* stream, bool& val)
{
if (val)
stream->Write("true", 4);
else
stream->Write("false", 5);
}
};
template<> struct DataSerializerTraits<uint8_t> : public DataSerializerTraitsIntegral<uint8_t>
@ -81,4 +120,102 @@ template<> struct DataSerializerTraits<std::string>
Memory::FreeArray(str, len);
}
static void log(IStream* stream, const std::string& str)
{
stream->Write("\"");
stream->Write(str.data(), str.size());
stream->Write("\"");
}
};
template<> struct DataSerializerTraits<NetworkPlayerId_t>
{
static void encode(IStream* stream, const NetworkPlayerId_t& val)
{
uint32_t temp = ByteSwapBE(val.id);
stream->Write(&temp);
}
static void decode(IStream* stream, NetworkPlayerId_t& val)
{
uint32_t temp;
stream->Read(&temp);
val.id = ByteSwapBE(temp);
}
static void log(IStream* stream, NetworkPlayerId_t& val)
{
char playerId[28] = {};
snprintf(playerId, sizeof(playerId), "%u", val.id);
stream->Write(playerId, strlen(playerId));
int32_t playerIndex = network_get_player_index(val.id);
if (playerIndex != -1)
{
const char* playerName = network_get_player_name(playerIndex);
if (playerName != nullptr)
{
stream->Write(" \"", 2);
stream->Write(playerName, strlen(playerName));
stream->Write("\"", 1);
}
}
}
};
template<> struct DataSerializerTraits<NetworkRideId_t>
{
static void encode(IStream* stream, const NetworkRideId_t& val)
{
uint32_t temp = ByteSwapBE(val.id);
stream->Write(&temp);
}
static void decode(IStream* stream, NetworkRideId_t& val)
{
uint32_t temp;
stream->Read(&temp);
val.id = ByteSwapBE(temp);
}
static void log(IStream* stream, NetworkRideId_t& val)
{
char rideId[28] = {};
snprintf(rideId, sizeof(rideId), "%u", val.id);
stream->Write(rideId, strlen(rideId));
Ride* ride = get_ride(val.id);
if (ride)
{
char rideName[256] = {};
format_string(rideName, 256, ride->name, &ride->name_arguments);
stream->Write(" \"", 2);
stream->Write(rideName, strlen(rideName));
stream->Write("\"", 1);
}
}
};
template<typename T> struct DataSerializerTraits<DataSerialiserTag<T>>
{
static void encode(IStream* stream, const DataSerialiserTag<T>& tag)
{
DataSerializerTraits<T> s;
s.encode(stream, tag.Data());
}
static void decode(IStream* stream, DataSerialiserTag<T>& tag)
{
DataSerializerTraits<T> s;
s.decode(stream, tag.Data());
}
static void log(IStream* stream, DataSerialiserTag<T>& tag)
{
const char* name = tag.Name();
stream->Write(name, strlen(name));
stream->Write(" = ", 3);
DataSerializerTraits<T> s;
s.log(stream, tag.Data());
stream->Write("; ", 2);
}
};

View File

@ -2635,7 +2635,7 @@ void Network::Client_Handle_GAME_ACTION([[maybe_unused]] NetworkConnection& conn
}
action->Serialise(ds);
if (player_id == action->GetPlayer())
if (player_id == action->GetPlayer().id)
{
// Only execute callbacks that belong to us,
// clients can have identical network ids assigned.
@ -2721,7 +2721,7 @@ void Network::Server_Handle_GAME_ACTION(NetworkConnection& connection, NetworkPa
ga->Serialise(stream);
// Set player to sender, should be 0 if sent from client.
ga->SetPlayer(connection.Player->Id);
ga->SetPlayer(NetworkPlayerId_t{ connection.Player->Id });
game_command_queue.emplace(tick, std::move(ga), _commandId++);
}
@ -3143,7 +3143,7 @@ uint32_t network_get_player_commands_ran(uint32_t index)
return gNetwork.player_list[index]->CommandsRan;
}
int32_t network_get_player_index(uint8_t id)
int32_t network_get_player_index(uint32_t id)
{
auto it = gNetwork.GetPlayerIteratorByID(id);
if (it == gNetwork.player_list.end())
@ -3915,7 +3915,7 @@ uint32_t network_get_player_commands_ran(uint32_t index)
{
return 0;
}
int32_t network_get_player_index(uint8_t id)
int32_t network_get_player_index(uint32_t id)
{
return -1;
}

View File

@ -68,3 +68,30 @@ enum NETWORK_COMMAND
NETWORK_COMMAND_MAX,
NETWORK_COMMAND_INVALID = -1
};
// Structure is used for networking specific fields with meaning,
// this structure can be used in combination with DataSerialiser
// to provide extra details with template specialization.
#pragma pack(push, 1)
template<typename T, size_t _TypeID> struct NetworkObjectId_t
{
NetworkObjectId_t(T v)
: id(v)
{
}
NetworkObjectId_t()
: id(T(-1))
{
}
operator T() const
{
return id;
}
T id;
};
#pragma pack(pop)
// NOTE: When adding new types make sure to have no duplicate _TypeID's otherwise
// there is no way to specialize templates if they have the exact symbol.
using NetworkPlayerId_t = NetworkObjectId_t<int32_t, 0>;
using NetworkRideId_t = NetworkObjectId_t<int32_t, 1>;

View File

@ -57,7 +57,7 @@ void network_set_player_last_action(uint32_t index, int32_t command);
LocationXYZ16 network_get_player_last_action_coord(uint32_t index);
void network_set_player_last_action_coord(uint32_t index, LocationXYZ16 coord);
uint32_t network_get_player_commands_ran(uint32_t index);
int32_t network_get_player_index(uint8_t id);
int32_t network_get_player_index(uint32_t id);
uint8_t network_get_player_group(uint32_t index);
void network_set_player_group(uint32_t index, uint32_t groupindex);
int32_t network_get_group_index(uint8_t id);