mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Add FastAPI interface between the game and map.
A possible explanation for the infrequent CTDs we've been seeing since adding fast forward is that QWebChannel doesn't keep a reference to the python objects that it passes to js, so if the object is GC'd before the front end is done with it, it crashes. We don't really like QWebChannel anyway, so this begins replacing that with FastAPI.
This commit is contained in:
@@ -9,7 +9,7 @@ from typing import Optional
|
||||
from PySide2 import QtWidgets
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2.QtGui import QPixmap
|
||||
from PySide2.QtWidgets import QApplication, QSplashScreen, QCheckBox
|
||||
from PySide2.QtWidgets import QApplication, QCheckBox, QSplashScreen
|
||||
from dcs.payloads import PayloadDirectories
|
||||
|
||||
from game import Game, VERSION, persistency
|
||||
@@ -18,6 +18,7 @@ from game.data.weapons import Pylon, Weapon, WeaponGroup
|
||||
from game.db import FACTIONS
|
||||
from game.dcs.aircrafttype import AircraftType
|
||||
from game.profiling import logged_duration
|
||||
from game.server import GameContext, Server
|
||||
from game.settings import Settings
|
||||
from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings
|
||||
from qt_ui import (
|
||||
@@ -135,6 +136,7 @@ def run_ui(game: Optional[Game]) -> None:
|
||||
|
||||
# Apply CSS (need works)
|
||||
GameUpdateSignal()
|
||||
GameUpdateSignal.get_instance().game_loaded.connect(GameContext.set)
|
||||
|
||||
# Start window
|
||||
window = QLiberationWindow(game)
|
||||
@@ -333,7 +335,8 @@ def main():
|
||||
lint_weapon_data_for_aircraft(AircraftType.named(args.aircraft))
|
||||
return
|
||||
|
||||
run_ui(game)
|
||||
with Server().run_in_thread():
|
||||
run_ui(game)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -10,6 +10,7 @@ from PySide2.QtCore import QUrl
|
||||
from PySide2.QtWebChannel import QWebChannel
|
||||
from PySide2.QtWebEngineWidgets import (
|
||||
QWebEnginePage,
|
||||
QWebEngineSettings,
|
||||
QWebEngineView,
|
||||
)
|
||||
|
||||
@@ -48,6 +49,11 @@ class QLiberationMap(QWebEngineView):
|
||||
self.channel.registerObject("game", self.map_model)
|
||||
|
||||
self.page = LoggingWebPage(self)
|
||||
# Required to allow "cross-origin" access from file:// scoped canvas.html to the
|
||||
# localhost HTTP backend.
|
||||
self.page.settings().setAttribute(
|
||||
QWebEngineSettings.LocalContentCanAccessRemoteUrls, True
|
||||
)
|
||||
self.page.setWebChannel(self.channel)
|
||||
self.page.load(
|
||||
QUrl.fromLocalFile(str(Path("resources/ui/map/canvas.html").resolve()))
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from PySide2.QtCore import Property, QObject, Signal
|
||||
|
||||
from game.server.leaflet import LeafletPoly, ShapelyUtil
|
||||
from game.sim.combat.aircombat import AirCombat
|
||||
from game.theater import ConflictTheater
|
||||
from .leaflet import LeafletPoly
|
||||
from .shapelyutil import ShapelyUtil
|
||||
|
||||
|
||||
class AirCombatJs(QObject):
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Set to True to enable computing expensive debugging information. At the time of
|
||||
# writing this only controls computing the waypoint placement zones.
|
||||
ENABLE_EXPENSIVE_DEBUG_TOOLS = False
|
||||
@@ -5,12 +5,12 @@ from typing import Optional
|
||||
from PySide2.QtCore import Property, QObject, Signal, Slot
|
||||
from dcs import Point
|
||||
|
||||
from game.server.leaflet import LeafletLatLon
|
||||
from game.theater import ConflictTheater, ControlPoint, ControlPointStatus, LatLon
|
||||
from game.utils import meters, nautical_miles
|
||||
from qt_ui.dialogs import Dialog
|
||||
from qt_ui.models import GameModel
|
||||
from qt_ui.windows.basemenu.QBaseMenu2 import QBaseMenu2
|
||||
from .leaflet import LeafletLatLon
|
||||
|
||||
MAX_SHIP_DISTANCE = nautical_miles(80)
|
||||
|
||||
|
||||
@@ -8,12 +8,11 @@ from shapely.geometry import LineString, Point as ShapelyPoint
|
||||
from game.ato import Flight, FlightWaypoint
|
||||
from game.ato.flightstate import InFlight
|
||||
from game.ato.flightwaypointtype import FlightWaypointType
|
||||
from game.server.leaflet import LeafletLatLon, LeafletPoly, ShapelyUtil
|
||||
from game.theater import ConflictTheater
|
||||
from game.utils import meters
|
||||
from gen.flights.flightplan import CasFlightPlan, PatrollingFlightPlan
|
||||
from qt_ui.models import AtoModel
|
||||
from .leaflet import LeafletLatLon, LeafletPoly
|
||||
from .shapelyutil import ShapelyUtil
|
||||
from .waypointjs import WaypointJs
|
||||
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ from typing import List
|
||||
|
||||
from PySide2.QtCore import Property, QObject, Signal, Slot
|
||||
|
||||
from game.server.leaflet import LeafletLatLon
|
||||
from game.theater import ConflictTheater, FrontLine
|
||||
from game.utils import nautical_miles
|
||||
from qt_ui.dialogs import Dialog
|
||||
from .leaflet import LeafletLatLon
|
||||
|
||||
|
||||
class FrontLineJs(QObject):
|
||||
|
||||
@@ -8,9 +8,9 @@ from dcs.vehicles import vehicle_map
|
||||
|
||||
from game import Game
|
||||
from game.dcs.groundunittype import GroundUnitType
|
||||
from game.server.leaflet import LeafletLatLon
|
||||
from game.theater import TheaterGroundObject
|
||||
from qt_ui.dialogs import Dialog
|
||||
from qt_ui.widgets.map.model.leaflet import LeafletLatLon
|
||||
from qt_ui.windows.groundobject.QGroundObjectMenu import QGroundObjectMenu
|
||||
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from game import Game
|
||||
from game.ato import Flight
|
||||
from game.flightplan import HoldZoneGeometry
|
||||
from .config import ENABLE_EXPENSIVE_DEBUG_TOOLS
|
||||
from .leaflet import LeafletPoly
|
||||
from .shapelyutil import ShapelyUtil
|
||||
|
||||
|
||||
class HoldZonesJs(BaseModel):
|
||||
home_bubble: LeafletPoly = Field(alias="homeBubble")
|
||||
target_bubble: LeafletPoly = Field(alias="targetBubble")
|
||||
join_bubble: LeafletPoly = Field(alias="joinBubble")
|
||||
excluded_zones: list[LeafletPoly] = Field(alias="excludedZones")
|
||||
permissible_zones: list[LeafletPoly] = Field(alias="permissibleZones")
|
||||
preferred_lines: list[LeafletPoly] = Field(alias="preferredLines")
|
||||
|
||||
@classmethod
|
||||
def empty(cls) -> HoldZonesJs:
|
||||
return HoldZonesJs(
|
||||
homeBubble=[],
|
||||
targetBubble=[],
|
||||
joinBubble=[],
|
||||
excludedZones=[],
|
||||
permissibleZones=[],
|
||||
preferredLines=[],
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def for_flight(cls, flight: Flight, game: Game) -> HoldZonesJs:
|
||||
if not ENABLE_EXPENSIVE_DEBUG_TOOLS:
|
||||
return HoldZonesJs.empty()
|
||||
target = flight.package.target
|
||||
home = flight.departure
|
||||
if flight.package.waypoints is None:
|
||||
return HoldZonesJs.empty()
|
||||
ip = flight.package.waypoints.ingress
|
||||
join = flight.package.waypoints.join
|
||||
geometry = HoldZoneGeometry(
|
||||
target.position, home.position, ip, join, game.blue, game.theater
|
||||
)
|
||||
return HoldZonesJs(
|
||||
homeBubble=ShapelyUtil.poly_to_leaflet(geometry.home_bubble, game.theater),
|
||||
targetBubble=ShapelyUtil.poly_to_leaflet(
|
||||
geometry.target_bubble, game.theater
|
||||
),
|
||||
joinBubble=ShapelyUtil.poly_to_leaflet(geometry.join_bubble, game.theater),
|
||||
excludedZones=ShapelyUtil.polys_to_leaflet(
|
||||
geometry.excluded_zones, game.theater
|
||||
),
|
||||
permissibleZones=ShapelyUtil.polys_to_leaflet(
|
||||
geometry.permissible_zones, game.theater
|
||||
),
|
||||
preferredLines=ShapelyUtil.lines_to_leaflet(
|
||||
geometry.preferred_lines, game.theater
|
||||
),
|
||||
)
|
||||
@@ -1,37 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from game import Game
|
||||
from game.ato import Flight
|
||||
from game.flightplan import IpZoneGeometry
|
||||
from .config import ENABLE_EXPENSIVE_DEBUG_TOOLS
|
||||
from .leaflet import LeafletPoly
|
||||
from .shapelyutil import ShapelyUtil
|
||||
|
||||
|
||||
class IpZonesJs(BaseModel):
|
||||
home_bubble: LeafletPoly = Field(alias="homeBubble")
|
||||
ipBubble: LeafletPoly = Field(alias="ipBubble")
|
||||
permissibleZone: LeafletPoly = Field(alias="permissibleZone")
|
||||
safeZones: list[LeafletPoly] = Field(alias="safeZones")
|
||||
|
||||
@classmethod
|
||||
def empty(cls) -> IpZonesJs:
|
||||
return IpZonesJs(homeBubble=[], ipBubble=[], permissibleZone=[], safeZones=[])
|
||||
|
||||
@classmethod
|
||||
def for_flight(cls, flight: Flight, game: Game) -> IpZonesJs:
|
||||
if not ENABLE_EXPENSIVE_DEBUG_TOOLS:
|
||||
return IpZonesJs.empty()
|
||||
target = flight.package.target
|
||||
home = flight.departure
|
||||
geometry = IpZoneGeometry(target.position, home.position, game.blue)
|
||||
return IpZonesJs(
|
||||
homeBubble=ShapelyUtil.poly_to_leaflet(geometry.home_bubble, game.theater),
|
||||
ipBubble=ShapelyUtil.poly_to_leaflet(geometry.ip_bubble, game.theater),
|
||||
permissibleZone=ShapelyUtil.poly_to_leaflet(
|
||||
geometry.permissible_zone, game.theater
|
||||
),
|
||||
safeZones=ShapelyUtil.polys_to_leaflet(geometry.safe_zones, game.theater),
|
||||
)
|
||||
@@ -1,58 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import Field
|
||||
from pydantic.main import BaseModel
|
||||
|
||||
from game import Game
|
||||
from game.ato import Flight
|
||||
from game.flightplan import JoinZoneGeometry
|
||||
from .config import ENABLE_EXPENSIVE_DEBUG_TOOLS
|
||||
from .leaflet import LeafletPoly
|
||||
from .shapelyutil import ShapelyUtil
|
||||
|
||||
|
||||
class JoinZonesJs(BaseModel):
|
||||
home_bubble: LeafletPoly = Field(alias="homeBubble")
|
||||
target_bubble: LeafletPoly = Field(alias="targetBubble")
|
||||
ip_bubble: LeafletPoly = Field(alias="ipBubble")
|
||||
excluded_zones: list[LeafletPoly] = Field(alias="excludedZones")
|
||||
permissible_zones: list[LeafletPoly] = Field(alias="permissibleZones")
|
||||
preferred_lines: list[LeafletPoly] = Field(alias="preferredLines")
|
||||
|
||||
@classmethod
|
||||
def empty(cls) -> JoinZonesJs:
|
||||
return JoinZonesJs(
|
||||
homeBubble=[],
|
||||
targetBubble=[],
|
||||
ipBubble=[],
|
||||
excludedZones=[],
|
||||
permissibleZones=[],
|
||||
preferredLines=[],
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def for_flight(cls, flight: Flight, game: Game) -> JoinZonesJs:
|
||||
if not ENABLE_EXPENSIVE_DEBUG_TOOLS:
|
||||
return JoinZonesJs.empty()
|
||||
target = flight.package.target
|
||||
home = flight.departure
|
||||
if flight.package.waypoints is None:
|
||||
return JoinZonesJs.empty()
|
||||
ip = flight.package.waypoints.ingress
|
||||
geometry = JoinZoneGeometry(target.position, home.position, ip, game.blue)
|
||||
return JoinZonesJs(
|
||||
homeBubble=ShapelyUtil.poly_to_leaflet(geometry.home_bubble, game.theater),
|
||||
targetBubble=ShapelyUtil.poly_to_leaflet(
|
||||
geometry.target_bubble, game.theater
|
||||
),
|
||||
ipBubble=ShapelyUtil.poly_to_leaflet(geometry.ip_bubble, game.theater),
|
||||
excludedZones=ShapelyUtil.polys_to_leaflet(
|
||||
geometry.excluded_zones, game.theater
|
||||
),
|
||||
permissibleZones=ShapelyUtil.polys_to_leaflet(
|
||||
geometry.permissible_zones, game.theater
|
||||
),
|
||||
preferredLines=ShapelyUtil.lines_to_leaflet(
|
||||
geometry.preferred_lines, game.theater
|
||||
),
|
||||
)
|
||||
@@ -1,4 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
LeafletLatLon = list[float]
|
||||
LeafletPoly = list[LeafletLatLon]
|
||||
@@ -1,6 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
@@ -10,6 +9,7 @@ from dcs import Point
|
||||
from game import Game
|
||||
from game.ato.airtaaskingorder import AirTaskingOrder
|
||||
from game.profiling import logged_duration
|
||||
from game.server.leaflet import LeafletLatLon
|
||||
from game.sim.combat import FrozenCombat
|
||||
from game.sim.combat.aircombat import AirCombat
|
||||
from game.sim.combat.atip import AtIp
|
||||
@@ -26,11 +26,7 @@ from .controlpointjs import ControlPointJs
|
||||
from .flightjs import FlightJs
|
||||
from .frontlinejs import FrontLineJs
|
||||
from .groundobjectjs import GroundObjectJs
|
||||
from .holdzonesjs import HoldZonesJs
|
||||
from .ipcombatjs import IpCombatJs
|
||||
from .ipzonesjs import IpZonesJs
|
||||
from .joinzonesjs import JoinZonesJs
|
||||
from .leaflet import LeafletLatLon
|
||||
from .mapzonesjs import MapZonesJs
|
||||
from .navmeshjs import NavMeshJs
|
||||
from .samcombatjs import SamCombatJs
|
||||
@@ -69,12 +65,10 @@ class MapModel(QObject):
|
||||
navmeshesChanged = Signal()
|
||||
mapZonesChanged = Signal()
|
||||
unculledZonesChanged = Signal()
|
||||
ipZonesChanged = Signal()
|
||||
joinZonesChanged = Signal()
|
||||
holdZonesChanged = Signal()
|
||||
airCombatsChanged = Signal()
|
||||
samCombatsChanged = Signal()
|
||||
ipCombatsChanged = Signal()
|
||||
selectedFlightChanged = Signal(str)
|
||||
|
||||
def __init__(self, game_model: GameModel, sim_controller: SimController) -> None:
|
||||
super().__init__()
|
||||
@@ -91,9 +85,6 @@ class MapModel(QObject):
|
||||
self._navmeshes = NavMeshJs([], [])
|
||||
self._map_zones = MapZonesJs([], [], [])
|
||||
self._unculled_zones = []
|
||||
self._ip_zones = IpZonesJs.empty()
|
||||
self._join_zones = JoinZonesJs.empty()
|
||||
self._hold_zones = HoldZonesJs.empty()
|
||||
self._selected_flight_index: Optional[Tuple[int, int]] = None
|
||||
self._air_combats = []
|
||||
self._sam_combats = []
|
||||
@@ -128,7 +119,6 @@ class MapModel(QObject):
|
||||
self._navmeshes = NavMeshJs([], [])
|
||||
self._map_zones = MapZonesJs([], [], [])
|
||||
self._unculled_zones = []
|
||||
self._ip_zones = IpZonesJs.empty()
|
||||
self._air_combats = []
|
||||
self._sam_combats = []
|
||||
self._ip_combats = []
|
||||
@@ -155,7 +145,6 @@ class MapModel(QObject):
|
||||
else:
|
||||
self._selected_flight_index = index, 0
|
||||
self.select_current_flight()
|
||||
self.reset_debug_zones()
|
||||
|
||||
def set_flight_selection(self, index: int) -> None:
|
||||
self.deselect_current_flight()
|
||||
@@ -174,7 +163,6 @@ class MapModel(QObject):
|
||||
self._selected_flight_index = self._selected_flight_index[0], None
|
||||
self._selected_flight_index = self._selected_flight_index[0], index
|
||||
self.select_current_flight()
|
||||
self.reset_debug_zones()
|
||||
|
||||
@property
|
||||
def _selected_flight(self) -> Optional[FlightJs]:
|
||||
@@ -193,8 +181,10 @@ class MapModel(QObject):
|
||||
def select_current_flight(self):
|
||||
flight = self._selected_flight
|
||||
if flight is None:
|
||||
self.selectedFlightChanged.emit(None)
|
||||
return None
|
||||
flight.set_selected(True)
|
||||
self.selectedFlightChanged.emit(str(flight.flight.id))
|
||||
|
||||
@staticmethod
|
||||
def leaflet_coord_for(point: Point, theater: ConflictTheater) -> LeafletLatLon:
|
||||
@@ -249,23 +239,6 @@ class MapModel(QObject):
|
||||
self.game.blue.ato, blue=True
|
||||
) | self._flights_in_ato(self.game.red.ato, blue=False)
|
||||
self.flightsChanged.emit()
|
||||
self.reset_debug_zones()
|
||||
|
||||
def reset_debug_zones(self) -> None:
|
||||
selected_flight = None
|
||||
if self._selected_flight is not None:
|
||||
selected_flight = self._selected_flight.flight
|
||||
if selected_flight is None:
|
||||
self._ip_zones = IpZonesJs.empty()
|
||||
self._join_zones = JoinZonesJs.empty()
|
||||
self._hold_zones = HoldZonesJs.empty()
|
||||
else:
|
||||
self._ip_zones = IpZonesJs.for_flight(selected_flight, self.game)
|
||||
self._join_zones = JoinZonesJs.for_flight(selected_flight, self.game)
|
||||
self._hold_zones = HoldZonesJs.for_flight(selected_flight, self.game)
|
||||
self.ipZonesChanged.emit()
|
||||
self.joinZonesChanged.emit()
|
||||
self.holdZonesChanged.emit()
|
||||
|
||||
@Property(list, notify=flightsChanged)
|
||||
def flights(self) -> list[FlightJs]:
|
||||
@@ -397,20 +370,6 @@ class MapModel(QObject):
|
||||
def unculledZones(self) -> list[UnculledZone]:
|
||||
return self._unculled_zones
|
||||
|
||||
@Property(str, notify=ipZonesChanged)
|
||||
def ipZones(self) -> str:
|
||||
return json.dumps(self._ip_zones.dict(by_alias=True))
|
||||
|
||||
@Property(str, notify=joinZonesChanged)
|
||||
def joinZones(self) -> str:
|
||||
# Must be dumped as a string and deserialized in js because QWebChannel can't
|
||||
# handle a dict. Can be cleaned up by switching from QWebChannel to FastAPI.
|
||||
return json.dumps(self._join_zones.dict(by_alias=True))
|
||||
|
||||
@Property(str, notify=holdZonesChanged)
|
||||
def holdZones(self) -> str:
|
||||
return json.dumps(self._hold_zones.dict(by_alias=True))
|
||||
|
||||
def reset_combats(self) -> None:
|
||||
self._air_combats = []
|
||||
self._sam_combats = []
|
||||
|
||||
@@ -3,8 +3,7 @@ from __future__ import annotations
|
||||
from PySide2.QtCore import Property, QObject, Signal
|
||||
|
||||
from game import Game
|
||||
from .leaflet import LeafletPoly
|
||||
from .shapelyutil import ShapelyUtil
|
||||
from game.server.leaflet import LeafletPoly, ShapelyUtil
|
||||
|
||||
|
||||
class MapZonesJs(QObject):
|
||||
|
||||
@@ -4,8 +4,8 @@ from PySide2.QtCore import Property, QObject, Signal
|
||||
|
||||
from game import Game
|
||||
from game.navmesh import NavMesh
|
||||
from game.server.leaflet import LeafletPoly
|
||||
from game.theater import ConflictTheater
|
||||
from .leaflet import LeafletPoly
|
||||
from .navmeshpolyjs import NavMeshPolyJs
|
||||
|
||||
|
||||
|
||||
@@ -3,9 +3,8 @@ from __future__ import annotations
|
||||
from PySide2.QtCore import Property, QObject, Signal
|
||||
|
||||
from game.navmesh import NavMeshPoly
|
||||
from game.server.leaflet import LeafletPoly, ShapelyUtil
|
||||
from game.theater import ConflictTheater
|
||||
from .leaflet import LeafletPoly
|
||||
from .shapelyutil import ShapelyUtil
|
||||
|
||||
|
||||
class NavMeshPolyJs(QObject):
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
from typing import Union
|
||||
|
||||
from dcs import Point
|
||||
from shapely.geometry import LineString, MultiLineString, MultiPolygon, Polygon
|
||||
|
||||
from game.theater import ConflictTheater
|
||||
from .leaflet import LeafletLatLon, LeafletPoly
|
||||
|
||||
|
||||
class ShapelyUtil:
|
||||
@staticmethod
|
||||
def poly_to_leaflet(poly: Polygon, theater: ConflictTheater) -> LeafletPoly:
|
||||
if poly.is_empty:
|
||||
return []
|
||||
return [
|
||||
theater.point_to_ll(Point(x, y)).as_list() for x, y in poly.exterior.coords
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def polys_to_leaflet(
|
||||
cls, poly: Union[Polygon, MultiPolygon], theater: ConflictTheater
|
||||
) -> list[LeafletPoly]:
|
||||
if isinstance(poly, MultiPolygon):
|
||||
polys = poly.geoms
|
||||
else:
|
||||
polys = [poly]
|
||||
return [cls.poly_to_leaflet(poly, theater) for poly in polys]
|
||||
|
||||
@staticmethod
|
||||
def line_to_leaflet(
|
||||
line: LineString, theater: ConflictTheater
|
||||
) -> list[LeafletLatLon]:
|
||||
return [theater.point_to_ll(Point(x, y)).as_list() for x, y in line.coords]
|
||||
|
||||
@classmethod
|
||||
def lines_to_leaflet(
|
||||
cls, line_string: MultiLineString | LineString, theater: ConflictTheater
|
||||
) -> list[list[LeafletLatLon]]:
|
||||
if isinstance(line_string, MultiLineString):
|
||||
lines = line_string.geoms
|
||||
else:
|
||||
lines = [line_string]
|
||||
return [cls.line_to_leaflet(line, theater) for line in lines]
|
||||
@@ -5,9 +5,9 @@ from typing import List
|
||||
from PySide2.QtCore import Property, QObject, Signal
|
||||
|
||||
from game import Game
|
||||
from game.server.leaflet import LeafletLatLon
|
||||
from game.theater import ControlPoint
|
||||
from game.transfers import MultiGroupTransport, TransportMap
|
||||
from .leaflet import LeafletLatLon
|
||||
|
||||
|
||||
class SupplyRouteJs(QObject):
|
||||
|
||||
@@ -2,10 +2,9 @@ from __future__ import annotations
|
||||
|
||||
from PySide2.QtCore import Property, QObject, Signal
|
||||
|
||||
from game.server.leaflet import LeafletPoly, ShapelyUtil
|
||||
from game.theater import ConflictTheater
|
||||
from game.threatzones import ThreatZones
|
||||
from .leaflet import LeafletPoly
|
||||
from .shapelyutil import ShapelyUtil
|
||||
|
||||
|
||||
class ThreatZonesJs(QObject):
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Iterator
|
||||
from PySide2.QtCore import Property, QObject, Signal
|
||||
|
||||
from game import Game
|
||||
from .leaflet import LeafletLatLon
|
||||
from game.server.leaflet import LeafletLatLon
|
||||
|
||||
|
||||
class UnculledZone(QObject):
|
||||
|
||||
@@ -7,10 +7,10 @@ from PySide2.QtCore import Property, QObject, Signal, Slot
|
||||
|
||||
from game.ato import Flight, FlightWaypoint
|
||||
from game.ato.flightwaypointtype import FlightWaypointType
|
||||
from game.server.leaflet import LeafletLatLon
|
||||
from game.theater import ConflictTheater, LatLon
|
||||
from gen.flights.flightplan import FlightPlan
|
||||
from qt_ui.models import AtoModel
|
||||
from .leaflet import LeafletLatLon
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .flightjs import FlightJs
|
||||
|
||||
Reference in New Issue
Block a user