mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-31 02:35:46 +01:00
Merge pull request #4615 from janisozaur/mediate-network-objects
Mediate network objects
This commit is contained in:
@@ -28,17 +28,19 @@
|
||||
#include "TcpSocket.h"
|
||||
|
||||
class NetworkPlayer;
|
||||
struct ObjectRepositoryItem;
|
||||
|
||||
class NetworkConnection final
|
||||
{
|
||||
public:
|
||||
ITcpSocket * Socket = nullptr;
|
||||
NetworkPacket InboundPacket;
|
||||
NETWORK_AUTH AuthStatus = NETWORK_AUTH_NONE;
|
||||
NetworkPlayer * Player = nullptr;
|
||||
uint32 PingTime = 0;
|
||||
NetworkKey Key;
|
||||
std::vector<uint8> Challenge;
|
||||
ITcpSocket * Socket = nullptr;
|
||||
NetworkPacket InboundPacket;
|
||||
NETWORK_AUTH AuthStatus = NETWORK_AUTH_NONE;
|
||||
NetworkPlayer * Player = nullptr;
|
||||
uint32 PingTime = 0;
|
||||
NetworkKey Key;
|
||||
std::vector<uint8> Challenge;
|
||||
std::vector<const ObjectRepositoryItem *> RequestedObjects;
|
||||
|
||||
NetworkConnection();
|
||||
~NetworkConnection();
|
||||
|
||||
@@ -68,6 +68,7 @@ bool NetworkPacket::CommandRequiresAuth()
|
||||
case NETWORK_COMMAND_AUTH:
|
||||
case NETWORK_COMMAND_TOKEN:
|
||||
case NETWORK_COMMAND_GAMEINFO:
|
||||
case NETWORK_COMMAND_OBJECTS:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
|
||||
@@ -56,6 +56,7 @@ enum NETWORK_COMMAND
|
||||
NETWORK_COMMAND_GROUPLIST,
|
||||
NETWORK_COMMAND_EVENT,
|
||||
NETWORK_COMMAND_TOKEN,
|
||||
NETWORK_COMMAND_OBJECTS,
|
||||
NETWORK_COMMAND_MAX,
|
||||
NETWORK_COMMAND_INVALID = -1
|
||||
};
|
||||
|
||||
@@ -50,6 +50,8 @@ int _pickup_peep_old_x = SPRITE_LOCATION_NULL;
|
||||
#include "../core/Path.hpp"
|
||||
#include "../core/String.hpp"
|
||||
#include "../core/Util.hpp"
|
||||
#include "../object/ObjectRepository.h"
|
||||
#include "../rct2/S6Exporter.h"
|
||||
|
||||
extern "C" {
|
||||
#include "../config.h"
|
||||
@@ -116,6 +118,7 @@ Network::Network()
|
||||
client_command_handlers[NETWORK_COMMAND_EVENT] = &Network::Client_Handle_EVENT;
|
||||
client_command_handlers[NETWORK_COMMAND_GAMEINFO] = &Network::Client_Handle_GAMEINFO;
|
||||
client_command_handlers[NETWORK_COMMAND_TOKEN] = &Network::Client_Handle_TOKEN;
|
||||
client_command_handlers[NETWORK_COMMAND_OBJECTS] = &Network::Client_Handle_OBJECTS;
|
||||
server_command_handlers.resize(NETWORK_COMMAND_MAX, 0);
|
||||
server_command_handlers[NETWORK_COMMAND_AUTH] = &Network::Server_Handle_AUTH;
|
||||
server_command_handlers[NETWORK_COMMAND_CHAT] = &Network::Server_Handle_CHAT;
|
||||
@@ -123,6 +126,7 @@ Network::Network()
|
||||
server_command_handlers[NETWORK_COMMAND_PING] = &Network::Server_Handle_PING;
|
||||
server_command_handlers[NETWORK_COMMAND_GAMEINFO] = &Network::Server_Handle_GAMEINFO;
|
||||
server_command_handlers[NETWORK_COMMAND_TOKEN] = &Network::Server_Handle_TOKEN;
|
||||
server_command_handlers[NETWORK_COMMAND_OBJECTS] = &Network::Server_Handle_OBJECTS;
|
||||
OpenSSL_add_all_algorithms();
|
||||
}
|
||||
|
||||
@@ -429,7 +433,7 @@ void Network::UpdateClient()
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NETWORK_STATUS_CONNECTED:
|
||||
case SOCKET_STATUS_CONNECTED:
|
||||
{
|
||||
status = NETWORK_STATUS_CONNECTED;
|
||||
server_connection.ResetLastPacketTime();
|
||||
@@ -858,6 +862,19 @@ void Network::Client_Send_AUTH(const char* name, const char* password, const cha
|
||||
server_connection.QueuePacket(std::move(packet));
|
||||
}
|
||||
|
||||
void Network::Client_Send_OBJECTS(const std::vector<std::string> &objects)
|
||||
{
|
||||
log_verbose("client requests %u objects", uint32(objects.size()));
|
||||
std::unique_ptr<NetworkPacket> packet(NetworkPacket::Allocate());
|
||||
*packet << (uint32)NETWORK_COMMAND_OBJECTS << (uint32)objects.size();
|
||||
for (uint32 i = 0; i < objects.size(); i++)
|
||||
{
|
||||
log_verbose("client requests object %s", objects[i].c_str());
|
||||
packet->Write((const uint8 *)objects[i].c_str(), 8);
|
||||
}
|
||||
server_connection.QueuePacket(std::move(packet));
|
||||
}
|
||||
|
||||
void Network::Server_Send_TOKEN(NetworkConnection& connection)
|
||||
{
|
||||
std::unique_ptr<NetworkPacket> packet(NetworkPacket::Allocate());
|
||||
@@ -866,6 +883,20 @@ void Network::Server_Send_TOKEN(NetworkConnection& connection)
|
||||
connection.QueuePacket(std::move(packet));
|
||||
}
|
||||
|
||||
void Network::Server_Send_OBJECTS(NetworkConnection& connection, const std::vector<const ObjectRepositoryItem *> &objects) const
|
||||
{
|
||||
log_verbose("Server sends objects list with %u items", objects.size());
|
||||
std::unique_ptr<NetworkPacket> packet(NetworkPacket::Allocate());
|
||||
*packet << (uint32)NETWORK_COMMAND_OBJECTS << (uint32)objects.size();
|
||||
for (size_t i = 0; i < objects.size(); i++)
|
||||
{
|
||||
log_verbose("Object %.8s (checksum %x)", objects[i]->ObjectEntry.name, objects[i]->ObjectEntry.checksum);
|
||||
packet->Write((const uint8 *)objects[i]->ObjectEntry.name, 8);
|
||||
*packet << objects[i]->ObjectEntry.checksum << objects[i]->ObjectEntry.flags;
|
||||
}
|
||||
connection.QueuePacket(std::move(packet));
|
||||
}
|
||||
|
||||
void Network::Server_Send_AUTH(NetworkConnection& connection)
|
||||
{
|
||||
uint8 new_playerid = 0;
|
||||
@@ -886,56 +917,22 @@ void Network::Server_Send_AUTH(NetworkConnection& connection)
|
||||
|
||||
void Network::Server_Send_MAP(NetworkConnection* connection)
|
||||
{
|
||||
bool RLEState = gUseRLE;
|
||||
gUseRLE = false;
|
||||
FILE* temp = tmpfile();
|
||||
if (!temp) {
|
||||
log_warning("Failed to create temporary file to save map.");
|
||||
return;
|
||||
}
|
||||
SDL_RWops* rw = SDL_RWFromFP(temp, SDL_TRUE);
|
||||
scenario_save_network(rw);
|
||||
gUseRLE = RLEState;
|
||||
int size = (int)SDL_RWtell(rw);
|
||||
std::vector<uint8> buffer(size);
|
||||
SDL_RWseek(rw, 0, RW_SEEK_SET);
|
||||
if (SDL_RWread(rw, &buffer[0], size, 1) == 0) {
|
||||
log_warning("Failed to read temporary map file into memory.");
|
||||
SDL_RWclose(rw);
|
||||
size_t out_size;
|
||||
unsigned char *header;
|
||||
header = save_for_network(rw, out_size, connection->RequestedObjects);
|
||||
SDL_RWclose(rw);
|
||||
if (header == nullptr) {
|
||||
connection->SetLastDisconnectReason(STR_MULTIPLAYER_CONNECTION_CLOSED);
|
||||
connection->Socket->Disconnect();
|
||||
return;
|
||||
}
|
||||
size_t chunksize = 65000;
|
||||
size_t out_size = size;
|
||||
unsigned char *compressed = util_zlib_deflate(&buffer[0], size, &out_size);
|
||||
unsigned char *header;
|
||||
if (compressed != NULL)
|
||||
{
|
||||
header = (unsigned char *)_strdup("open2_sv6_zlib");
|
||||
size_t header_len = strlen((char *)header) + 1; // account for null terminator
|
||||
header = (unsigned char *)realloc(header, header_len + out_size);
|
||||
if (header == nullptr) {
|
||||
log_error("Failed to allocate %u bytes.", header_len + out_size);
|
||||
connection->SetLastDisconnectReason(STR_MULTIPLAYER_CONNECTION_CLOSED);
|
||||
connection->Socket->Disconnect();
|
||||
free(compressed);
|
||||
return;
|
||||
}
|
||||
memcpy(&header[header_len], compressed, out_size);
|
||||
out_size += header_len;
|
||||
free(compressed);
|
||||
log_verbose("Sending map of size %u bytes, compressed to %u bytes", size, out_size);
|
||||
} else {
|
||||
log_warning("Failed to compress the data, falling back to non-compressed sv6.");
|
||||
header = (unsigned char *)malloc(size);
|
||||
if (header == nullptr) {
|
||||
log_error("Failed to allocate %u bytes.", size);
|
||||
connection->SetLastDisconnectReason(STR_MULTIPLAYER_CONNECTION_CLOSED);
|
||||
connection->Socket->Disconnect();
|
||||
return;
|
||||
}
|
||||
out_size = size;
|
||||
memcpy(header, &buffer[0], size);
|
||||
}
|
||||
for (size_t i = 0; i < out_size; i += chunksize) {
|
||||
size_t datasize = Math::Min(chunksize, out_size - i);
|
||||
std::unique_ptr<NetworkPacket> packet(NetworkPacket::Allocate());
|
||||
@@ -948,7 +945,48 @@ void Network::Server_Send_MAP(NetworkConnection* connection)
|
||||
}
|
||||
}
|
||||
free(header);
|
||||
SDL_RWclose(rw);
|
||||
}
|
||||
|
||||
unsigned char * Network::save_for_network(SDL_RWops *rw_buffer, size_t &out_size, const std::vector<const ObjectRepositoryItem *> &objects) const
|
||||
{
|
||||
unsigned char * header = nullptr;
|
||||
out_size = 0;
|
||||
bool RLEState = gUseRLE;
|
||||
gUseRLE = false;
|
||||
scenario_save_network(rw_buffer, objects);
|
||||
gUseRLE = RLEState;
|
||||
int size = (int)SDL_RWtell(rw_buffer);
|
||||
std::vector<uint8> buffer(size);
|
||||
SDL_RWseek(rw_buffer, 0, RW_SEEK_SET);
|
||||
if (SDL_RWread(rw_buffer, &buffer[0], size, 1) == 0) {
|
||||
log_warning("Failed to read temporary map file into memory.");
|
||||
return nullptr;
|
||||
}
|
||||
unsigned char *compressed = util_zlib_deflate(&buffer[0], size, &out_size);
|
||||
if (compressed != NULL)
|
||||
{
|
||||
header = (unsigned char *)_strdup("open2_sv6_zlib");
|
||||
size_t header_len = strlen((char *)header) + 1; // account for null terminator
|
||||
header = (unsigned char *)realloc(header, header_len + out_size);
|
||||
if (header == nullptr) {
|
||||
log_error("Failed to allocate %u bytes.", header_len + out_size);
|
||||
} else {
|
||||
memcpy(&header[header_len], compressed, out_size);
|
||||
out_size += header_len;
|
||||
log_verbose("Sending map of size %u bytes, compressed to %u bytes", size, out_size);
|
||||
}
|
||||
free(compressed);
|
||||
} else {
|
||||
log_warning("Failed to compress the data, falling back to non-compressed sv6.");
|
||||
header = (unsigned char *)malloc(size);
|
||||
if (header == nullptr) {
|
||||
log_error("Failed to allocate %u bytes.", size);
|
||||
} else {
|
||||
out_size = size;
|
||||
memcpy(header, &buffer[0], size);
|
||||
}
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
void Network::Client_Send_CHAT(const char* text)
|
||||
@@ -1418,10 +1456,8 @@ void Network::Server_Client_Joined(const char* name, const std::string &keyhash,
|
||||
const char * player_name = (const char *) player->name.c_str();
|
||||
format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_JOINED_THE_GAME, &player_name);
|
||||
chat_history_add(text);
|
||||
Server_Send_MAP(&connection);
|
||||
gNetwork.Server_Send_EVENT_PLAYER_JOINED(player_name);
|
||||
Server_Send_GROUPLIST(connection);
|
||||
Server_Send_PLAYERLIST();
|
||||
std::vector<const ObjectRepositoryItem *> objects = scenario_get_packable_objects();
|
||||
Server_Send_OBJECTS(connection, objects);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1435,6 +1471,62 @@ void Network::Server_Handle_TOKEN(NetworkConnection& connection, NetworkPacket&
|
||||
Server_Send_TOKEN(connection);
|
||||
}
|
||||
|
||||
void Network::Client_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet)
|
||||
{
|
||||
IObjectRepository * repo = GetObjectRepository();
|
||||
uint32 size;
|
||||
packet >> size;
|
||||
log_verbose("client received object list, it has %u entries", size);
|
||||
std::vector<std::string> requested_objects;
|
||||
for (uint32 i = 0; i < size; i++)
|
||||
{
|
||||
const char * name = (const char *)packet.Read(8);
|
||||
// Required, as packet has no null terminators.
|
||||
std::string s(name, name + 8);
|
||||
uint32 checksum, flags;
|
||||
packet >> checksum >> flags;
|
||||
const ObjectRepositoryItem * ori = repo->FindObject(s.c_str());
|
||||
// This could potentially request the object if checksums don't match, but since client
|
||||
// won't replace its version with server-provided one, we don't do that.
|
||||
if (ori == nullptr) {
|
||||
log_verbose("Requesting object %s with checksum %x from server",
|
||||
s.c_str(), checksum);
|
||||
requested_objects.push_back(s);
|
||||
} else if (ori->ObjectEntry.checksum != checksum || ori->ObjectEntry.flags != flags) {
|
||||
log_warning("Object %s has different checksum/flags (%x/%x) than server (%x/%x).",
|
||||
s.c_str(), ori->ObjectEntry.checksum, ori->ObjectEntry.flags, checksum, flags);
|
||||
}
|
||||
}
|
||||
Client_Send_OBJECTS(requested_objects);
|
||||
}
|
||||
|
||||
void Network::Server_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet)
|
||||
{
|
||||
uint32 size;
|
||||
packet >> size;
|
||||
log_verbose("Client requested %u objects", size);
|
||||
IObjectRepository * repo = GetObjectRepository();
|
||||
for (uint32 i = 0; i < size; i++)
|
||||
{
|
||||
const char * name = (const char *)packet.Read(8);
|
||||
// This is required, as packet does not have null terminator
|
||||
std::string s(name, name + 8);
|
||||
log_verbose("Client requested object %s", s.c_str());
|
||||
const ObjectRepositoryItem * item = repo->FindObject(s.c_str());
|
||||
if (item == nullptr) {
|
||||
log_warning("Client tried getting non-existent object %s from us.", s.c_str());
|
||||
} else {
|
||||
connection.RequestedObjects.push_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
const char * player_name = (const char *) connection.Player->name.c_str();
|
||||
Server_Send_MAP(&connection);
|
||||
gNetwork.Server_Send_EVENT_PLAYER_JOINED(player_name);
|
||||
Server_Send_GROUPLIST(connection);
|
||||
Server_Send_PLAYERLIST();
|
||||
}
|
||||
|
||||
void Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& packet)
|
||||
{
|
||||
if (connection.AuthStatus != NETWORK_AUTH_OK) {
|
||||
|
||||
@@ -55,7 +55,7 @@ extern "C" {
|
||||
// This define 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 "15"
|
||||
#define NETWORK_STREAM_VERSION "16"
|
||||
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -83,6 +83,8 @@ enum {
|
||||
NETWORK_TICK_FLAG_CHECKSUMS = 1 << 0,
|
||||
};
|
||||
|
||||
struct ObjectRepositoryItem;
|
||||
|
||||
class Network
|
||||
{
|
||||
public:
|
||||
@@ -142,6 +144,8 @@ public:
|
||||
void Server_Send_EVENT_PLAYER_JOINED(const char *playerName);
|
||||
void Server_Send_EVENT_PLAYER_DISCONNECTED(const char *playerName, const char *reason);
|
||||
void Client_Send_GAMEINFO();
|
||||
void Client_Send_OBJECTS(const std::vector<std::string> &objects);
|
||||
void Server_Send_OBJECTS(NetworkConnection& connection, const std::vector<const ObjectRepositoryItem *> &objects) const;
|
||||
|
||||
std::vector<std::unique_ptr<NetworkPlayer>> player_list;
|
||||
std::vector<std::unique_ptr<NetworkGroup>> group_list;
|
||||
@@ -236,6 +240,10 @@ private:
|
||||
void Client_Handle_EVENT(NetworkConnection& connection, NetworkPacket& packet);
|
||||
void Client_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& packet);
|
||||
void Server_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& packet);
|
||||
void Client_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet);
|
||||
void Server_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet);
|
||||
|
||||
unsigned char * save_for_network(SDL_RWops *buffer, size_t &out_size, const std::vector<const ObjectRepositoryItem *> &objects) const;
|
||||
};
|
||||
|
||||
namespace Convert
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "../core/Exception.hpp"
|
||||
#include "../core/IStream.hpp"
|
||||
#include "../core/String.hpp"
|
||||
#include "../object/ObjectRepository.h"
|
||||
#include "../object/Object.h"
|
||||
#include "S6Exporter.h"
|
||||
|
||||
extern "C"
|
||||
@@ -46,7 +48,6 @@ extern "C"
|
||||
|
||||
S6Exporter::S6Exporter()
|
||||
{
|
||||
ExportObjects = false;
|
||||
RemoveTracklessRides = false;
|
||||
memset(&_s6, 0, sizeof(_s6));
|
||||
}
|
||||
@@ -90,7 +91,7 @@ void S6Exporter::SaveScenario(SDL_RWops *rw)
|
||||
void S6Exporter::Save(SDL_RWops * rw, bool isScenario)
|
||||
{
|
||||
_s6.header.type = isScenario ? S6_TYPE_SCENARIO : S6_TYPE_SAVEDGAME;
|
||||
_s6.header.num_packed_objects = ExportObjects ? scenario_get_num_packed_objects_to_write() : 0;
|
||||
_s6.header.num_packed_objects = uint16(ExportObjectsList.size());
|
||||
_s6.header.version = S6_RCT2_VERSION;
|
||||
_s6.header.magic_number = S6_MAGIC_NUMBER;
|
||||
|
||||
@@ -121,10 +122,11 @@ void S6Exporter::Save(SDL_RWops * rw, bool isScenario)
|
||||
SDL_RWwrite(rw, buffer, encodedLength, 1);
|
||||
}
|
||||
|
||||
log_warning("exporting %u objects", _s6.header.num_packed_objects);
|
||||
// 2: Write packed objects
|
||||
if (_s6.header.num_packed_objects > 0)
|
||||
{
|
||||
if (!scenario_write_packed_objects(rw))
|
||||
if (!scenario_write_packed_objects(rw, ExportObjectsList))
|
||||
{
|
||||
free(buffer);
|
||||
throw Exception("Unable to pack objects.");
|
||||
@@ -330,7 +332,7 @@ void S6Exporter::Export()
|
||||
_s6.current_expenditure = gCurrentExpenditure;
|
||||
_s6.current_profit = gCurrentProfit;
|
||||
_s6.weekly_profit_average_dividend = gWeeklyProfitAverageDividend;
|
||||
_s6.weekly_profit_average_divisor = gWeeklyProfitAverageDivisor;
|
||||
_s6.weekly_profit_average_divisor = gWeeklyProfitAverageDivisor;
|
||||
// pad_0135833A
|
||||
|
||||
memcpy(_s6.weekly_profit_history, gWeeklyProfitHistory, sizeof(_s6.weekly_profit_history));
|
||||
@@ -451,6 +453,112 @@ uint32 S6Exporter::GetLoanHash(money32 initialCash, money32 bankLoan, uint32 max
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
// Save game state without modifying any of the state for multiplayer
|
||||
int scenario_save_network(SDL_RWops * rw, const std::vector<const ObjectRepositoryItem *> &objects)
|
||||
{
|
||||
viewport_set_saved_view();
|
||||
|
||||
bool result = false;
|
||||
auto s6exporter = new S6Exporter();
|
||||
try
|
||||
{
|
||||
s6exporter->ExportObjectsList = objects;
|
||||
s6exporter->Export();
|
||||
s6exporter->SaveGame(rw);
|
||||
result = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
delete s6exporter;
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write other data not in normal save files
|
||||
SDL_RWwrite(rw, gSpriteSpatialIndex, 0x10001 * sizeof(uint16), 1);
|
||||
SDL_WriteLE32(rw, gGamePaused);
|
||||
SDL_WriteLE32(rw, _guestGenerationProbability);
|
||||
SDL_WriteLE32(rw, _suggestedGuestMaximum);
|
||||
SDL_WriteU8(rw, gCheatsSandboxMode);
|
||||
SDL_WriteU8(rw, gCheatsDisableClearanceChecks);
|
||||
SDL_WriteU8(rw, gCheatsDisableSupportLimits);
|
||||
SDL_WriteU8(rw, gCheatsDisableTrainLengthLimit);
|
||||
SDL_WriteU8(rw, gCheatsEnableChainLiftOnAllTrack);
|
||||
SDL_WriteU8(rw, gCheatsShowAllOperatingModes);
|
||||
SDL_WriteU8(rw, gCheatsShowVehiclesFromOtherTrackTypes);
|
||||
SDL_WriteU8(rw, gCheatsFastLiftHill);
|
||||
SDL_WriteU8(rw, gCheatsDisableBrakesFailure);
|
||||
SDL_WriteU8(rw, gCheatsDisableAllBreakdowns);
|
||||
SDL_WriteU8(rw, gCheatsUnlockAllPrices);
|
||||
SDL_WriteU8(rw, gCheatsBuildInPauseMode);
|
||||
SDL_WriteU8(rw, gCheatsIgnoreRideIntensity);
|
||||
SDL_WriteU8(rw, gCheatsDisableVandalism);
|
||||
SDL_WriteU8(rw, gCheatsDisableLittering);
|
||||
SDL_WriteU8(rw, gCheatsNeverendingMarketing);
|
||||
SDL_WriteU8(rw, gCheatsFreezeClimate);
|
||||
SDL_WriteU8(rw, gCheatsDisablePlantAging);
|
||||
SDL_WriteU8(rw, gCheatsAllowArbitraryRideTypeChanges);
|
||||
|
||||
gfx_invalidate_screen();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool object_is_custom(const ObjectRepositoryItem * object)
|
||||
{
|
||||
Guard::ArgumentNotNull(object);
|
||||
|
||||
// Validate the object is not one from base game or expansion pack
|
||||
return (object->LoadedObject != nullptr &&
|
||||
object->LoadedObject->GetLegacyData() != nullptr
|
||||
&& !(object->ObjectEntry.flags & 0xF0));
|
||||
}
|
||||
|
||||
int scenario_write_packed_objects(SDL_RWops* rw, std::vector<const ObjectRepositoryItem *> &objects)
|
||||
{
|
||||
log_verbose("exporting packed objects");
|
||||
for (const auto &object : objects)
|
||||
{
|
||||
Guard::ArgumentNotNull(object);
|
||||
log_verbose("exporting object %.8s", object->ObjectEntry.name);
|
||||
if (object_is_custom(object))
|
||||
{
|
||||
if (!object_saved_packed(rw, &object->ObjectEntry))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log_warning("Refusing to pack vanilla/expansion object \"%s\"", object->ObjectEntry.name);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006AA244
|
||||
*/
|
||||
std::vector<const ObjectRepositoryItem *> scenario_get_packable_objects()
|
||||
{
|
||||
std::vector<const ObjectRepositoryItem *> objects;
|
||||
IObjectRepository * repo = GetObjectRepository();
|
||||
for (size_t i = 0; i < repo->GetNumObjects(); i++)
|
||||
{
|
||||
const ObjectRepositoryItem *item = &repo->GetObjects()[i];
|
||||
// Validate the object is not one from base game or expansion pack
|
||||
if (object_is_custom(item))
|
||||
{
|
||||
objects.push_back(item);
|
||||
}
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
enum {
|
||||
@@ -489,7 +597,9 @@ extern "C"
|
||||
auto s6exporter = new S6Exporter();
|
||||
try
|
||||
{
|
||||
s6exporter->ExportObjects = (flags & S6_SAVE_FLAG_EXPORT);
|
||||
if (flags & S6_SAVE_FLAG_EXPORT) {
|
||||
s6exporter->ExportObjectsList = scenario_get_packable_objects();
|
||||
}
|
||||
s6exporter->RemoveTracklessRides = true;
|
||||
s6exporter->Export();
|
||||
if (flags & S6_SAVE_FLAG_SCENARIO)
|
||||
@@ -515,57 +625,4 @@ extern "C"
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Save game state without modifying any of the state for multiplayer
|
||||
int scenario_save_network(SDL_RWops * rw)
|
||||
{
|
||||
viewport_set_saved_view();
|
||||
|
||||
bool result = false;
|
||||
auto s6exporter = new S6Exporter();
|
||||
try
|
||||
{
|
||||
s6exporter->ExportObjects = true;
|
||||
s6exporter->Export();
|
||||
s6exporter->SaveGame(rw);
|
||||
result = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
delete s6exporter;
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write other data not in normal save files
|
||||
SDL_RWwrite(rw, gSpriteSpatialIndex, 0x10001 * sizeof(uint16), 1);
|
||||
SDL_WriteLE32(rw, gGamePaused);
|
||||
SDL_WriteLE32(rw, _guestGenerationProbability);
|
||||
SDL_WriteLE32(rw, _suggestedGuestMaximum);
|
||||
SDL_WriteU8(rw, gCheatsSandboxMode);
|
||||
SDL_WriteU8(rw, gCheatsDisableClearanceChecks);
|
||||
SDL_WriteU8(rw, gCheatsDisableSupportLimits);
|
||||
SDL_WriteU8(rw, gCheatsDisableTrainLengthLimit);
|
||||
SDL_WriteU8(rw, gCheatsEnableChainLiftOnAllTrack);
|
||||
SDL_WriteU8(rw, gCheatsShowAllOperatingModes);
|
||||
SDL_WriteU8(rw, gCheatsShowVehiclesFromOtherTrackTypes);
|
||||
SDL_WriteU8(rw, gCheatsFastLiftHill);
|
||||
SDL_WriteU8(rw, gCheatsDisableBrakesFailure);
|
||||
SDL_WriteU8(rw, gCheatsDisableAllBreakdowns);
|
||||
SDL_WriteU8(rw, gCheatsUnlockAllPrices);
|
||||
SDL_WriteU8(rw, gCheatsBuildInPauseMode);
|
||||
SDL_WriteU8(rw, gCheatsIgnoreRideIntensity);
|
||||
SDL_WriteU8(rw, gCheatsDisableVandalism);
|
||||
SDL_WriteU8(rw, gCheatsDisableLittering);
|
||||
SDL_WriteU8(rw, gCheatsNeverendingMarketing);
|
||||
SDL_WriteU8(rw, gCheatsFreezeClimate);
|
||||
SDL_WriteU8(rw, gCheatsDisablePlantAging);
|
||||
SDL_WriteU8(rw, gCheatsAllowArbitraryRideTypeChanges);
|
||||
|
||||
gfx_invalidate_screen();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
extern "C"
|
||||
@@ -24,14 +27,20 @@ extern "C"
|
||||
#include "../object_list.h"
|
||||
}
|
||||
|
||||
struct ObjectRepositoryItem;
|
||||
|
||||
int scenario_save_network(SDL_RWops* rw, const std::vector<const ObjectRepositoryItem *> &objects);
|
||||
int scenario_write_packed_objects(SDL_RWops* rw, std::vector<const ObjectRepositoryItem *> &objects);
|
||||
std::vector<const ObjectRepositoryItem *> scenario_get_packable_objects();
|
||||
|
||||
/**
|
||||
* Class to export RollerCoaster Tycoon 2 scenarios (*.SC6) and saved games (*.SV6).
|
||||
*/
|
||||
class S6Exporter final
|
||||
{
|
||||
public:
|
||||
bool ExportObjects;
|
||||
bool RemoveTracklessRides;
|
||||
std::vector<const ObjectRepositoryItem *> ExportObjectsList;
|
||||
|
||||
S6Exporter();
|
||||
|
||||
|
||||
@@ -685,41 +685,6 @@ int scenario_prepare_for_save()
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006AA244
|
||||
*/
|
||||
int scenario_get_num_packed_objects_to_write()
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) {
|
||||
const rct_object_entry *entry = get_loaded_object_entry(i);
|
||||
void *entryData = get_loaded_object_chunk(i);
|
||||
if (entryData != (void*)-1 && !(entry->flags & 0xF0)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006AA26E
|
||||
*/
|
||||
int scenario_write_packed_objects(SDL_RWops* rw)
|
||||
{
|
||||
for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) {
|
||||
const rct_object_entry *entry = get_loaded_object_entry(i);
|
||||
void *entryData = get_loaded_object_chunk(i);
|
||||
if (entryData != (void*)-1 && !(entry->flags & 0xF0)) {
|
||||
if (!object_saved_packed(rw, entry)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006AA039
|
||||
|
||||
@@ -416,9 +416,6 @@ unsigned int scenario_rand();
|
||||
unsigned int scenario_rand_max(unsigned int max);
|
||||
int scenario_prepare_for_save();
|
||||
int scenario_save(SDL_RWops* rw, int flags);
|
||||
int scenario_save_network(SDL_RWops* rw);
|
||||
int scenario_get_num_packed_objects_to_write();
|
||||
int scenario_write_packed_objects(SDL_RWops* rw);
|
||||
void scenario_remove_trackless_rides(rct_s6_data *s6);
|
||||
void scenario_fix_ghosts(rct_s6_data *s6);
|
||||
void scenario_set_filename(const char *value);
|
||||
|
||||
Reference in New Issue
Block a user