diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 1c99185aad..a5fa408280 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -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) { diff --git a/src/openttd.cpp b/src/openttd.cpp index 5525c1b2b0..9bfb939660 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -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 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(...) { \ diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 86d30a98a3..115bf42ea0 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -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; diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index d391e2b486..e0cd50ce06 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -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 }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 627f0ce0bc..b349ae2fe5 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -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 diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 00f3e85900..ae6615ed93 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -43,6 +43,7 @@ #include "../string_func_extra.h" #include "../fios.h" #include "../error.h" +#include "../scope.h" #include #include @@ -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); diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 081e2226a9..07830ffb73 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -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; diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index c81d9eb6d8..9248b7fb38 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -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_vencs; +static std::vector _train_vencs; +static std::vector _roadvehicle_vencs; +static std::vector _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 +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}, };