Pre-allocate laser codes for FLOTs and flights.

This commit is contained in:
Dan Albert 2023-07-22 14:31:38 -07:00
parent 31289adb50
commit 85e11711b6
13 changed files with 89 additions and 34 deletions

View File

@ -1,8 +1,10 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from game.ato.loadouts import Loadout
from game.lasercodes import LaserCode
from game.savecompat import has_save_compat_for
if TYPE_CHECKING:
from game.squadrons import Pilot
@ -13,8 +15,30 @@ class FlightMember:
self.pilot = pilot
self.loadout = loadout
self.use_custom_loadout = False
self.tgp_laser_code: LaserCode | None = None
self.properties: dict[str, bool | float | int] = {}
@has_save_compat_for(9)
def __setstate__(self, state: dict[str, Any]) -> None:
if "tgp_laser_code" not in state:
state["tgp_laser_code"] = None
self.__dict__.update(state)
def assign_tgp_laser_code(self, code: LaserCode) -> None:
if self.tgp_laser_code is not None:
raise RuntimeError(
f"{self.pilot} already has already been assigned laser code "
f"{self.tgp_laser_code}"
)
self.tgp_laser_code = code
def release_tgp_laser_code(self) -> None:
if self.tgp_laser_code is None:
raise RuntimeError(f"{self.pilot} has no assigned laser code")
self.tgp_laser_code.release()
self.tgp_laser_code = None
@property
def is_player(self) -> bool:
if self.pilot is None:

View File

@ -46,9 +46,11 @@ class FlightMembers(IFlightRoster):
def resize(self, new_size: int) -> None:
if self.max_size > new_size:
self.flight.squadron.return_pilots(
[m.pilot for m in self.members[new_size:] if m.pilot is not None]
)
for member in self.members[new_size:]:
if (pilot := member.pilot) is not None:
self.flight.squadron.return_pilot(pilot)
if (code := member.tgp_laser_code) is not None:
code.release()
self.members = self.members[:new_size]
return
if self.max_size:
@ -71,6 +73,9 @@ class FlightMembers(IFlightRoster):
self.flight.squadron.return_pilots(
[p for p in self.iter_pilots() if p is not None]
)
for member in self.members:
if (code := member.tgp_laser_code) is not None:
code.release()
def use_same_loadout_for_all_members(self) -> None:
if not self.members:

View File

@ -26,6 +26,7 @@ if TYPE_CHECKING:
from .data.doctrine import Doctrine
from .factions.faction import Faction
from .game import Game
from .lasercodes import LaserCodeRegistry
from .sim import GameUpdateEvents
@ -90,6 +91,10 @@ class Coalition:
assert self._navmesh is not None
return self._navmesh
@property
def laser_code_registry(self) -> LaserCodeRegistry:
return self.game.laser_code_registry
def __getstate__(self) -> dict[str, Any]:
state = self.__dict__.copy()
# Avoid persisting any volatile types that can be deterministically

View File

@ -10,9 +10,10 @@ from ..ato.starttype import StartType
from ..db.database import Database
if TYPE_CHECKING:
from game.dcs.aircrafttype import AircraftType
from game.squadrons.airwing import AirWing
from game.ato.closestairfields import ClosestAirfields
from game.dcs.aircrafttype import AircraftType
from game.lasercodes import LaserCodeRegistry
from game.squadrons.airwing import AirWing
from .missionproposals import ProposedFlight
@ -24,6 +25,7 @@ class PackageBuilder:
location: MissionTarget,
closest_airfields: ClosestAirfields,
air_wing: AirWing,
laser_code_registry: LaserCodeRegistry,
flight_db: Database[Flight],
is_player: bool,
package_country: str,
@ -35,6 +37,7 @@ class PackageBuilder:
self.package_country = package_country
self.package = Package(location, flight_db, auto_asap=asap)
self.air_wing = air_wing
self.laser_code_registry = laser_code_registry
self.start_type = start_type
def plan_flight(self, plan: ProposedFlight) -> bool:
@ -63,6 +66,11 @@ class PackageBuilder:
start_type,
divert=self.find_divert_field(squadron.aircraft, squadron.location),
)
for member in flight.iter_members():
if member.is_player:
member.assign_tgp_laser_code(
self.laser_code_registry.alloc_laser_code()
)
self.package.add_flight(flight)
return True

