Speed up game tick by caching TGO threat regions.

Still more could be done here by caching the merged poly at the theater
level, but this goes a long way.

Aircraft commit regions are already cached (in the FlightState), so
those are already fairly fast. The combined A2A commit boundary could
also potentially be cached at the theater level.
This commit is contained in:
Dan Albert 2022-03-07 21:19:04 -08:00
parent 453f6ac74a
commit 73a8ec02b2
3 changed files with 36 additions and 13 deletions

View File

@ -1,12 +1,12 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Iterator from collections.abc import Iterator
from typing import Any, Optional, TYPE_CHECKING from typing import TYPE_CHECKING
from dcs import Point from dcs import Point
from shapely.ops import unary_union from shapely.ops import unary_union
from game.utils import dcs_to_shapely_point, meters from game.utils import dcs_to_shapely_point
if TYPE_CHECKING: if TYPE_CHECKING:
from game.theater import ConflictTheater, TheaterGroundObject from game.theater import ConflictTheater, TheaterGroundObject
@ -36,14 +36,7 @@ class SamEngagementZones:
individual_zones = [] individual_zones = []
for cp in theater.control_points_for(player): for cp in theater.control_points_for(player):
for tgo in cp.connected_objectives: for tgo in cp.connected_objectives:
if (region := cls.threat_region(tgo)) is not None: if (region := tgo.threat_poly()) is not None:
commit_regions.append(region) commit_regions.append(region)
individual_zones.append((tgo, region)) individual_zones.append((tgo, region))
return SamEngagementZones(unary_union(commit_regions), individual_zones) return SamEngagementZones(unary_union(commit_regions), individual_zones)
@classmethod
def threat_region(cls, tgo: TheaterGroundObject) -> Optional[ThreatPoly]:
threat_range = tgo.max_threat_range()
if threat_range <= meters(0):
return None
return dcs_to_shapely_point(tgo.position).buffer(threat_range.meters)

View File

@ -3,10 +3,11 @@ from __future__ import annotations
import itertools import itertools
import uuid import uuid
from abc import ABC from abc import ABC
from typing import Iterator, List, Optional, TYPE_CHECKING from typing import Any, Iterator, List, Optional, TYPE_CHECKING
from dcs.mapping import Point from dcs.mapping import Point
from dcs.unittype import VehicleType from dcs.unittype import VehicleType
from shapely.geometry import Point as ShapelyPoint
from game.sidc import ( from game.sidc import (
Entity, Entity,
@ -19,15 +20,16 @@ from game.sidc import (
Status, Status,
SymbolSet, SymbolSet,
) )
from .missiontarget import MissionTarget
from ..data.radar_db import LAUNCHER_TRACKER_PAIRS, TELARS, TRACK_RADARS from ..data.radar_db import LAUNCHER_TRACKER_PAIRS, TELARS, TRACK_RADARS
from ..utils import Distance, Heading, meters from ..utils import Distance, Heading, meters
if TYPE_CHECKING: if TYPE_CHECKING:
from game.ato.flighttype import FlightType
from game.threatzones import ThreatPoly
from .theatergroup import TheaterUnit, TheaterGroup from .theatergroup import TheaterUnit, TheaterGroup
from .controlpoint import ControlPoint from .controlpoint import ControlPoint
from ..ato.flighttype import FlightType
from .missiontarget import MissionTarget
NAME_BY_CATEGORY = { NAME_BY_CATEGORY = {
"ewr": "Early Warning Radar", "ewr": "Early Warning Radar",
@ -69,6 +71,16 @@ class TheaterGroundObject(MissionTarget, SidcDescribable, ABC):
self.control_point = control_point self.control_point = control_point
self.sea_object = sea_object self.sea_object = sea_object
self.groups: List[TheaterGroup] = [] self.groups: List[TheaterGroup] = []
self._threat_poly: ThreatPoly | None = None
def __getstate__(self) -> dict[str, Any]:
state = self.__dict__.copy()
del state["_threat_poly"]
return state
def __setstate__(self, state: dict[str, Any]) -> None:
state["_threat_poly"] = None
self.__dict__.update(state)
@property @property
def sidc_status(self) -> Status: def sidc_status(self) -> Status:
@ -193,6 +205,22 @@ class TheaterGroundObject(MissionTarget, SidcDescribable, ABC):
def threat_range(self, group: TheaterGroup, radar_only: bool = False) -> Distance: def threat_range(self, group: TheaterGroup, radar_only: bool = False) -> Distance:
return self._max_range_of_type(group, "threat_range") return self._max_range_of_type(group, "threat_range")
def threat_poly(self) -> ThreatPoly | None:
if self._threat_poly is None:
self._threat_poly = self._make_threat_poly()
return self._threat_poly
def invalidate_threat_poly(self) -> None:
self._threat_poly = None
def _make_threat_poly(self) -> ThreatPoly | None:
threat_range = self.max_threat_range()
if not threat_range:
return None
point = ShapelyPoint(self.position.x, self.position.y)
return point.buffer(threat_range.meters)
@property @property
def is_ammo_depot(self) -> bool: def is_ammo_depot(self) -> bool:
return self.category == "ammo" return self.category == "ammo"
@ -215,6 +243,7 @@ class TheaterGroundObject(MissionTarget, SidcDescribable, ABC):
yield self.position yield self.position
def clear(self) -> None: def clear(self) -> None:
self.invalidate_threat_poly()
self.groups = [] self.groups = []
@property @property

View File

@ -58,6 +58,7 @@ class TheaterUnit:
def kill(self, events: GameUpdateEvents) -> None: def kill(self, events: GameUpdateEvents) -> None:
self.alive = False self.alive = False
self.ground_object.invalidate_threat_poly()
events.update_tgo(self.ground_object) events.update_tgo(self.ground_object)
@property @property