Remove eager loading of factions.

Eager loading meant that users would need to restart Liberation to pick
up changes to faction files. That's annoying for modders, slows down
start up, and uselessly sits in RAM when it's not needed after game
creation.

Also removes the __getitem__ and __iter__ methods in favor of named
methods, since the dunder methods are more or less impenetrable for IDEs
and grep.
This commit is contained in:
Dan Albert 2023-04-17 21:47:34 -07:00
parent f3d2952579
commit 42e9a6294b
5 changed files with 62 additions and 71 deletions

View File

@ -1,4 +1 @@
from .faction import Faction from .faction import Faction
from .factionloader import FactionLoader
FACTIONS = FactionLoader()

View File

@ -1,55 +0,0 @@
from __future__ import annotations
import json
import logging
from pathlib import Path
from typing import Dict, Iterator, List, Optional, Type
from game import persistence
from game.factions.faction import Faction
FACTION_DIRECTORY = Path("./resources/factions/")
class FactionLoader:
def __init__(self) -> None:
self._factions: Optional[Dict[str, Faction]] = None
@property
def factions(self) -> Dict[str, Faction]:
self.initialize()
assert self._factions is not None
return self._factions
def initialize(self) -> None:
if self._factions is None:
self._factions = self.load_factions()
@staticmethod
def find_faction_files_in(path: Path) -> List[Path]:
return [f for f in path.glob("*.json") if f.is_file()]
@classmethod
def load_factions(cls: Type[FactionLoader]) -> Dict[str, Faction]:
user_faction_path = Path(persistence.base_path()) / "Liberation/Factions"
files = cls.find_faction_files_in(
FACTION_DIRECTORY
) + cls.find_faction_files_in(user_faction_path)
factions = {}
for f in files:
try:
with f.open("r", encoding="utf-8") as fdata:
data = json.load(fdata)
factions[data["name"]] = Faction.from_json(data)
logging.info("Loaded faction : " + str(f))
except Exception:
logging.exception(f"Unable to load faction : {f}")
return factions
def __getitem__(self, name: str) -> Faction:
return self.factions[name]
def __iter__(self) -> Iterator[str]:
return iter(self.factions.keys())

46
game/factions/factions.py Normal file
View File

@ -0,0 +1,46 @@
from __future__ import annotations
import json
import logging
from collections.abc import Iterator
from pathlib import Path
from game import persistence
from .faction import Faction
class Factions:
def __init__(self, factions: dict[str, Faction]) -> None:
self.factions = factions
def get_by_name(self, name: str) -> Faction:
return self.factions[name]
def iter_faction_names(self) -> Iterator[str]:
return iter(self.factions.keys())
@staticmethod
def iter_faction_files_in(path: Path) -> Iterator[Path]:
yield from path.glob("*.json")
@classmethod
def iter_faction_files(cls) -> Iterator[Path]:
yield from cls.iter_faction_files_in(Path("resources/factions/"))
yield from cls.iter_faction_files_in(
Path(persistence.base_path()) / "Liberation/Factions"
)
@classmethod
def load(cls) -> Factions:
factions = {}
for path in cls.iter_faction_files():
try:
with path.open("r", encoding="utf-8") as fdata:
data = json.load(fdata)
faction = Faction.from_json(data)
factions[faction.name] = faction
logging.info("Loaded faction from %s", path)
except Exception:
logging.exception(f"Unable to load faction from %s", path)
return Factions(factions)

View File

