A corporation manager and dashboard for EVE Online
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 

341 lignes
9.2 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 _Type(_UniqueObject):
  87. """Represents any type, including ships and materials."""
  88. @property
  89. def name(self):
  90. """The item's name, as a string."""
  91. return self._data["name"]
  92. @property
  93. def group_id(self):
  94. """The item's group ID, as an integer."""
  95. return self._data["group_id"]
  96. @property
  97. def market_group_id(self):
  98. """The item's market group ID, as an integer, or None."""
  99. return self._data.get("market_group_id")
  100. class _Killable(_UniqueObject):
  101. """Represents a killable object, like a ship, structure, or fighter."""
  102. def __init__(self, universe, kid, cat, data):
  103. super().__init__(universe, kid, data)
  104. self._cat = cat
  105. @property
  106. def name(self):
  107. """The killable object's name, as a string."""
  108. return self._data["name"]
  109. @property
  110. def group(self):
  111. """The killable object's group, as a string."""
  112. return self._data["group"]
  113. @property
  114. def is_ship(self):
  115. """Whether the killable object is a ship."""
  116. return self._cat == "ships"
  117. @property
  118. def is_structure(self):
  119. """Whether the killable object is a structure."""
  120. return self._cat == "structures"
  121. @property
  122. def is_fighter(self):
  123. """Whether the killable object is a fighter."""
  124. return self._cat == "fighters"
  125. class _DummySolarSystem(_SolarSystem):
  126. """Represents an unknown or invalid solar system."""
  127. def __init__(self, universe):
  128. super().__init__(universe, -1, {
  129. "name": "Unknown",
  130. "constellation": -1,
  131. "region": -1,
  132. "security": 0.0
  133. })
  134. class _DummyConstellation(_Constellation):
  135. """Represents an unknown or invalid constellation."""
  136. def __init__(self, universe):
  137. super().__init__(universe, -1, {
  138. "name": "Unknown",
  139. "region": -1
  140. })
  141. class _DummyRegion(_Region):
  142. """Represents an unknown or invalid region."""
  143. def __init__(self, universe):
  144. super().__init__(universe, -1, {
  145. "name": "Unknown"
  146. })
  147. class _DummyFaction(_Faction):
  148. """Represents an unknown or invalid faction."""
  149. def __init__(self, universe):
  150. super().__init__(universe, -1, {
  151. "name": "Unknown"
  152. })
  153. class _DummyType(_Type):
  154. """Represents an unknown or invalid type."""
  155. def __init__(self, universe):
  156. super().__init__(universe, -1, {
  157. "name": "Unknown",
  158. "group_id": -1,
  159. "market_group_id": -1
  160. })
  161. class _DummyKillable(_Killable):
  162. """Represents an unknown or invalid killable object."""
  163. def __init__(self, universe):
  164. super().__init__(universe, -1, None, {
  165. "name": "Unknown",
  166. "group": "Unknown"
  167. })
  168. class Universe:
  169. """EVE API module for static universe data."""
  170. def __init__(self, datadir):
  171. self._dir = datadir
  172. self._lock = Lock()
  173. self._loaded = False
  174. self._systems = {}
  175. self._constellations = {}
  176. self._regions = {}
  177. self._factions = {}
  178. self._types = {}
  179. self._killable_idx = {}
  180. self._killable_tab = {}
  181. @staticmethod
  182. def _load_yaml(path):
  183. """Load in and return a YAML file with the given path."""
  184. with gzip.open(str(path), "rb") as fp:
  185. return yaml.load(fp, Loader=yaml.CLoader)
  186. def _load(self):
  187. """Load in universe data. This can be called multiple times safely."""
  188. if self._loaded:
  189. return
  190. with self._lock:
  191. if self._loaded:
  192. return
  193. galaxy = self._load_yaml(self._dir / "galaxy.yml.gz")
  194. self._systems = galaxy["systems"]
  195. self._constellations = galaxy["constellations"]
  196. self._regions = galaxy["regions"]
  197. del galaxy
  198. entities = self._load_yaml(self._dir / "entities.yml.gz")
  199. self._factions = entities["factions"]
  200. del entities
  201. self._types = self._load_yaml(self._dir / "types.yml.gz")
  202. killables = self._load_yaml(self._dir / "killables.yml.gz")
  203. self._killable_idx = {kid: cat for cat, kids in killables.items()
  204. for kid in kids}
  205. self._killable_tab = killables
  206. del killables
  207. self._loaded = True
  208. def system(self, sid):
  209. """Return a _SolarSystem with the given ID.
  210. If the ID is invalid, return a dummy unknown object with ID -1.
  211. """
  212. self._load()
  213. if sid not in self._systems:
  214. return _DummySolarSystem(self)
  215. return _SolarSystem(self, sid, self._systems[sid])
  216. def constellation(self, cid):
  217. """Return a _Constellation with the given ID.
  218. If the ID is invalid, return a dummy unknown object with ID -1.
  219. """
  220. self._load()
  221. if cid not in self._constellations:
  222. return _DummyConstellation(self)
  223. return _Constellation(self, cid, self._constellations[cid])
  224. def region(self, rid):
  225. """Return a _Region with the given ID.
  226. If the ID is invalid, return a dummy unknown object with ID -1.
  227. """
  228. self._load()
  229. if rid not in self._regions:
  230. return _DummyRegion(self)
  231. return _Region(self, rid, self._regions[rid])
  232. def faction(self, fid):
  233. """Return a _Faction with the given ID.
  234. If the ID is invalid, return a dummy unknown object with ID -1.
  235. """
  236. self._load()
  237. if fid not in self._factions:
  238. return _DummyFaction(self)
  239. return _Faction(self, fid, self._factions[fid])
  240. def type(self, tid):
  241. """Return a _Type with the given ID.
  242. If the ID is invalid, return a dummy unknown object with ID -1.
  243. """
  244. self._load()
  245. if tid not in self._types:
  246. return _DummyKillable(self)
  247. return _Type(self, tid, self._types[tid])
  248. def killable(self, kid):
  249. """Return a _Killable with the given ID.
  250. If the ID is invalid, return a dummy unknown object with ID -1.
  251. """
  252. self._load()
  253. if kid not in self._killable_idx:
  254. return _DummyKillable(self)
  255. cat = self._killable_idx[kid]
  256. return _Killable(self, kid, cat, self._killable_tab[cat][kid])