From e1572c09ff611a38b6bc9484ead7f39ba8084810 Mon Sep 17 00:00:00 2001 From: Khopa Date: Sun, 25 Oct 2020 14:08:11 +0100 Subject: [PATCH] Improved faction & faction loader typing. Fixed error with netherlands faction. --- game/db.py | 7 +- game/factions/faction.py | 105 ++++++++++------------- game/factions/faction_loader.py | 39 +++++---- resources/factions/netherlands_1990.json | 2 +- 4 files changed, 68 insertions(+), 85 deletions(-) diff --git a/game/db.py b/game/db.py index 23575771..e88f7b89 100644 --- a/game/db.py +++ b/game/db.py @@ -1,6 +1,6 @@ from datetime import datetime from enum import Enum -from typing import Any, Dict, List, Optional, Tuple, Type, Union +from typing import Dict, List, Optional, Tuple, Type, Union from dcs.countries import country_dict from dcs.helicopters import ( @@ -153,10 +153,9 @@ from dcs.vehicles import ( ) import pydcs_extensions.frenchpack.frenchpack as frenchpack -from game.factions import faction_loader from game.factions.faction import Faction - # PATCH pydcs data with MODS +from game.factions.faction_loader import FactionLoader from pydcs_extensions.a4ec.a4ec import A_4E_C from pydcs_extensions.mb339.mb339 import MB_339PAN from pydcs_extensions.rafale.rafale import Rafale_A_S, Rafale_M @@ -912,7 +911,7 @@ CARRIER_TAKEOFF_BAN: List[Type[FlyingType]] = [ Units separated by country. country : DCS Country name """ -FACTIONS: [Faction] = faction_loader.load_factions() +FACTIONS: [Faction] = FactionLoader.load_factions() CARRIER_TYPE_BY_PLANE = { FA_18C_hornet: CVN_74_John_C__Stennis, diff --git a/game/factions/faction.py b/game/factions/faction.py index 9bb5f8a0..e189bf77 100644 --- a/game/factions/faction.py +++ b/game/factions/faction.py @@ -1,11 +1,14 @@ +from __future__ import annotations + import logging -from dataclasses import dataclass -from typing import Optional +from dataclasses import dataclass, field +from typing import Optional, Dict, Type, List, Any import dcs +from dcs.countries import country_dict from dcs.planes import PlaneType, plane_map from dcs.unittype import VehicleType, UnitType -from dcs.vehicles import vehicle_map, Armor, Unarmed, Infantry, Fortification, Artillery, AirDefence +from dcs.vehicles import Armor, Unarmed, Infantry, Artillery, AirDefence from game.data.building_data import WW2_ALLIES_BUILDINGS, DEFAULT_AVAILABLE_BUILDINGS, WW2_GERMANY_BUILDINGS from game.data.doctrine import Doctrine, MODERN_DOCTRINE, COLDWAR_DOCTRINE, WWII_DOCTRINE @@ -16,117 +19,96 @@ from pydcs_extensions.mod_units import MODDED_VEHICLES, MODDED_AIRPLANES class Faction: # Country used by this faction - country: str + country: str = field(default="") # Nice name of the faction - name: str + name: str = field(default="") # Available aircraft - aircrafts: [UnitType] + aircrafts: List[UnitType] = field(default_factory=list) # Available awacs aircraft - awacs: [UnitType] + awacs: List[UnitType] = field(default_factory=list) # Available tanker aircraft - tankers: [UnitType] + tankers: List[UnitType] = field(default_factory=list) # Available frontline units - frontline_units: [VehicleType] + frontline_units: List[VehicleType] = field(default_factory=list) # Available artillery units - artillery_units: [VehicleType] + artillery_units: List[VehicleType] = field(default_factory=list) # Infantry units used - infantry_units: [VehicleType] + infantry_units: List[VehicleType] = field(default_factory=list) # Logistics units used - logistics_units: [VehicleType] + logistics_units: List[VehicleType] = field(default_factory=list) # List of units that can be deployed as SHORAD - shorads: [str] + shorads: List[str] = field(default_factory=list) # Possible SAMS site generators for this faction - sams: [str] + sams: List[str] = field(default_factory=list) # Possible Missile site generators for this faction - missiles: [str] + missiles: List[str] = field(default_factory=list) # Required mods or asset packs - requirements: {str: str} + requirements: {str: str} = field(default_factory=dict) # possible aircraft carrier units - aircraft_carrier: [UnitType] + aircraft_carrier: List[UnitType] = field(default_factory=list) # possible helicopter carrier units - helicopter_carrier: [UnitType] + helicopter_carrier: List[UnitType] = field(default_factory=list) # Possible carrier names - carrier_names: [str] + carrier_names: List[str] = field(default_factory=list) # Possible helicopter carrier names - helicopter_carrier_names: [str] + helicopter_carrier_names: List[str] = field(default_factory=list) # Navy group generators - navy_generators: [str] + navy_generators: List[str] = field(default_factory=list) # Available destroyers - destroyers: [str] + destroyers: List[str] = field(default_factory=list) # Available cruisers - cruisers: [str] + cruisers: List[str] = field(default_factory=list) # How many navy group should we try to generate per CP on startup for this faction - navy_group_count: int + navy_group_count: int = field(default=1) # How many missiles group should we try to generate per CP on startup for this faction - missiles_group_count: int + missiles_group_count: int = field(default=1) # Whether this faction has JTAC access - has_jtac: bool + has_jtac: bool = field(default=False) # Unit to use as JTAC for this faction - jtac_unit: str + jtac_unit: str = field(default="") # doctrine - doctrine: Doctrine + doctrine: Doctrine = field(default=MODERN_DOCTRINE) # List of available buildings for this faction - building_set: [str] + building_set: List[str] = field(default_factory=list) - def __init__(self): - self.country = "" - self.name = "" - self.aircrafts = [] - self.awacs = [] - self.tankers = [] - self.frontline_units = [] - self.artillery_units = [] - self.infantry_units = [] - self.logistics_units = [] - self.shorads = [] - self.sams = [] - self.missiles = [] - self.requirements = {} - self.aircraft_carrier = [] - self.helicopter_carrier = [] - self.carrier_names = [] - self.helicopter_carrier_names = [] - self.navy_generators = [] - self.destroyers = [] - self.cruisers = [] - self.navy_group_count = 0 - self.missiles_group_count = 0 - self.has_jtac = False - self.jtac_unit = "" - self.doctrine = None @classmethod - def from_json(cls, json): + def from_json(cls: Type[Faction], json: Dict[str, any]) -> Faction: faction = Faction() - faction.country = json.get("country", "USA") - faction.name = json.get("name", "???") + faction.country = json.get("country", "/") + if faction.country not in [c.name for c in country_dict.values()]: + raise AssertionError("Faction's country (\"{}\") is not a valid DCS country ID".format(faction.country)) + + faction.name = json.get("name", "") + if not faction.name: + raise AssertionError("Faction has no valid name") faction.aircrafts = [f for f in [aircraft_loader(aircraft) for aircraft in json.get("aircrafts", [])] if f is not None] faction.awacs = [f for f in [aircraft_loader(aircraft) for aircraft in json.get("awacs", [])] if f is not None] @@ -179,14 +161,15 @@ class Faction: return faction @property - def units(self): + def units(self) -> List[UnitType]: return self.infantry_units + self.aircrafts + self.awacs + self.artillery_units + self.frontline_units + self.tankers + self.logistics_units -def unit_loader(unit: str, class_repository:[]) -> Optional[PlaneType]: +def unit_loader(unit: str, class_repository: List[Any]) -> Optional[PlaneType]: """ Find unit by name :param unit: Unit name as string + :param class_repository: Repository of classes (Either a module, a class, or a list of classes) :return: The unit as a PyDCS type """ if unit is None: @@ -202,9 +185,9 @@ def unit_loader(unit: str, class_repository:[]) -> Optional[PlaneType]: if m.__name__ == unit: return m logging.info("FACTION ERROR : Unable to find " + unit + " in pydcs") - print("FACTION ERROR : Unable to find " + unit + " in pydcs") return None + aircraft_loader = lambda x: unit_loader(x, [dcs.planes, dcs.helicopters, MODDED_AIRPLANES]) vehicle_loader = lambda x: unit_loader(x, [Infantry, Unarmed, Armor, AirDefence, Artillery, MODDED_VEHICLES]) ship_loader = lambda x: unit_loader(x, [dcs.ships]) diff --git a/game/factions/faction_loader.py b/game/factions/faction_loader.py index 36b0c532..d182ea73 100644 --- a/game/factions/faction_loader.py +++ b/game/factions/faction_loader.py @@ -1,30 +1,31 @@ +from __future__ import annotations import json -import os import logging +from pathlib import Path +from typing import Type from game.factions.faction import Faction FACTION_DIRECTORY = "./resources/factions/" -def load_factions() -> {str, Faction}: - files = os.listdir(FACTION_DIRECTORY) - files = [f for f in files if f.endswith(".json")] +class FactionLoader: - factions = {} + @classmethod + def load_factions(cls: Type[FactionLoader]) -> {str, Faction}: - for f in files: - print(f) - path = os.path.join(FACTION_DIRECTORY, f) - logging.info("Loading faction" + path) - #try: - with open(path, "r", encoding="utf-8") as fdata: - data = json.load(fdata, encoding="utf-8") - factions[data["name"]] = Faction.from_json(data) - logging.info("Loaded faction : " + path) - #except Exception as e: - # print(e) - # logging.error("Unable to load faction : " + path) + path = Path(FACTION_DIRECTORY) + files = [f for f in path.glob("*.json") if f.is_file()] + factions = {} - print(factions) - return factions \ No newline at end of file + for f in files: + logging.info("Loading faction" + str(f)) + try: + with open(f, "r", encoding="utf-8") as fdata: + data = json.load(fdata, encoding="utf-8") + factions[data["name"]] = Faction.from_json(data) + logging.info("Loaded faction : " + str(f)) + except Exception as e: + logging.error("Unable to load faction : " + path, e) + + return factions diff --git a/resources/factions/netherlands_1990.json b/resources/factions/netherlands_1990.json index 5373fda8..3b915643 100644 --- a/resources/factions/netherlands_1990.json +++ b/resources/factions/netherlands_1990.json @@ -1,5 +1,5 @@ { - "country": "Netherlands", + "country": "The Netherlands", "name": "Netherlands 1990", "authors": "Khopa", "description": "",