A corporation manager and dashboard for EVE Online
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

298 lines
8.0 KiB

  1. # -*- coding: utf-8 -*-
  2. import gzip
  3. from threading import Lock
  4. import yaml
  5. __all__ = ["Universe"]
  6. class _UniqueObject:
  7. """Base class for uniquely ID'd objects in the universe."""
  8. def __init__(self, universe, id_, data):
  9. self._universe = universe
  10. self._id = id_
  11. self._data = data
  12. @property
  13. def id(self):
  14. """The object's unique ID, as an integer."""
  15. return self._id
  16. class _SolarSystem(_UniqueObject):
  17. """Represents a solar system."""
  18. @property
  19. def name(self):
  20. """The solar system's name, as a string."""
  21. return self._data["name"]
  22. @property
  23. def constellation(self):
  24. """The solar system's constellation, as a _Constellation object."""
  25. return self._universe.constellation(self._data["constellation"])
  26. @property
  27. def region(self):
  28. """The solar system's region, as a _Region object."""
  29. return self._universe.region(self._data["region"])
  30. @property
  31. def security(self):
  32. """The solar system's security status, as a float."""
  33. return self._data["security"]
  34. @property
  35. def faction(self):
  36. """The solar system's faction, as a _Faction object, or None."""
  37. if "faction" in self._data:
  38. return self._universe.faction(self._data["faction"])
  39. return self.constellation.faction
  40. @property
  41. def is_nullsec(self):
  42. """Whether the solar system is in nullsec."""
  43. return self.security < 0.05
  44. @property
  45. def is_lowsec(self):
  46. """Whether the solar system is in nullsec."""
  47. return self.security >= 0.05 and self.security < 0.45
  48. @property
  49. def is_highsec(self):
  50. """Whether the solar system is in nullsec."""
  51. return self.security >= 0.45
  52. class _Constellation(_UniqueObject):
  53. """Represents a constellation."""
  54. @property
  55. def name(self):
  56. """The constellation's name, as a string."""
  57. return self._data["name"]
  58. @property
  59. def region(self):
  60. """The constellation's region, as a _Region object."""
  61. return self._universe.region(self._data["region"])
  62. @property
  63. def faction(self):
  64. """The constellation's faction, as a _Faction object, or None."""
  65. if "faction" in self._data:
  66. return self._universe.faction(self._data["faction"])
  67. return self.region.faction
  68. class _Region(_UniqueObject):
  69. """Represents a region."""
  70. @property
  71. def name(self):
  72. """The region's name, as a string."""
  73. return self._data["name"]
  74. @property
  75. def faction(self):
  76. """The region's faction, as a _Faction object, or None."""
  77. if "faction" in self._data:
  78. return self._universe.faction(self._data["faction"])
  79. return None
  80. class _Faction(_UniqueObject):
  81. """Represents a faction."""
  82. @property
  83. def name(self):
  84. """The faction's name, as a string."""
  85. return self._data["name"]
  86. class _Killable(_UniqueObject):
  87. """Represents a killable object, like a ship, structure, or fighter."""
  88. def __init__(self, universe, kid, cat, data):
  89. super().__init__(universe, kid, data)
  90. self._cat = cat
  91. @property
  92. def name(self):
  93. """The killable object's name, as a string."""
  94. return self._data["name"]
  95. @property
  96. def group(self):
  97. """The killable object's group, as a string."""
  98. return self._data["group"]
  99. @property
  100. def is_ship(self):
  101. """Whether the killable object is a ship."""
  102. return self._cat == "ships"
  103. @property
  104. def is_structure(self):
  105. """Whether the killable object is a structure."""
  106. return self._cat == "structures"
  107. @property
  108. def is_fighter(self):
  109. """Whether the killable object is a fighter."""
  110. return self._cat == "fighters"
  111. class _DummySolarSystem(_SolarSystem):
  112. """Represents an unknown or invalid solar system."""
  113. def __init__(self, universe):
  114. super().__init__(universe, -1, {
  115. "name": "Unknown",
  116. "constellation": -1,
  117. "region": -1,
  118. "security": 0.0
  119. })
  120. class _DummyConstellation(_Constellation):
  121. """Represents an unknown or invalid constellation."""
  122. def __init__(self, universe):
  123. super().__init__(universe, -1, {
  124. "name": "Unknown",
  125. "region": -1
  126. })
  127. class _DummyRegion(_Region):
  128. """Represents an unknown or invalid region."""
  129. def __init__(self, universe):
  130. super().__init__(universe, -1, {
  131. "name": "Unknown"
  132. })
  133. class _DummyFaction(_Faction):
  134. """Represents an unknown or invalid faction."""
  135. def __init__(self, universe):
  136. super().__init__(universe, -1, {
  137. "name": "Unknown"
  138. })
  139. class _DummyKillable(_Killable):
  140. """Represents an unknown or invalid killable object."""
  141. def __init__(self, universe):
  142. super().__init__(universe, -1, None, {
  143. "name": "Unknown",
  144. "group": "Unknown"
  145. })
  146. class Universe:
  147. """EVE API module for static universe data."""
  148. def __init__(self, datadir):
  149. self._dir = datadir
  150. self._lock = Lock()
  151. self._loaded = False
  152. self._systems = {}
  153. self._constellations = {}
  154. self._regions = {}
  155. self._factions = {}
  156. self._killable_idx = {}
  157. self._killable_tab = {}
  158. @staticmethod
  159. def _load_yaml(path):
  160. """Load in and return a YAML file with the given path."""
  161. with gzip.open(str(path), "rb") as fp:
  162. return yaml.load(fp, Loader=yaml.CLoader)
  163. def _load(self):
  164. """Load in universe data. This can be called multiple times safely."""
  165. if self._loaded:
  166. return
  167. with self._lock:
  168. if self._loaded:
  169. return
  170. galaxy = self._load_yaml(self._dir / "galaxy.yml.gz")
  171. self._systems = galaxy["systems"]
  172. self._constellations = galaxy["constellations"]
  173. self._regions = galaxy["regions"]
  174. del galaxy
  175. entities = self._load_yaml(self._dir / "entities.yml.gz")
  176. self._factions = entities["factions"]
  177. del entities
  178. types = self._load_yaml(self._dir / "types.yml.gz")
  179. self._killable_idx = {kid: cat for cat, kids in types.items()
  180. for kid in kids}
  181. self._killable_tab = types
  182. del types
  183. self._loaded = True
  184. def system(self, sid):
  185. """Return a _SolarSystem with the given ID.
  186. If the ID is invalid, return a dummy unknown object with ID -1.
  187. """
  188. self._load()
  189. if sid not in self._systems:
  190. return _DummySolarSystem(self)
  191. return _SolarSystem(self, sid, self._systems[sid])
  192. def constellation(self, cid):
  193. """Return a _Constellation with the given ID.
  194. If the ID is invalid, return a dummy unknown object with ID -1.
  195. """
  196. self._load()
  197. if cid not in self._constellations:
  198. return _DummyConstellation(self)
  199. return _Constellation(self, cid, self._constellations[cid])
  200. def region(self, rid):
  201. """Return a _Region with the given ID.
  202. If the ID is invalid, return a dummy unknown object with ID -1.
  203. """
  204. self._load()
  205. if rid not in self._regions:
  206. return _DummyRegion(self)
  207. return _Region(self, rid, self._regions[rid])
  208. def faction(self, fid):
  209. """Return a _Faction with the given ID.
  210. If the ID is invalid, return a dummy unknown object with ID -1.
  211. """
  212. self._load()
  213. if fid not in self._factions:
  214. return _DummyFaction(self)
  215. return _Faction(self, fid, self._factions[fid])
  216. def killable(self, kid):
  217. """Return a _Killable with the given ID.
  218. If the ID is invalid, return a dummy unknown object with ID -1.
  219. """
  220. self._load()
  221. if kid not in self._killable_idx:
  222. return _DummyKillable(self)
  223. cat = self._killable_idx[kid]
  224. return _Killable(self, kid, cat, self._killable_tab[cat][kid])