Send vehicle caches to network clients to prevent desyncs due to bad GRFs

pull/206/head
Jonathan G Rennison 4 years ago
parent 4ba11fa822
commit 1f54c8a018

@ -617,7 +617,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap()
sent_packets = 4; // We start with trying 4 packets
/* Make a dump of the current game */
if (SaveWithFilter(this->savegame, true) != SL_OK) usererror("network savedump failed");
if (SaveWithFilter(this->savegame, true, true) != SL_OK) usererror("network savedump failed");
}
if (this->status == STATUS_MAP) {

@ -1331,6 +1331,22 @@ void SwitchToMode(SwitchMode new_mode)
SmallMapWindow::RebuildColourIndexIfNecessary();
}
void WriteVehicleInfo(char *&p, const char *last, const Vehicle *u, const Vehicle *v, uint length)
{
p += seprintf(p, last, ": type %i, vehicle %i (%i), company %i, unit number %i, wagon %i, engine: ",
(int)u->type, u->index, v->index, (int)u->owner, v->unitnumber, length);
SetDParam(0, u->engine_type);
p = GetString(p, STR_ENGINE_NAME, last);
uint32 grfid = u->GetGRFID();
if (grfid) {
p += seprintf(p, last, ", GRF: %08X", BSWAP32(grfid));
GRFConfig *grfconfig = GetGRFConfig(grfid);
if (grfconfig) {
p += seprintf(p, last, ", %s, %s", grfconfig->GetName(), grfconfig->filename);
}
}
}
/**
* Check the validity of some of the caches.
* Especially in the sense of desyncs between
@ -1359,18 +1375,7 @@ void CheckCaches(bool force_check, std::function<void(const char *)> log)
}
auto output_veh_info = [&](char *&p, const Vehicle *u, const Vehicle *v, uint length) {
p += seprintf(p, lastof(cclog_buffer), ": type %i, vehicle %i (%i), company %i, unit number %i, wagon %i, engine: ",
(int)u->type, u->index, v->index, (int)u->owner, v->unitnumber, length);
SetDParam(0, u->engine_type);
p = GetString(p, STR_ENGINE_NAME, lastof(cclog_buffer));
uint32 grfid = u->GetGRFID();
if (grfid) {
p += seprintf(p, lastof(cclog_buffer), ", GRF: %08X", BSWAP32(grfid));
GRFConfig *grfconfig = GetGRFConfig(grfid);
if (grfconfig) {
p += seprintf(p, lastof(cclog_buffer), ", %s, %s", grfconfig->GetName(), grfconfig->filename);
}
}
WriteVehicleInfo(p, lastof(cclog_buffer), u, v, length);
};
#define CCLOGV(...) { \

@ -3840,6 +3840,10 @@ bool AfterLoadGame()
extern void YapfCheckRailSignalPenalties();
YapfCheckRailSignalPenalties();
if (_networking && !_network_server) {
SlProcessVENC();
}
/* Show this message last to avoid covering up an error message if we bail out part way */
switch (gcf_res) {
case GLC_COMPATIBLE: ShowErrorMessage(STR_NEWGRF_COMPATIBLE_LOAD_WARNING, INVALID_STRING_ID, WL_CRITICAL); break;

@ -138,6 +138,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_ORDER_FLAGS_EXTRA, XSCF_NULL, 1, 1, "order_flags_extra", nullptr, nullptr, nullptr },
{ XSLFI_ONE_WAY_DT_ROAD_STOP, XSCF_NULL, 1, 1, "one_way_dt_road_stop", nullptr, nullptr, nullptr },
{ XSLFI_ONE_WAY_ROAD_STATE, XSCF_NULL, 1, 1, "one_way_road_state", nullptr, nullptr, nullptr },
{ XSLFI_VENC_CHUNK, XSCF_IGNORABLE_ALL, 1, 1, "venc_chunk", nullptr, nullptr, "VENC" },
{ XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker
};

