diff --git a/src/misc.cpp b/src/misc.cpp index a0412c634c..95b2bd873b 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -120,6 +120,9 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin ClearCargoPacketDeferredPayments(); PoolBase::Clean(PT_NORMAL); + extern void ClearNewSignalStyleMapping(); + ClearNewSignalStyleMapping(); + RebuildStationKdtree(); RebuildTownKdtree(); RebuildViewportKdtree(); diff --git a/src/newgrf_newsignals.cpp b/src/newgrf_newsignals.cpp index 1e8865b550..9445747267 100644 --- a/src/newgrf_newsignals.cpp +++ b/src/newgrf_newsignals.cpp @@ -19,6 +19,7 @@ std::vector _new_signals_grfs; std::array _new_signal_styles; +std::array _new_signal_style_mapping; uint _num_new_signal_styles = 0; /* virtual */ uint32 NewSignalsScopeResolver::GetRandomBits() const diff --git a/src/newgrf_newsignals.h b/src/newgrf_newsignals.h index 881e95c381..21391cc661 100644 --- a/src/newgrf_newsignals.h +++ b/src/newgrf_newsignals.h @@ -47,6 +47,13 @@ struct NewSignalStyle { PalSpriteID signals[SIGTYPE_END][2][2]; }; extern std::array _new_signal_styles; +struct NewSignalStyleMapping { + uint32 grfid = 0; + uint8 grf_local_id = 0; + + inline bool operator==(const NewSignalStyleMapping& o) const { return grfid == o.grfid && grf_local_id == o.grf_local_id; } +}; +extern std::array _new_signal_style_mapping; extern uint _num_new_signal_styles; /** Resolver for the new signals scope. */ diff --git a/src/openttd.cpp b/src/openttd.cpp index cb84cf535f..05bb2d4622 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -447,6 +447,9 @@ static void ShutdownGame() FreeSignalPrograms(); FreeSignalDependencies(); + extern void ClearNewSignalStyleMapping(); + ClearNewSignalStyleMapping(); + extern void ClearAllSignalSpeedRestrictions(); ClearAllSignalSpeedRestrictions(); diff --git a/src/saveload/CMakeLists.txt b/src/saveload/CMakeLists.txt index 10141526e3..88f7e1ee16 100644 --- a/src/saveload/CMakeLists.txt +++ b/src/saveload/CMakeLists.txt @@ -28,6 +28,7 @@ add_files( misc_sl.cpp newgrf_sl.cpp newgrf_sl.h + newsignals_sl.cpp object_sl.cpp oldloader.cpp oldloader.h diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 11d2f276d3..47582eff5a 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -173,7 +173,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_RV_ORDER_EXTRA_FLAGS, XSCF_IGNORABLE_UNKNOWN, 1, 1, "rv_order_extra_flags", nullptr, nullptr, nullptr }, { XSLFI_GRF_ROADSTOPS, XSCF_NULL, 1, 1, "grf_road_stops", nullptr, nullptr, nullptr }, { XSLFI_INDUSTRY_ANIM_MASK, XSCF_IGNORABLE_ALL, 1, 1, "industry_anim_mask", nullptr, nullptr, nullptr }, - { XSLFI_NEW_SIGNAL_STYLES, XSCF_NULL, 1, 1, "new_signal_styles", nullptr, nullptr, "XBST" }, + { XSLFI_NEW_SIGNAL_STYLES, XSCF_NULL, 2, 2, "new_signal_styles", nullptr, nullptr, "XBST,NSID" }, { XSLFI_SCRIPT_INT64, XSCF_NULL, 1, 1, "script_int64", nullptr, nullptr, nullptr }, { XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker }; diff --git a/src/saveload/newsignals_sl.cpp b/src/saveload/newsignals_sl.cpp new file mode 100644 index 0000000000..620be20948 --- /dev/null +++ b/src/saveload/newsignals_sl.cpp @@ -0,0 +1,42 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file newsignals_sl.cpp Code handling saving and loading of new signals */ + +#include "../stdafx.h" +#include "../newgrf_newsignals.h" + +#include "saveload.h" + +#include "../safeguards.h" + +static void Save_NSID() +{ + SlSetLength(4 + (lengthof(_new_signal_style_mapping) * 5)); + SlWriteUint32(lengthof(_new_signal_style_mapping)); + for (const NewSignalStyleMapping &mapping : _new_signal_style_mapping) { + SlWriteUint32(mapping.grfid); + SlWriteByte(mapping.grf_local_id); + } +} + +static void Load_NSID() +{ + uint count = SlReadUint32(); + for (uint i = 0; i < count; i++) { + NewSignalStyleMapping mapping; + mapping.grfid = SlReadUint32(); + mapping.grf_local_id = SlReadByte(); + if (i < lengthof(_new_signal_style_mapping)) _new_signal_style_mapping[i] = mapping; + } +} + +static const ChunkHandler new_signal_chunk_handlers[] = { + { 'NSID', Save_NSID, Load_NSID, nullptr, nullptr, CH_RIFF }, +}; + +extern const ChunkHandlerTable _new_signal_chunk_handlers(new_signal_chunk_handlers); diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index df170a1ced..0c176449aa 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -300,6 +300,7 @@ static const std::vector &ChunkHandlers() extern const ChunkHandlerTable _bridge_signal_chunk_handlers; extern const ChunkHandlerTable _tunnel_chunk_handlers; extern const ChunkHandlerTable _train_speed_adaptation_chunk_handlers; + extern const ChunkHandlerTable _new_signal_chunk_handlers; extern const ChunkHandlerTable _debug_chunk_handlers; /** List of all chunks in a savegame. */ @@ -346,6 +347,7 @@ static const std::vector &ChunkHandlers() _bridge_signal_chunk_handlers, _tunnel_chunk_handlers, _train_speed_adaptation_chunk_handlers, + _new_signal_chunk_handlers, _debug_chunk_handlers, }; diff --git a/src/signal.cpp b/src/signal.cpp index 43bfebda73..80f22e3964 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -1503,6 +1503,79 @@ void UpdateAllSignalAspects() } } +void ClearNewSignalStyleMapping() +{ + _new_signal_style_mapping.fill({}); +} + +static bool RemapNewSignalStyles(const std::array &new_mapping) +{ + const std::array old_mapping = _new_signal_style_mapping; + _new_signal_style_mapping = new_mapping; + + uint8 remap_table[MAX_NEW_SIGNAL_STYLES + 1] = {}; + remap_table[0] = 0; + + uint8 next_free = _num_new_signal_styles; + + bool do_remap = false; + for (uint i = 0; i < MAX_NEW_SIGNAL_STYLES; i++) { + if (old_mapping[i].grfid == 0) { + remap_table[i + 1] = 0; + continue; + } + + auto find_target = [&]() { + for (uint j = 0; j < MAX_NEW_SIGNAL_STYLES; j++) { + if (old_mapping[i].grfid == _new_signal_style_mapping[j].grfid && old_mapping[i].grf_local_id == _new_signal_style_mapping[j].grf_local_id) { + remap_table[i + 1] = j + 1; + if (i != j) do_remap = true; + return; + } + } + if (next_free < MAX_NEW_SIGNAL_STYLES) { + remap_table[i + 1] = next_free + 1; + _new_signal_style_mapping[next_free].grfid = old_mapping[i].grfid; + _new_signal_style_mapping[next_free].grf_local_id = old_mapping[i].grf_local_id; + next_free++; + do_remap = true; + } else { + remap_table[i + 1] = 0; + do_remap = true; + } + }; + find_target(); + } + + if (do_remap) { + const TileIndex map_size = MapSize(); + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_RAILWAY) && HasSignals(t)) { + SetSignalStyle(t, TRACK_LOWER, remap_table[GetSignalStyle(t, TRACK_LOWER)]); + SetSignalStyle(t, TRACK_UPPER, remap_table[GetSignalStyle(t, TRACK_UPPER)]); + } + if (IsRailTunnelBridgeTile(t) && GetTunnelBridgeDirection(t) < DIAGDIR_SW) { + /* Only process west end of tunnel/bridge */ + uint8 old_style = GetTunnelBridgeSignalStyle(t); + uint8 new_style = remap_table[old_style]; + if (new_style != old_style) { + SetTunnelBridgeSignalStyle(t, GetOtherTunnelBridgeEnd(t), new_style); + } + } + } + } + + return do_remap; +} + +static void DetermineSignalStyleMapping(std::array &mapping) +{ + for (uint i = 0; i < _num_new_signal_styles; i++) { + mapping[i].grfid = _new_signal_styles[i].grffile->grfid; + mapping[i].grf_local_id = _new_signal_styles[i].grf_local_id; + } +} + static bool DetermineExtraAspectsVariable() { bool changed = false; @@ -1518,30 +1591,31 @@ static bool DetermineExtraAspectsVariable() for (const GRFFile *grf : _new_signals_grfs) { new_extra_aspects = std::max(new_extra_aspects, grf->new_signal_extra_aspects); } - for (uint i = 0; i < _num_new_signal_styles; i++) { - if (HasBit(_new_signal_styles[i].style_flags, NSSF_NO_ASPECT_INC)) { - SetBit(_signal_style_masks.non_aspect_inc, i + 1); - SetBit(_signal_style_masks.no_tunnel_bridge, i + 1); - } - if (HasBit(_new_signal_styles[i].style_flags, NSSF_ALWAYS_RESERVE_THROUGH)) { - SetBit(_signal_style_masks.always_reserve_through, i + 1); - SetBit(_signal_style_masks.no_tunnel_bridge, i + 1); - } - if (HasBit(_new_signal_styles[i].style_flags, NSSF_LOOKAHEAD_SINGLE_SIGNAL)) { - _new_signal_styles[i].lookahead_extra_aspects = 0; - SetBit(_signal_style_masks.next_only, i + 1); - } else if (HasBit(_new_signal_styles[i].style_flags, NSSF_LOOKAHEAD_ASPECTS_SET)) { - _new_signal_styles[i].lookahead_extra_aspects = std::min(_new_signal_styles[i].lookahead_extra_aspects, _new_signal_styles[i].grffile->new_signal_extra_aspects); - } else { - _new_signal_styles[i].lookahead_extra_aspects = _new_signal_styles[i].grffile->new_signal_extra_aspects; - } - if (HasBit(_new_signal_styles[i].style_flags, NSSF_OPPOSITE_SIDE)) { - SetBit(_signal_style_masks.signal_opposite_side, i + 1); - } + } + + for (uint i = 0; i < _num_new_signal_styles; i++) { + if (HasBit(_new_signal_styles[i].style_flags, NSSF_NO_ASPECT_INC)) { + SetBit(_signal_style_masks.non_aspect_inc, i + 1); + SetBit(_signal_style_masks.no_tunnel_bridge, i + 1); } - for (uint i = _num_new_signal_styles; i < MAX_NEW_SIGNAL_STYLES; i++) { - _new_signal_styles[i].lookahead_extra_aspects = new_extra_aspects; + if (HasBit(_new_signal_styles[i].style_flags, NSSF_ALWAYS_RESERVE_THROUGH)) { + SetBit(_signal_style_masks.always_reserve_through, i + 1); + SetBit(_signal_style_masks.no_tunnel_bridge, i + 1); } + if (HasBit(_new_signal_styles[i].style_flags, NSSF_LOOKAHEAD_SINGLE_SIGNAL)) { + _new_signal_styles[i].lookahead_extra_aspects = 0; + SetBit(_signal_style_masks.next_only, i + 1); + } else if (HasBit(_new_signal_styles[i].style_flags, NSSF_LOOKAHEAD_ASPECTS_SET)) { + _new_signal_styles[i].lookahead_extra_aspects = std::min(_new_signal_styles[i].lookahead_extra_aspects, _new_signal_styles[i].grffile->new_signal_extra_aspects); + } else { + _new_signal_styles[i].lookahead_extra_aspects = _new_signal_styles[i].grffile->new_signal_extra_aspects; + } + if (HasBit(_new_signal_styles[i].style_flags, NSSF_OPPOSITE_SIDE)) { + SetBit(_signal_style_masks.signal_opposite_side, i + 1); + } + } + for (uint i = _num_new_signal_styles; i < MAX_NEW_SIGNAL_STYLES; i++) { + _new_signal_styles[i].lookahead_extra_aspects = new_extra_aspects; } _extra_aspects = new_extra_aspects; @@ -1561,9 +1635,23 @@ static bool DetermineExtraAspectsVariable() void UpdateExtraAspectsVariable() { - bool changed = DetermineExtraAspectsVariable(); + std::array new_mapping; + DetermineSignalStyleMapping(new_mapping); + + bool style_remap = false; + if (new_mapping != _new_signal_style_mapping) { + style_remap = RemapNewSignalStyles(new_mapping); + } + + bool style_change = DetermineExtraAspectsVariable(); + + if (style_remap || style_change) { + if (_networking && !_network_server) { + const char *msg = "Network client recalculating signal states and/or signal style mappings, this is likely to cause desyncs"; + DEBUG(desync, 0, "%s", msg); + LogDesyncMsg(msg); + } - if (changed) { UpdateAllSignalReserveThroughBits(); if (_extra_aspects > 0) UpdateAllSignalAspects(); UpdateAllBlockSignals(); @@ -1573,6 +1661,7 @@ void UpdateExtraAspectsVariable() void InitialiseExtraAspectsVariable() { + DetermineSignalStyleMapping(_new_signal_style_mapping); DetermineExtraAspectsVariable(); }