View File

@ -141,6 +141,7 @@ class PackageFulfiller:
mission.location,
ObjectiveDistanceCache.get_closest_airfields(mission.location),
self.air_wing,
self.coalition.laser_code_registry,
self.flight_db,
self.is_player,
self.coalition.country_name,

View File

@ -24,8 +24,10 @@ from .campaignloader import CampaignAirWingConfig
from .coalition import Coalition
from .db.gamedb import GameDb
from .infos.information import Information
from .lasercodes.lasercoderegistry import LaserCodeRegistry
from .persistence import SaveManager
from .profiling import logged_duration
from .savecompat import has_save_compat_for
from .settings import Settings
from .theater import ConflictTheater
from .theater.bullseye import Bullseye
@ -112,6 +114,7 @@ class Game:
self.current_unit_id = 0
self.current_group_id = 0
self.name_generator = naming.namegen
self.laser_code_registry = LaserCodeRegistry()
self.db = GameDb()
@ -139,8 +142,13 @@ class Game:
self.on_load(game_still_initializing=True)
@has_save_compat_for(9)
def __setstate__(self, state: dict[str, Any]) -> None:
self.__dict__.update(state)
if not hasattr(self, "laser_code_registry"):
self.laser_code_registry = LaserCodeRegistry()
for front_line in self.theater.conflicts():
front_line.laser_code = self.laser_code_registry.alloc_laser_code()
# Regenerate any state that was not persisted.
self.on_load()
@ -299,7 +307,7 @@ class Game:
self.theater.iads_network.initialize_network(self.theater.ground_objects)
for control_point in self.theater.controlpoints:
control_point.initialize_turn_0()
control_point.initialize_turn_0(self.laser_code_registry)
for tgo in control_point.connected_objectives:
self.db.tgos.add(tgo.id, tgo)

View File

@ -18,7 +18,6 @@ from game.ato.package import Package
from game.ato.starttype import StartType
from game.factions.faction import Faction
from game.missiongenerator.missiondata import MissionData
from game.lasercodes import LaserCodeRegistry
from game.radio.radios import RadioRegistry
from game.radio.tacan import TacanRegistry
from game.runways import RunwayData
@ -48,7 +47,6 @@ class AircraftGenerator:
time: datetime,
radio_registry: RadioRegistry,
tacan_registry: TacanRegistry,
laser_code_registry: LaserCodeRegistry,
unit_map: UnitMap,
mission_data: MissionData,
helipads: dict[ControlPoint, StaticGroup],
@ -59,7 +57,6 @@ class AircraftGenerator:
self.time = time
self.radio_registry = radio_registry
self.tacan_registy = tacan_registry
self.laser_code_registry = laser_code_registry
self.unit_map = unit_map
self.flights: List[FlightData] = []
self.mission_data = mission_data
@ -174,7 +171,6 @@ class AircraftGenerator:
self.time,
self.radio_registry,
self.tacan_registy,
self.laser_code_registry,
self.mission_data,
dynamic_runways,
self.use_client,

View File