@ -92,6 +92,7 @@ enum SlXvFeatureIndex {
XSLFI_ORDER_FLAGS_EXTRA, ///< Order flags field extra size
XSLFI_ONE_WAY_DT_ROAD_STOP, ///< One-way drive-through road stops
XSLFI_ONE_WAY_ROAD_STATE, ///< One-way road state cache
XSLFI_VENC_CHUNK, ///< VENC chunk
XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit
XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk

@ -43,6 +43,7 @@
#include "../string_func_extra.h"
#include "../fios.h"
#include "../error.h"
#include "../scope.h"
#include <atomic>
#include <string>
@ -226,6 +227,7 @@ struct SaveLoadParams {
byte ff_state; ///< The state of fast-forward when saving started.
bool saveinprogress; ///< Whether there is currently a save in progress.
bool networkserversave; ///< Whether this save is being sent to a network client
};
static SaveLoadParams _sl; ///< Parameters used for/at saveload.
@ -2896,6 +2898,8 @@ static inline void ClearSaveLoadState()
delete _sl.lf;
_sl.lf = nullptr;
_sl.networkserversave = false;
GamelogStopAnyAction();
}
@ -3042,12 +3046,14 @@ static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded)
* Save the game using a (writer) filter.
* @param writer The filter to write the savegame to.
* @param threaded Whether to try to perform the saving asynchronously.
* @param networkserversave Whether this is a network server save.
* @return Return the result of the action. #SL_OK or #SL_ERROR
*/
SaveOrLoadResult SaveWithFilter(SaveFilter *writer, bool threaded)
SaveOrLoadResult SaveWithFilter(SaveFilter *writer, bool threaded, bool networkserversave)
{
try {
_sl.action = SLA_SAVE;
_sl.networkserversave = networkserversave;
return DoSave(writer, threaded);
} catch (...) {
ClearSaveLoadState();
@ -3055,6 +3061,11 @@ SaveOrLoadResult SaveWithFilter(SaveFilter *writer, bool threaded)
}
}
bool IsNetworkServerSave()
{
return _sl.networkserversave;
}
struct ThreadedLoadFilter : LoadFilter {
static const size_t BUFFER_COUNT = 4;
@ -3179,6 +3190,10 @@ static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
}
SlXvResetState();
SlResetVENC();
auto guard = scope_guard([&]() {
SlResetVENC();
});
uint32 hdr[2];
if (_sl.lf->Read((byte*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
@ -3396,6 +3411,7 @@ SaveOrLoadResult SaveOrLoad(const char *filename, SaveLoadOperation fop, Detaile
default: NOT_REACHED();
}
_sl.networkserversave = false;
FILE *fh = (fop == SLO_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);

@ -376,8 +376,9 @@ void WaitTillSaved();
void ProcessAsyncSaveFinish();
void DoExitSave();
SaveOrLoadResult SaveWithFilter(struct SaveFilter *writer, bool threaded);
SaveOrLoadResult SaveWithFilter(struct SaveFilter *writer, bool threaded, bool networkserversave);
SaveOrLoadResult LoadWithFilter(struct LoadFilter *reader);
bool IsNetworkServerSave();
typedef void ChunkSaveLoadProc();
typedef void AutolengthProc(void *arg);
@ -1085,6 +1086,9 @@ void NORETURN CDECL SlErrorCorruptFmt(const char *format, ...) WARN_FORMAT(1, 2)
bool SaveloadCrashWithMissingNewGRFs();
void SlResetVENC();
void SlProcessVENC();
extern char _savegame_format[8];
extern bool _do_autosave;

@ -1137,8 +1137,244 @@ void Load_VESR()
}
}
struct vehicle_venc {
VehicleID id;
VehicleCache vcache;
};
struct train_venc {
VehicleID id;
GroundVehicleCache gvcache;
bool cached_tilt;
uint8 cached_num_engines;
byte user_def_data;
int cached_max_curve_speed;
};
struct roadvehicle_venc {
VehicleID id;
GroundVehicleCache gvcache;
};
struct aircraft_venc {
VehicleID id;
uint16 cached_max_range;
};
static std::vector<vehicle_venc> _vehicle_vencs;
static std::vector<train_venc> _train_vencs;
static std::vector<roadvehicle_venc> _roadvehicle_vencs;
static std::vector<aircraft_venc> _aircraft_vencs;
void Save_VENC()
{
if (!IsNetworkServerSave()) {
SlSetLength(0);
return;
}
SlAutolength([](void *) {
int types[4] = {};
int total = 0;
for (Vehicle *v : Vehicle::Iterate()) {
total++;
if (v->type < VEH_COMPANY_END) types[v->type]++;
}
/* vehicle cache */
SlWriteUint32(total);
for (Vehicle *v : Vehicle::Iterate()) {
SlWriteUint32(v->index);
SlWriteUint16(v->vcache.cached_max_speed);
SlWriteUint16(v->vcache.cached_cargo_age_period);
SlWriteByte(v->vcache.cached_vis_effect);
SlWriteByte(v->vcache.cached_veh_flags);
}
auto write_gv_cache = [&](const GroundVehicleCache &cache) {
SlWriteUint32(cache.cached_weight);
SlWriteUint32(cache.cached_slope_resistance);
SlWriteUint32(cache.cached_max_te);
SlWriteUint32(cache.cached_axle_resistance);
SlWriteUint32(cache.cached_max_track_speed);
SlWriteUint32(cache.cached_power);
SlWriteUint32(cache.cached_air_drag);
SlWriteUint16(cache.cached_total_length);
SlWriteUint16(cache.first_engine);
SlWriteByte(cache.cached_veh_length);
};
/* train */
SlWriteUint32(types[VEH_TRAIN]);
for (Train *t : Train::Iterate()) {
SlWriteUint32(t->index);
write_gv_cache(t->gcache);
SlWriteByte(t->tcache.cached_tilt);
SlWriteByte(t->tcache.cached_num_engines);
SlWriteByte(t->tcache.user_def_data);
SlWriteUint32(t->tcache.cached_max_curve_speed);
}
/* road vehicle */
SlWriteUint32(types[VEH_ROAD]);
for (RoadVehicle *rv : RoadVehicle::Iterate()) {
SlWriteUint32(rv->index);
write_gv_cache(rv->gcache);
}
/* aircraft */
SlWriteUint32(types[VEH_AIRCRAFT]);
for (Aircraft *a : Aircraft::Iterate()) {
SlWriteUint32(a->index);
SlWriteUint16(a->acache.cached_max_range);
}
}, nullptr);
}
void Load_VENC()
{
if (SlGetFieldLength() == 0) return;
if (!_networking || _network_server) {
SlSkipBytes(SlGetFieldLength());
return;
}
_vehicle_vencs.resize(SlReadUint32());
for (vehicle_venc &venc : _vehicle_vencs) {
venc.id = SlReadUint32();
venc.vcache.cached_max_speed = SlReadUint16();
venc.vcache.cached_cargo_age_period = SlReadUint16();
venc.vcache.cached_vis_effect = SlReadByte();
venc.vcache.cached_veh_flags = SlReadByte();
}
auto read_gv_cache = [&](GroundVehicleCache &cache) {
cache.cached_weight = SlReadUint32();
cache.cached_slope_resistance = SlReadUint32();
cache.cached_max_te = SlReadUint32();
cache.cached_axle_resistance = SlReadUint32();
cache.cached_max_track_speed = SlReadUint32();
cache.cached_power = SlReadUint32();
cache.cached_air_drag = SlReadUint32();
cache.cached_total_length = SlReadUint16();
cache.first_engine = SlReadUint16();
cache.cached_veh_length = SlReadByte();
};
_train_vencs.resize(SlReadUint32());
for (train_venc &venc : _train_vencs) {
venc.id = SlReadUint32();
read_gv_cache(venc.gvcache);
venc.cached_tilt = SlReadByte();
venc.cached_num_engines = SlReadByte();
venc.user_def_data = SlReadByte();
venc.cached_max_curve_speed = SlReadUint32();
}
_roadvehicle_vencs.resize(SlReadUint32());
for (roadvehicle_venc &venc : _roadvehicle_vencs) {
venc.id = SlReadUint32();
read_gv_cache(venc.gvcache);
}
_aircraft_vencs.resize(SlReadUint32());
for (aircraft_venc &venc : _aircraft_vencs) {
venc.id = SlReadUint32();
venc.cached_max_range = SlReadUint16();
}
}
void SlResetVENC()
{
_vehicle_vencs.clear();
_train_vencs.clear();
_roadvehicle_vencs.clear();
_aircraft_vencs.clear();
}
static void LogVehicleVENCMessage(const Vehicle *v, const char *var)
{
char log_buffer[1024];
char *p = log_buffer + seprintf(log_buffer, lastof(log_buffer), "[load]: vehicle cache mismatch: %s", var);
extern void WriteVehicleInfo(char *&p, const char *last, const Vehicle *u, const Vehicle *v, uint length);
uint length = 0;
for (const Vehicle *u = v->First(); u != v; u = u->Next()) {
length++;
}
WriteVehicleInfo(p, lastof(log_buffer), v, v->First(), length);
DEBUG(desync, 0, "%s", log_buffer);
LogDesyncMsg(log_buffer);
}
template <typename T>
void CheckVehicleVENCProp(T &v_prop, T venc_prop, const Vehicle *v, const char *var)
{
if (v_prop != venc_prop) {
v_prop = venc_prop;
LogVehicleVENCMessage(v, var);
}
}
void SlProcessVENC()
{
for (const vehicle_venc &venc : _vehicle_vencs) {
Vehicle *v = Vehicle::GetIfValid(venc.id);
if (v == nullptr) continue;
CheckVehicleVENCProp(v->vcache.cached_max_speed, venc.vcache.cached_max_speed, v, "cached_max_speed");
CheckVehicleVENCProp(v->vcache.cached_cargo_age_period, venc.vcache.cached_cargo_age_period, v, "cached_cargo_age_period");
CheckVehicleVENCProp(v->vcache.cached_vis_effect, venc.vcache.cached_vis_effect, v, "cached_vis_effect");
if (HasBit(v->vcache.cached_veh_flags ^ venc.vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT)) {
SB(v->vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT, 1, HasBit(venc.vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT) ? 1 : 0);
LogVehicleVENCMessage(v, "VCF_LAST_VISUAL_EFFECT");
}
}
auto check_gv_cache = [&](GroundVehicleCache &v_gvcache, const GroundVehicleCache &venc_gvcache, const Vehicle *v) {
CheckVehicleVENCProp(v_gvcache.cached_weight, venc_gvcache.cached_weight, v, "cached_weight");
CheckVehicleVENCProp(v_gvcache.cached_slope_resistance, venc_gvcache.cached_slope_resistance, v, "cached_slope_resistance");
CheckVehicleVENCProp(v_gvcache.cached_max_te, venc_gvcache.cached_max_te, v, "cached_max_te");
CheckVehicleVENCProp(v_gvcache.cached_axle_resistance, venc_gvcache.cached_axle_resistance, v, "cached_axle_resistance");
CheckVehicleVENCProp(v_gvcache.cached_max_track_speed, venc_gvcache.cached_max_track_speed, v, "cached_max_track_speed");
CheckVehicleVENCProp(v_gvcache.cached_power, venc_gvcache.cached_power, v, "cached_power");
CheckVehicleVENCProp(v_gvcache.cached_air_drag, venc_gvcache.cached_air_drag, v, "cached_air_drag");
CheckVehicleVENCProp(v_gvcache.cached_total_length, venc_gvcache.cached_total_length, v, "cached_total_length");
CheckVehicleVENCProp(v_gvcache.first_engine, venc_gvcache.first_engine, v, "first_engine");
CheckVehicleVENCProp(v_gvcache.cached_veh_length, venc_gvcache.cached_veh_length, v, "cached_veh_length");
};
for (const train_venc &venc : _train_vencs) {
Train *t = Train::GetIfValid(venc.id);
if (t == nullptr) continue;
check_gv_cache(t->gcache, venc.gvcache, t);
CheckVehicleVENCProp(t->tcache.cached_tilt, venc.cached_tilt, t, "cached_tilt");
CheckVehicleVENCProp(t->tcache.cached_num_engines, venc.cached_num_engines, t, "cached_num_engines");
CheckVehicleVENCProp(t->tcache.user_def_data, venc.user_def_data, t, "user_def_data");
CheckVehicleVENCProp(t->tcache.cached_max_curve_speed, venc.cached_max_curve_speed, t, "cached_max_curve_speed");
}
for (const roadvehicle_venc &venc : _roadvehicle_vencs) {
RoadVehicle *rv = RoadVehicle::GetIfValid(venc.id);
if (rv == nullptr) continue;
check_gv_cache(rv->gcache, venc.gvcache, rv);
}
for (const aircraft_venc &venc : _aircraft_vencs) {
Aircraft *a = Aircraft::GetIfValid(venc.id);
if (a == nullptr) continue;
if (a->acache.cached_max_range != venc.cached_max_range) {
a->acache.cached_max_range = venc.cached_max_range;
a->acache.cached_max_range_sqr = venc.cached_max_range * venc.cached_max_range;
LogVehicleVENCMessage(a, "cached_max_range");
}
}
}
extern const ChunkHandler _veh_chunk_handlers[] = {
{ 'VEHS', Save_VEHS, Load_VEHS, Ptrs_VEHS, nullptr, CH_SPARSE_ARRAY},
{ 'VEOX', Save_VEOX, Load_VEOX, nullptr, nullptr, CH_SPARSE_ARRAY},
{ 'VESR', Save_VESR, Load_VESR, nullptr, nullptr, CH_SPARSE_ARRAY | CH_LAST},
{ 'VESR', Save_VESR, Load_VESR, nullptr, nullptr, CH_SPARSE_ARRAY},
{ 'VENC', Save_VENC, Load_VENC, nullptr, nullptr, CH_RIFF | CH_LAST},
};

Loading…
Cancel
Save