@ -17,7 +17,7 @@ from game import Game, VERSION, logging_config, persistence
from game.campaignloader.campaign import Campaign, DEFAULT_BUDGET from game.campaignloader.campaign import Campaign, DEFAULT_BUDGET
from game.data.weapons import Pylon, Weapon, WeaponGroup from game.data.weapons import Pylon, Weapon, WeaponGroup
from game.dcs.aircrafttype import AircraftType from game.dcs.aircrafttype import AircraftType
from game.factions import FACTIONS from game.factions.factions import Factions
from game.profiling import logged_duration from game.profiling import logged_duration
from game.server import EventStream, Server from game.server import EventStream, Server
from game.settings import Settings from game.settings import Settings
@ -275,9 +275,10 @@ def create_game(
inject_custom_payloads(Path(persistence.base_path())) inject_custom_payloads(Path(persistence.base_path()))
campaign = Campaign.from_file(campaign_path) campaign = Campaign.from_file(campaign_path)
theater = campaign.load_theater(advanced_iads) theater = campaign.load_theater(advanced_iads)
faction_loader = Factions.load()
generator = GameGenerator( generator = GameGenerator(
FACTIONS[blue], faction_loader.get_by_name(blue),
FACTIONS[red], faction_loader.get_by_name(red),
theater, theater,
campaign.load_air_wing_config(theater), campaign.load_air_wing_config(theater),
Settings( Settings(

View File

@ -10,7 +10,8 @@ from PySide6.QtWidgets import QCheckBox, QLabel, QTextEdit, QVBoxLayout
from jinja2 import Environment, FileSystemLoader, select_autoescape from jinja2 import Environment, FileSystemLoader, select_autoescape
from game.campaignloader.campaign import Campaign, DEFAULT_BUDGET from game.campaignloader.campaign import Campaign, DEFAULT_BUDGET
from game.factions import FACTIONS, Faction from game.factions import Faction
from game.factions.factions import Factions
from game.settings import Settings from game.settings import Settings
from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings
from qt_ui.widgets.QLiberationCalendar import QLiberationCalendar from qt_ui.widgets.QLiberationCalendar import QLiberationCalendar
@ -224,6 +225,8 @@ class FactionSelection(QtWidgets.QWizardPage):
def __init__(self, parent=None): def __init__(self, parent=None):
super(FactionSelection, self).__init__(parent) super(FactionSelection, self).__init__(parent)
self.factions = Factions.load()
self.setTitle("Faction selection") self.setTitle("Faction selection")
self.setSubTitle( self.setSubTitle(
"\nChoose the two opposing factions and select the player side." "\nChoose the two opposing factions and select the player side."
@ -243,7 +246,7 @@ class FactionSelection(QtWidgets.QWizardPage):
blueFaction = QtWidgets.QLabel("<b>Player Faction :</b>") blueFaction = QtWidgets.QLabel("<b>Player Faction :</b>")
self.blueFactionSelect = QtWidgets.QComboBox() self.blueFactionSelect = QtWidgets.QComboBox()
for f in FACTIONS: for f in self.factions.iter_faction_names():
self.blueFactionSelect.addItem(f) self.blueFactionSelect.addItem(f)
blueFaction.setBuddy(self.blueFactionSelect) blueFaction.setBuddy(self.blueFactionSelect)
@ -259,7 +262,7 @@ class FactionSelection(QtWidgets.QWizardPage):
self.redFactionDescription.setReadOnly(True) self.redFactionDescription.setReadOnly(True)
# Setup default selected factions # Setup default selected factions
for i, r in enumerate(FACTIONS): for i, r in enumerate(self.factions.iter_faction_names()):
self.redFactionSelect.addItem(r) self.redFactionSelect.addItem(r)
if r == "Russia 1990": if r == "Russia 1990":
self.redFactionSelect.setCurrentIndex(i) self.redFactionSelect.setCurrentIndex(i)
@ -305,10 +308,10 @@ class FactionSelection(QtWidgets.QWizardPage):
self.blueFactionSelect.clear() self.blueFactionSelect.clear()
self.redFactionSelect.clear() self.redFactionSelect.clear()
for f in FACTIONS: for f in self.factions.iter_faction_names():
self.blueFactionSelect.addItem(f) self.blueFactionSelect.addItem(f)
for i, r in enumerate(FACTIONS): for i, r in enumerate(self.factions.iter_faction_names()):
self.redFactionSelect.addItem(r) self.redFactionSelect.addItem(r)
if r == campaign.recommended_enemy_faction: if r == campaign.recommended_enemy_faction:
self.redFactionSelect.setCurrentIndex(i) self.redFactionSelect.setCurrentIndex(i)
@ -318,9 +321,8 @@ class FactionSelection(QtWidgets.QWizardPage):
self.updateUnitRecap() self.updateUnitRecap()
def updateUnitRecap(self): def updateUnitRecap(self):
red_faction = self.factions.get_by_name(self.redFactionSelect.currentText())
red_faction = FACTIONS[self.redFactionSelect.currentText()] blue_faction = self.factions.get_by_name(self.blueFactionSelect.currentText())
blue_faction = FACTIONS[self.blueFactionSelect.currentText()]
template = jinja_env.get_template("factiontemplate_EN.j2") template = jinja_env.get_template("factiontemplate_EN.j2")
@ -332,11 +334,11 @@ class FactionSelection(QtWidgets.QWizardPage):
@property @property
def selected_blue_faction(self) -> Faction: def selected_blue_faction(self) -> Faction:
return FACTIONS[self.blueFactionSelect.currentText()] return self.factions.get_by_name(self.blueFactionSelect.currentText())
@property @property
def selected_red_faction(self) -> Faction: def selected_red_faction(self) -> Faction:
return FACTIONS[self.redFactionSelect.currentText()] return self.factions.get_by_name(self.redFactionSelect.currentText())
class TheaterConfiguration(QtWidgets.QWizardPage): class TheaterConfiguration(QtWidgets.QWizardPage):