@ -11,8 +11,7 @@ from dcs.unitgroup import FlyingGroup
from game.ato import Flight, FlightType
from game.callsigns import callsign_for_support_unit
from game.data.weapons import Pylon, WeaponType as WeaponTypeEnum
from game.lasercodes import LaserCodeRegistry
from game.data.weapons import Pylon
from game.missiongenerator.logisticsgenerator import LogisticsGenerator
from game.missiongenerator.missiondata import AwacsInfo, MissionData, TankerInfo
from game.radio.radios import RadioFrequency, RadioRegistry
@ -40,7 +39,6 @@ class FlightGroupConfigurator:
time: datetime,
radio_registry: RadioRegistry,
tacan_registry: TacanRegistry,
laser_code_registry: LaserCodeRegistry,
mission_data: MissionData,
dynamic_runways: dict[str, RunwayData],
use_client: bool,
@ -53,7 +51,6 @@ class FlightGroupConfigurator:
self.time = time
self.radio_registry = radio_registry
self.tacan_registry = tacan_registry
self.laser_code_registry = laser_code_registry
self.mission_data = mission_data
self.dynamic_runways = dynamic_runways
self.use_client = use_client
@ -132,8 +129,8 @@ class FlightGroupConfigurator:
self, unit: FlyingUnit, member: FlightMember, laser_codes: list[Optional[int]]
) -> None:
self.set_skill(unit, member)
if member.loadout.has_weapon_of_type(WeaponTypeEnum.TGP) and member.is_player:
laser_codes.append(self.laser_code_registry.alloc_laser_code().code)
if (code := member.tgp_laser_code) is not None:
laser_codes.append(code.code)
else:
laser_codes.append(None)

View File

@ -37,7 +37,6 @@ from game.ground_forces.ai_ground_planner import (
DISTANCE_FROM_FRONTLINE,
)
from game.ground_forces.combat_stance import CombatStance
from game.lasercodes import LaserCodeRegistry
from game.naming import namegen
from game.radio.radios import RadioRegistry
from game.theater.controlpoint import ControlPoint
@ -80,7 +79,6 @@ class FlotGenerator:
unit_map: UnitMap,
radio_registry: RadioRegistry,
mission_data: MissionData,
laser_code_registry: LaserCodeRegistry,
) -> None:
self.mission = mission
self.conflict = conflict
@ -92,7 +90,6 @@ class FlotGenerator:
self.unit_map = unit_map
self.radio_registry = radio_registry
self.mission_data = mission_data
self.laser_code_registry = laser_code_registry
def generate(self) -> None:
position = FrontLineConflictDescription.frontline_position(
@ -141,9 +138,9 @@ class FlotGenerator:
# laser codes to 1113 to allow lasing for Su-25 Frogfoots and A-10A Warthogs.
# Otherwise use 1688 for the first JTAC, 1687 for the second etc.
if self.game.lua_plugin_manager.is_option_enabled("ctld", "fc3LaserCode"):
code = self.laser_code_registry.fc3_code
code = self.game.laser_code_registry.fc3_code
else:
code = self.laser_code_registry.alloc_laser_code()
code = self.conflict.front_line.laser_code
utype = self.game.blue.faction.jtac_unit
if utype is None:

View File

@ -34,7 +34,6 @@ from .flotgenerator import FlotGenerator
from .forcedoptionsgenerator import ForcedOptionsGenerator
from .frontlineconflictdescription import FrontLineConflictDescription
from .kneeboard import KneeboardGenerator
from game.lasercodes import LaserCodeRegistry
from .luagenerator import LuaGenerator
from .missiondata import MissionData
from .tgogenerator import TgoGenerator
@ -65,7 +64,6 @@ class MissionGenerator:
self.mission_data = MissionData()
self.laser_code_registry = LaserCodeRegistry()
self.radio_registry = RadioRegistry()
self.tacan_registry = TacanRegistry()
@ -236,7 +234,6 @@ class MissionGenerator:
self.unit_map,
self.radio_registry,
self.mission_data,
self.laser_code_registry,
)
ground_conflict_gen.generate()
@ -262,7 +259,6 @@ class MissionGenerator:
self.time,
self.radio_registry,
self.tacan_registry,
self.laser_code_registry,
self.unit_map,
mission_data=air_support_generator.mission_data,
helipads=tgo_generator.helipads,

