Game.City.CityConfigurationSystem
Assembly: Game
Namespace: Game.City
Type: class
Base: GameSystemBase
Implements: IDefaultSerializable, ISerializable, IPostDeserialize
Summary: CityConfigurationSystem manages per-city configuration used by the game and by mods. It stores and serializes settings such as the city name, theme, camera position, traffic handedness, gameplay options (unlock all, unlimited money, unlock map tiles, natural disasters), and required content (prefab references). It also applies loaded configuration to runtime systems (camera, flip-traffic system, unlock-all system) during PostDeserialize/OnGameLoaded and exposes hooks to resolve entity references after serialization (PatchReferences). The system records enabled mods when saving a game and restores camera/controller state when loading.
Fields
-
private string m_LoadedCityName
{{ Stores the city name read from serialization. Used to set the public cityName unless overrideLoadedOptions is true. }} -
private NativeList<Entity> m_RequiredContent
{{ List of required content prefab Entity references for this city configuration. Allocated with Allocator.Persistent in OnCreate and disposed in OnDestroy. Serialized only when the format includes ContentPrefabInCityConfiguration. }} -
private bool m_LoadedLeftHandTraffic
{{ The left-hand traffic flag as read from the saved configuration. Used to determine whether to flip lane handedness when the game loads. }} -
private bool m_LoadedNaturalDisasters
{{ Natural disasters flag loaded from saved data. }} -
private bool m_UnlockAll
{{ Backing field for the unlockAll property when not using loaded values. Setting this updates the saved backing value; the public getter may return true based on loaded state. }} -
private bool m_LoadedUnlockAll
{{ The unlock-all flag as read from saved data. }} -
private bool m_UnlimitedMoney
{{ Backing field for the unlimitedMoney property when not using loaded values. }} -
private bool m_LoadedUnlimitedMoney
{{ The unlimited-money flag as read from saved data. }} -
private bool m_UnlockMapTiles
{{ Backing field for the unlockMapTiles property when not using loaded values. }} -
private bool m_LoadedUnlockMapTiles
{{ The unlock-map-tiles flag as read from saved data. }} -
private PrefabSystem m_PrefabSystem
{{ Cached reference to PrefabSystem retrieved in OnCreate; used to look up prefabs (e.g. ThemePrefab) during PostDeserialize and PatchReferences. }} -
private UnlockAllSystem m_UnlockAllSystem
{{ Cached UnlockAllSystem used to enable unlock-all mode on game load. }} -
private FlipTrafficHandednessSystem m_FlipTrafficHandednessSystem
{{ Cached system used to flip traffic handedness when loaded options differ from current. }} -
private CameraUpdateSystem m_CameraUpdateSystem
{{ Cached camera update system used to restore camera pivot/rotation/zoom and active controller after loading. }} -
private EntityQuery m_ThemeQuery
{{ Query to find ThemeData entities (excluding Locked). Used to pick a default theme if one isn't specified in the saved data. }} -
private EntityQuery m_SubLaneQuery
{{ Query for Game.Net.SubLane components. When some options change (like traffic handedness) the system adds Updated component to this query to force lane updates. }} -
public float3 m_CameraPivot
{{ Camera pivot saved/loaded from configuration. }} -
public float2 m_CameraAngle
{{ Camera angle saved/loaded (stored as float2 with swapped order for rotation reconstruction). }} -
public float m_CameraZoom
{{ Camera zoom saved/loaded. }} -
public Entity m_CameraFollow
{{ Entity that the orbit camera was following at save time (or Entity.Null). }} -
private static readonly float3 kDefaultCameraPivot
{{ Default pivot value used when no saved camera is available. }} -
private static readonly float2 kDefaultCameraAngle
{{ Default camera angle used when no saved camera is available. }} -
private static readonly float kDefaultCameraZoom
{{ Default camera zoom used when no saved camera is available. }} -
private static readonly Entity kDefaultCameraFollow
{{ Default camera follow entity (Entity.Null). }}
Properties
-
public string cityName { get; set; }
{{ Public city name used by the UI and game. Normally set from loaded data unless overrideLoadedOptions is true. }} -
public string overrideCityName { get; set; }
{{ If set prior to preload, this override will be applied to the cityName on game preload. }} -
[CanBeNull] public string overrideThemeName { get; set; }
{{ Optional theme name to override the defaultTheme when deserializing; if provided it is searched among available ThemePrefabs in PostDeserialize. }} -
public Entity defaultTheme { get; set; }
{{ The chosen theme entity for the city configuration (may be resolved via PatchReferences). }} -
public Entity loadedDefaultTheme { get; set; }
{{ The theme entity ID read from the saved data. }} -
public ref NativeList<Entity> requiredContent => ref m_RequiredContent
{{ Exposes a ref to the required content list so other systems can inspect or add required prefab entities. Be careful with lifetime: list is managed/owned by this system. }} -
public bool leftHandTraffic { get; set; }
{{ Current left-hand traffic setting. }} -
public bool overrideLeftHandTraffic { get; set; }
{{ If set before preload, this value will be applied during OnGamePreload. }} -
public bool naturalDisasters { get; set; }
{{ Current natural disaster setting. }} -
public bool overrideNaturalDisasters { get; set; }
{{ If set before preload, applied during OnGamePreload. }} -
public bool unlockAll { get; set; }
{{ Getter returns true if m_LoadedUnlockAll is true (if loaded) otherwise returns the m_UnlockAll value. Setter writes m_UnlockAll. This allows the loaded save to force the unlock-all option regardless of runtime backing. }} -
public bool overrideUnlockAll { get; set; }
{{ If set before preload, applied during OnGamePreload. }} -
public bool unlimitedMoney { get; set; }
{{ Similar to unlockAll: getter returns true if the loaded value indicates unlimited money; setter writes m_UnlimitedMoney backing field. }} -
public bool overrideUnlimitedMoney { get; set; }
{{ If set before preload, applied during OnGamePreload. }} -
public bool unlockMapTiles { get; set; }
{{ Similar pattern for unlock-map-tiles option. }} -
public bool overrideUnlockMapTiles { get; set; }
{{ If set before preload, applied during OnGamePreload. }} -
public bool overrideLoadedOptions { get; set; }
{{ When true, the system will not overwrite runtime options with loaded ones on Deserialize/PostDeserialize; useful for forcing current session options to persist across loads. }} -
public HashSet<string> usedMods { get; private set; } = new HashSet<string>()
{{ Set of mod identifiers that were enabled when the save was written. Populated in PostDeserialize from ModManager.GetModsEnabled() when saving a game. }}
Constructors
public CityConfigurationSystem()
{{ Default constructor. No explicit initialization other than what the base and OnCreate provide. Marked with [Preserve] for reflection/serialization usage in Unity. Instance initialization (prefab/system caching, NativeList allocation, queries) is performed in OnCreate. }}
Methods
-
public void PatchReferences(ref PrefabReferences references)
{{ Resolves Entity references stored on the system (defaultTheme, loadedDefaultTheme, and each entry in m_RequiredContent) via PrefabReferences.Check(EntityManager, entity). Call this after loading or when prefab indices may have changed to remap stored entity IDs to current runtime IDs. }} -
[Preserve] protected override void OnCreate()
{{ Initializes cached subsystem references (PrefabSystem, UnlockAllSystem, FlipTrafficHandednessSystem, CameraUpdateSystem), allocates m_RequiredContent (NativeListwith Allocator.Persistent) and creates EntityQueries (m_ThemeQuery and m_SubLaneQuery). Marked [Preserve] so it is kept by code stripping. }} -
[Preserve] protected override void OnDestroy()
{{ Disposes m_RequiredContent and calls base.OnDestroy. Always ensure the NativeList is properly disposed to avoid leaks. }} -
[Preserve] protected override void OnUpdate()
{{ Empty override in this class — the system does not perform per-frame logic here. }} -
protected override void OnGamePreload(Purpose purpose, GameMode mode)
{{ Applies override* properties (overrideCityName, overrideLeftHandTraffic, etc.) to the public properties (cityName, leftHandTraffic, naturalDisasters, unlockAll, unlimitedMoney, unlockMapTiles) prior to game load. This allows external code to force specific options before the rest of the game initializes. }} -
protected override void OnGameLoaded(Context serializationContext)
{{ Runs after the game has finished loading: enables the UnlockAllSystem if unlockAll is true. }} -
public void PostDeserialize(Context context)
{{ Called after Deserialize. Responsibilities: - If defaultTheme is not set or overrideThemeName is provided, it will search available themes (via m_ThemeQuery and m_PrefabSystem) and set defaultTheme accordingly.
- If leftHandTraffic or defaultTheme differ from loaded values, mark lanes as Updated (AddComponent
on m_SubLaneQuery). - If handedness changed, call FlipTrafficHandednessSystem.Update() to apply the change.
- Restore camera controller/pivot/rotation/zoom/followed entity by setting properties on CameraUpdateSystem controllers (orbitCameraController or gamePlayController) and set activeCameraController accordingly.
-
Collect enabled mods via ModManager.GetModsEnabled() into usedMods. Note: overrideThemeName is cleared after processing. }}
-
public void Serialize<TWriter>(TWriter writer) where TWriter : IWriter
{{ Serializes the configuration to the provided writer. The method writes: - cityName
- defaultTheme
- requiredContent list (NativeList
) - leftHandTraffic, naturalDisasters
- Camera pivot/angle/zoom/follow info (special handling depending on writer.context.purpose — SaveMap vs SaveGame — and camera state)
- m_UnlimitedMoney, m_UnlockAll, m_UnlockMapTiles
-
In SaveGame purpose, writes usedMods count followed by each used mod string; otherwise writes 0 for usedMods. The method uses writer.context.purpose to decide which camera data to write (map vs game). }}
-
public void Deserialize<TReader>(TReader reader) where TReader : IReader
{{ Reads configuration from reader and populates loaded* fields. Behavior depends on reader.context.version and format flags: - Reads m_LoadedCityName when supported.
- Reads loadedDefaultTheme.
- Reads m_RequiredContent if format includes ContentPrefabInCityConfiguration, otherwise clears the list.
- Reads m_LoadedLeftHandTraffic, m_LoadedNaturalDisasters, camera position data (or resets to defaults if version does not include), m_LoadedUnlimitedMoney, m_LoadedUnlockAll, m_LoadedUnlockMapTiles depending on version flags.
- Reads usedMods when version supports saveGameUsedMods.
- Applies loaded values to public properties unless overrideLoadedOptions is true. If overrideLoadedOptions is true and we are loading a saved game, some values (defaultTheme, leftHandTraffic) are still applied.
-
Clears overrideLoadedOptions after applying. Note: After Deserialization the caller should call PostDeserialize to finalize theme/camera application and mod list population. }}
-
private void ResetCameraProperties()
{{ Helper to set camera properties to default constants: kDefaultCameraPivot, kDefaultCameraAngle, kDefaultCameraZoom, kDefaultCameraFollow. Used during Deserialize when camera data isn't present in the save. }} -
public void SetDefaults(Context context)
{{ Resets the loaded fields and public properties to default (empty or false) values and clears requiredContent and usedMods. If overrideLoadedOptions is false, public properties are set from the loaded defaults (which are cleared here). Also resets camera properties. Use this to initialize a fresh city configuration when starting a new session. }}
Usage Example
// Example: force an override city name and left-hand traffic before preload
var cityConfig = World.GetOrCreateSystemManaged<Game.City.CityConfigurationSystem>();
cityConfig.overrideCityName = "MyModCity";
cityConfig.overrideLeftHandTraffic = true;
cityConfig.overrideLoadedOptions = false; // allow loaded options to be applied unless overridden
// Example: show how OnCreate allocates required content (this is similar to the actual implementation)
[Preserve]
protected override void OnCreate()
{
base.OnCreate();
// Note: the real system caches subsystem references and allocates the NativeList with Persistent allocator
// m_RequiredContent = new NativeList<Entity>(0, Allocator.Persistent);
}
Notes and Modding Tips: - Call PatchReferences(ref PrefabReferences) after loading prefabs or remapping IDs so stored Entity references (themes, required content) point to the current runtime entities. - If you change leftHandTraffic externally, add Updated to relevant lane entities or call FlipTrafficHandednessSystem.Update() to ensure lanes are reprocessed. - m_RequiredContent is allocated with Allocator.Persistent and disposed in OnDestroy — do not dispose it yourself unless you take ownership. - Use override* properties before the game preload phase to ensure they are applied. Setting overrideLoadedOptions can prevent the saved file from clobbering mod/app-provided options.