View File

@ -81,6 +81,7 @@ if TYPE_CHECKING:
from game import Game
from game.ato.flighttype import FlightType
from game.coalition import Coalition
from game.lasercodes.lasercoderegistry import LaserCodeRegistry
from game.sim import GameUpdateEvents
from game.squadrons.squadron import Squadron
from game.transfers import PendingTransfers
@ -377,42 +378,50 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
assert self._front_line_db is None
self._front_line_db = game.db.front_lines
def initialize_turn_0(self) -> None:
def initialize_turn_0(self, laser_code_registry: LaserCodeRegistry) -> None:
# We don't need to send events for turn 0. The UI isn't up yet, and it'll fetch
# the entire game state when it comes up.
from game.sim import GameUpdateEvents
self._create_missing_front_lines(GameUpdateEvents())
self._create_missing_front_lines(laser_code_registry, GameUpdateEvents())
@property
def front_line_db(self) -> Database[FrontLine]:
assert self._front_line_db is not None
return self._front_line_db
def _create_missing_front_lines(self, events: GameUpdateEvents) -> None:
def _create_missing_front_lines(
self, laser_code_registry: LaserCodeRegistry, events: GameUpdateEvents
) -> None:
for connection in self.convoy_routes.keys():
if not connection.front_line_active_with(
self
) and not connection.is_friendly_to(self):
self._create_front_line_with(connection, events)
self._create_front_line_with(laser_code_registry, connection, events)
def _create_front_line_with(
self, connection: ControlPoint, events: GameUpdateEvents
self,
laser_code_registry: LaserCodeRegistry,
connection: ControlPoint,
events: GameUpdateEvents,
) -> None:
blue, red = FrontLine.sort_control_points(self, connection)
front = FrontLine(blue, red)
front = FrontLine(blue, red, laser_code_registry.alloc_laser_code())
self.front_lines[connection] = front
connection.front_lines[self] = front
self.front_line_db.add(front.id, front)
events.update_front_line(front)
def _remove_front_line_with(
self, connection: ControlPoint, events: GameUpdateEvents
self,
connection: ControlPoint,
events: GameUpdateEvents,
) -> None:
front = self.front_lines[connection]
del self.front_lines[connection]
del connection.front_lines[self]
self.front_line_db.remove(front.id)
front.laser_code.release()
events.delete_front_line(front)
def _clear_front_lines(self, events: GameUpdateEvents) -> None:
@ -839,7 +848,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
self._coalition = new_coalition
self.base.set_strength_to_minimum()
self._clear_front_lines(events)
self._create_missing_front_lines(events)
self._create_missing_front_lines(game.laser_code_registry, events)
events.update_control_point(self)
# All the attached TGOs have either been depopulated or captured. Tell the UI to

View File

@ -8,6 +8,7 @@ from typing import Any, Iterator, List, TYPE_CHECKING, Tuple
from dcs.mapping import Point
from .missiontarget import MissionTarget
from ..lasercodes.lasercode import LaserCode
from ..utils import Heading, pairwise
if TYPE_CHECKING:
@ -49,10 +50,12 @@ class FrontLine(MissionTarget):
self,
blue_point: ControlPoint,
red_point: ControlPoint,
laser_code: LaserCode,
) -> None:
self.id = uuid.uuid4()
self.blue_cp = blue_point
self.red_cp = red_point
self.laser_code = laser_code
try:
route = list(blue_point.convoy_route_to(red_point))
except KeyError:

View File

@ -196,6 +196,12 @@ class QFlightCreator(QDialog):
roster=roster,
)
for member in flight.iter_members():
if member.is_player:
member.assign_tgp_laser_code(
self.game.laser_code_registry.alloc_laser_code()
)
# noinspection PyUnresolvedReferences
self.created.emit(flight)
self.accept()