From 516c41b825aabd9794d30c2b9e88b31c7aad71bd Mon Sep 17 00:00:00 2001 From: monkeyman192 Date: Tue, 18 Nov 2025 00:22:25 +1100 Subject: [PATCH 1/4] Initial work on functions relating to ship mesh regen --- nmspy/data/types.py | 32 +++++++++++++++++++++++++++++++- tools/data.json | 15 +++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/nmspy/data/types.py b/nmspy/data/types.py index 8974d20..dc4ebf8 100644 --- a/nmspy/data/types.py +++ b/nmspy/data/types.py @@ -274,9 +274,32 @@ def StoreCurrentSystemSpaceStationEndpoint( ): ... +@partial_struct +class cGcPlayerShipOwnership(Structure): + # Both these found at the top of cGcPlayerShipOwnership::UpdateMeshRefresh + mbShouldRefreshMesh: Annotated[bool, Field(c_bool, 0xA690)] + mMeshRefreshState: Annotated[int, Field(c_uint32, 0xA694)] + + @function_hook( + "48 89 5C 24 ? 55 56 57 41 54 41 56 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 45" + ) + def UpdateMeshRefresh(self, this: "_Pointer[cGcPlayerShipOwnership]"): ... + + @function_hook( + "48 8B C4 55 53 56 48 8D A8 ? ? ? ? 48 81 EC ? ? ? ? 80 B9 ? ? ? ? ? 48 8B F1" + ) + def Update( + self, + this: "_Pointer[cGcPlayerShipOwnership]", + lfTimestep: Annotated[float, c_float], + ): ... + + @partial_struct class cGcGameState(Structure): mPlayerState: Annotated[cGcPlayerState, 0xA950] + # Found in cGcGameState:: Update + mPlayerShipOwnership: Annotated[cGcPlayerShipOwnership, 0xA2BD0] @function_hook("48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 88 54 24") def OnSaveProgressCompleted( @@ -292,11 +315,18 @@ def OnSaveProgressCompleted( def LoadFromPersistentStorage( self, this: "_Pointer[cGcGameState]", - leSlot: c_uint32, + leSlot: Annotated[int, c_uint32], a3: c_int32, lbNetworkClientLoad: Annotated[bool, c_bool], ) -> c_uint64: ... + @function_hook( + "48 8B C4 48 89 58 ? 48 89 70 ? 48 89 78 ? 55 41 54 41 55 41 56 41 57 48 8D A8 ? ? ? ? 48 81 EC ? ? ? ? F3 0F 10 91" + ) + def Update( + self, this: "_Pointer[cGcGameState]", lfTimeStep: Annotated[float, c_float] + ): ... + class cTkFSM(Structure): @function_hook( diff --git a/tools/data.json b/tools/data.json index f06544c..be00d73 100644 --- a/tools/data.json +++ b/tools/data.json @@ -743,5 +743,20 @@ "name": "Engine::AddGroupNode", "signature": "48 89 5C 24 ? 57 48 81 EC ? ? ? ? 44 8B D2 44 8B CA 41 C1 EA ? 41 81 E1 ? ? ? ? 48 8B D9 45 85 D2 0F 84 ? ? ? ? 41 81 F9 ? ? ? ? 0F 84 ? ? ? ? 8B CA 48 8B 15 ? ? ? ? 81 E1 ? ? ? ? 48 8B 82 ? ? ? ? 48 63 0C 88 48 8B 82 ? ? ? ? 48 8B 3C C8 48 85 FF 74 ? 8B 4F ? 8B C1 25 ? ? ? ? 41 3B C1 75 ? C1 E9 ? 41 3B CA 75 ? 4C 8B CF 48 8D 4C 24 ? BA ? ? ? ? E8 ? ? ? ? 48 8B 0D ? ? ? ? 48 8D 05 ? ? ? ? 4C 8D 4C 24 ? 48 89 44 24 ? 41 B8 ? ? ? ? 48 89 7C 24 ? 48 8B D3 E8 ? ? ? ? 48 8D 4C 24 ? E8 ? ? ? ? EB ? C7 03 ? ? ? ? 48 8B C3 48 8B 9C 24 ? ? ? ? 48 81 C4 ? ? ? ? 5F C3 CC CC CC CC CC 48 89 5C 24", "mangled_name": "?AddGroupNode@Engine@@YA?AVTkHandle@@V2@PEBD@Z" + }, + { + "name": "cGcPlayerShipOwnership::UpdateMeshRefresh", + "signature": "48 89 5C 24 ? 55 56 57 41 54 41 56 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 45", + "mangled_name": "?UpdateMeshRefresh@cGcPlayerShipOwnership@@AEAAXXZ" + }, + { + "name": "cGcPlayerShipOwnership::Update", + "signature": "48 8B C4 55 53 56 48 8D A8 ? ? ? ? 48 81 EC ? ? ? ? 80 B9 ? ? ? ? ? 48 8B F1", + "mangled_name": "?Update@cGcPlayerShipOwnership@@QEAAXM@Z" + }, + { + "name": "cGcGameState::Update", + "signature": "48 8B C4 48 89 58 ? 48 89 70 ? 48 89 78 ? 55 41 54 41 55 41 56 41 57 48 8D A8 ? ? ? ? 48 81 EC ? ? ? ? F3 0F 10 91", + "mangled_name": "?Update@cGcGameState@@QEAAXM@Z" } ] \ No newline at end of file From d7999a5a83e9f5ba5f234cad99bf149bbe304db4 Mon Sep 17 00:00:00 2001 From: monkeyman192 Date: Wed, 19 Nov 2025 00:06:54 +1100 Subject: [PATCH 2/4] Further ship ownership data --- nmspy/data/types.py | 36 +++++++++++++++++++++++++++++++++--- tools/data.json | 10 ++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/nmspy/data/types.py b/nmspy/data/types.py index dc4ebf8..e3239e7 100644 --- a/nmspy/data/types.py +++ b/nmspy/data/types.py @@ -247,6 +247,8 @@ class cGcPlayerState(Structure): muUnits: Annotated[int, Field(c_uint32, 0x1BC)] muNanites: Annotated[int, Field(c_uint32, 0x1C0)] muSpecials: Annotated[int, Field(c_uint32, 0x1C4)] + # Found in cGcPlayerShipOwnership::SpawnNewShip + miPrimaryShip: Annotated[int, Field(c_uint32, 0xC4F0)] @function_hook( "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC ? 44 8B 81 ? ? ? ? 48 8D 2D" @@ -276,9 +278,12 @@ def StoreCurrentSystemSpaceStationEndpoint( @partial_struct class cGcPlayerShipOwnership(Structure): - # Both these found at the top of cGcPlayerShipOwnership::UpdateMeshRefresh - mbShouldRefreshMesh: Annotated[bool, Field(c_bool, 0xA690)] - mMeshRefreshState: Annotated[int, Field(c_uint32, 0xA694)] + + @partial_struct + class sGcShipData(Structure): + _total_size_ = 0x48 + + mPlayerShipSeed: Annotated[basic.cTkSeed, 0x0] @function_hook( "48 89 5C 24 ? 55 56 57 41 54 41 56 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 45" @@ -294,6 +299,31 @@ def Update( lfTimestep: Annotated[float, c_float], ): ... + @function_hook("44 89 44 24 ? 48 89 54 24 ? 55 56 41 54 41 56 41 57 48 8D 6C 24") + def SpawnNewShip( + self, + this: "_Pointer[cGcPlayerShipOwnership]", + lMatrix: _Pointer[basic.cTkMatrix34], + leLandingGearState: c_uint32, # cGcPlayerShipOwnership::ShipSpawnLandingGearState + liShipIndex: c_int32, + lbSpawnShipOverride: c_bool, + ) -> c_bool: + ... + + @function_hook("48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC ? 48 8B 35 ? ? ? ? 8B DA") + def DestroyShip( + self, + this: "_Pointer[cGcPlayerShipOwnership]", + liShipIndex: c_int32, + ) -> c_bool: + ... + + # Not sure about this... + mShips: Annotated[list[sGcShipData], Field(sGcShipData * 12, 0x58)] + # Both these found at the top of cGcPlayerShipOwnership::UpdateMeshRefresh + mbShouldRefreshMesh: Annotated[bool, Field(c_bool, 0xA690)] + mMeshRefreshState: Annotated[int, Field(c_uint32, 0xA694)] + @partial_struct class cGcGameState(Structure): diff --git a/tools/data.json b/tools/data.json index be00d73..a6dd786 100644 --- a/tools/data.json +++ b/tools/data.json @@ -758,5 +758,15 @@ "name": "cGcGameState::Update", "signature": "48 8B C4 48 89 58 ? 48 89 70 ? 48 89 78 ? 55 41 54 41 55 41 56 41 57 48 8D A8 ? ? ? ? 48 81 EC ? ? ? ? F3 0F 10 91", "mangled_name": "?Update@cGcGameState@@QEAAXM@Z" + }, + { + "name": "cGcPlayerShipOwnership::SpawnNewShip", + "signature": "44 89 44 24 ? 48 89 54 24 ? 55 56 41 54 41 56 41 57 48 8D 6C 24", + "mangled_name": "?SpawnNewShip@cGcPlayerShipOwnership@@QEAA_NAEBVcTkMatrix34@@W4ShipSpawnLandingGearState@1@H_N@Z" + }, + { + "name": "cGcPlayerShipOwnership::DestroyShip", + "signature": "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC ? 48 8B 35 ? ? ? ? 8B DA", + "mangled_name": "?DestroyShip@cGcPlayerShipOwnership@@QEAA_NH@Z" } ] \ No newline at end of file From 863720b95fded41d78da48ae8b2c19015f6b2194 Mon Sep 17 00:00:00 2001 From: monkeyman192 Date: Wed, 19 Nov 2025 23:40:27 +1100 Subject: [PATCH 3/4] Linked up more useful common structs --- nmspy/common.py | 10 ++++ nmspy/data/enums/__init__.py | 1 + nmspy/data/enums/internal_enums.py | 20 +++++++ nmspy/data/types.py | 88 +++++++++++++++++------------- 4 files changed, 82 insertions(+), 37 deletions(-) diff --git a/nmspy/common.py b/nmspy/common.py index 63119c7..1380b5f 100644 --- a/nmspy/common.py +++ b/nmspy/common.py @@ -6,6 +6,16 @@ class GameData: GcApplication: nms.cGcApplication = None # type: ignore + @property + def environment(self) -> Optional[nms.cGcEnvironment]: + if (sim := self.simulation) is not None: + return sim.mEnvironment + + @property + def player_environment(self) -> Optional[nms.cGcPlayerEnvironment]: + if (env := self.environment) is not None: + return env.mPlayerEnvironment + @property def game_state(self) -> Optional[nms.cGcGameState]: if self.GcApplication is not None: diff --git a/nmspy/data/enums/__init__.py b/nmspy/data/enums/__init__.py index 09c2090..e7f8558 100644 --- a/nmspy/data/enums/__init__.py +++ b/nmspy/data/enums/__init__.py @@ -6,6 +6,7 @@ StateEnum, eStormState, eLanguageRegion, + EnvironmentLocation, ) # The following list is auto-generated. diff --git a/nmspy/data/enums/internal_enums.py b/nmspy/data/enums/internal_enums.py index 28a4548..a9957bd 100644 --- a/nmspy/data/enums/internal_enums.py +++ b/nmspy/data/enums/internal_enums.py @@ -77,3 +77,23 @@ class eLanguageRegion(IntEnum): SimplifiedChinese = 0xE TencentChinese = 0xF Korean = 0x10 + + +class EnvironmentLocation(): + class Enum(IntEnum): + None_ = 0x0 + Default = 0x1 + SpaceStation = 0x2 + PlanetOnFoot = 0x3 + PlanetInShip = 0x4 + PlanetInVehicle = 0x5 + Underwater = 0x6 + Cave = 0x7 + IndoorInBase = 0x8 + Freighter = 0x9 + FreighterInternals = 0xA + AbandonedFreighter = 0xB + InFleet = 0xC + InSpaceObject = 0xD + Nexus = 0xE + Anomaly = 0xF diff --git a/nmspy/data/types.py b/nmspy/data/types.py index e3239e7..632df69 100644 --- a/nmspy/data/types.py +++ b/nmspy/data/types.py @@ -613,8 +613,59 @@ def Update( ): ... +@partial_struct +class cGcPlayerEnvironment(Structure): + mPlayerTM: Annotated[basic.cTkMatrix34, Field(basic.cTkMatrix34, 0x0)] + mUp: Annotated[basic.Vector3f, Field(basic.Vector3f, 0x40)] + + # Found below the call to cTkDynamicGravityControl::GetGravity in cGcPlayerEnvironment::Update + miNearestPlanetIndex: Annotated[int, Field(c_uint32, 0x2BC)] + mfDistanceFromPlanet: Annotated[float, Field(c_float, 0x2C0)] + mfNearestPlanetSealevel: Annotated[float, Field(c_float, 0x2C4)] + mNearestPlanetPos: Annotated[basic.Vector3f, Field(basic.Vector3f, 0x2D0)] + mbInsidePlanetAtmosphere: Annotated[bool, Field(c_bool, 0x2EC)] + meLocation: Annotated[ + enums.EnvironmentLocation.Enum, + Field(c_enum32[enums.EnvironmentLocation.Enum], 0x458) + ] + meLocationStable: Annotated[ + enums.EnvironmentLocation.Enum, + Field(c_enum32[enums.EnvironmentLocation.Enum], 0x464) + ] + + @function_hook("48 83 EC ? 80 B9 ? ? ? ? ? C6 04 24") + def IsOnboardOwnFreighter( + self, this: "_Pointer[cGcPlayerEnvironment]" + ) -> c_bool: ... + + @function_hook("8B 81 ? ? ? ? 83 E8 ? 83 F8 ? 0F 96 C0 C3 48 83 EC") + def IsOnPlanet(self, this: "_Pointer[cGcPlayerEnvironment]") -> c_bool: ... + + @function_hook("48 8B C4 F3 0F 11 48 ? 55 53 41 54") + def Update( + self, + this: "_Pointer[cGcPlayerEnvironment]", + lfTimeStep: Annotated[float, c_float], + ): ... + + +@partial_struct +class cGcEnvironment(Structure): + # Passed into multiple cGcPlayerEnvironment methods. + mPlayerEnvironment: Annotated[cGcPlayerEnvironment, 0x8A0] + + @function_hook( + "48 8B C4 48 89 48 ? 55 53 56 57 41 54 41 55 41 56 41 57 48 8D A8 ? ? ? ? 48 81 EC ? ? ? ? 0F 29 70 ? 4C 8B E9" + ) + def UpdateRender(self, this: "_Pointer[cGcEnvironment]"): + # TODO: There could be a few good functions to get which are called in here... + ... + + @partial_struct class cGcSimulation(Structure): + # Found in cGcSimulation::Update. Passed into cGcEnvironment::Update. + mEnvironment: Annotated[cGcEnvironment, 0xAF790] mPlayer: Annotated[cGcPlayer, 0x24DE40] # Found in cGcSimulation::Update. Passed into cGcSolarSystem::Update. mpSolarSystem: Annotated[_Pointer[cGcSolarSystem], 0x24C670] @@ -885,34 +936,6 @@ def UpdateGravityPoint( ] -@partial_struct -class cGcPlayerEnvironment(Structure): - mPlayerTM: Annotated[basic.cTkMatrix34, Field(basic.cTkMatrix34, 0x0)] - mUp: Annotated[basic.Vector3f, Field(basic.Vector3f, 0x40)] - - # Found below the call to cTkDynamicGravityControl::GetGravity in cGcPlayerEnvironment::Update - miNearestPlanetIndex: Annotated[int, Field(c_uint32, 0x2BC)] - mfDistanceFromPlanet: Annotated[float, Field(c_float, 0x2C0)] - mfNearestPlanetSealevel: Annotated[float, Field(c_float, 0x2C4)] - mNearestPlanetPos: Annotated[basic.Vector3f, Field(basic.Vector3f, 0x2D0)] - mbInsidePlanetAtmosphere: Annotated[bool, Field(c_bool, 0x2EC)] - - @function_hook("48 83 EC ? 80 B9 ? ? ? ? ? C6 04 24") - def IsOnboardOwnFreighter( - self, this: "_Pointer[cGcPlayerEnvironment]" - ) -> c_bool: ... - - @function_hook("8B 81 ? ? ? ? 83 E8 ? 83 F8 ? 0F 96 C0 C3 48 83 EC") - def IsOnPlanet(self, this: "_Pointer[cGcPlayerEnvironment]") -> c_bool: ... - - @function_hook("48 8B C4 F3 0F 11 48 ? 55 53 41 54") - def Update( - self, - this: "_Pointer[cGcPlayerEnvironment]", - lfTimeStep: Annotated[float, c_float], - ): ... - - class Engine: @static_function_hook("40 53 48 83 EC ? 44 8B D1 44 8B C1") @staticmethod @@ -1156,15 +1179,6 @@ def StateChange( ): ... -class cGcEnvironment(Structure): - @function_hook( - "48 8B C4 48 89 48 ? 55 53 56 57 41 54 41 55 41 56 41 57 48 8D A8 ? ? ? ? 48 81 EC ? ? ? ? 0F 29 70 ? 4C 8B E9" - ) - def UpdateRender(self, this: "_Pointer[cGcEnvironment]"): - # TODO: There could be a few good functions to get which are called in here... - ... - - class cGcPlayerNotifications(Structure): @function_hook("48 89 5C 24 ? 48 89 74 24 ? 57 48 81 EC ? ? ? ? 44 8B 81") def AddTimedMessage( From 4cc26c2ee7a8da3e629b2f5302b5bba948c475f2 Mon Sep 17 00:00:00 2001 From: monkeyman192 Date: Mon, 24 Nov 2025 10:45:22 +1100 Subject: [PATCH 4/4] Version fix and add another function relating to spaceship heat factor --- nmspy/data/enums/internal_enums.py | 2 +- nmspy/data/types.py | 30 ++++++++++++++++++++---------- pyproject.toml | 2 +- tools/data.json | 5 +++++ uv.lock | 2 +- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/nmspy/data/enums/internal_enums.py b/nmspy/data/enums/internal_enums.py index a9957bd..7915763 100644 --- a/nmspy/data/enums/internal_enums.py +++ b/nmspy/data/enums/internal_enums.py @@ -79,7 +79,7 @@ class eLanguageRegion(IntEnum): Korean = 0x10 -class EnvironmentLocation(): +class EnvironmentLocation: class Enum(IntEnum): None_ = 0x0 Default = 0x1 diff --git a/nmspy/data/types.py b/nmspy/data/types.py index 632df69..1d37641 100644 --- a/nmspy/data/types.py +++ b/nmspy/data/types.py @@ -278,7 +278,6 @@ def StoreCurrentSystemSpaceStationEndpoint( @partial_struct class cGcPlayerShipOwnership(Structure): - @partial_struct class sGcShipData(Structure): _total_size_ = 0x48 @@ -307,16 +306,16 @@ def SpawnNewShip( leLandingGearState: c_uint32, # cGcPlayerShipOwnership::ShipSpawnLandingGearState liShipIndex: c_int32, lbSpawnShipOverride: c_bool, - ) -> c_bool: - ... - - @function_hook("48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC ? 48 8B 35 ? ? ? ? 8B DA") + ) -> c_bool: ... + + @function_hook( + "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC ? 48 8B 35 ? ? ? ? 8B DA" + ) def DestroyShip( self, this: "_Pointer[cGcPlayerShipOwnership]", liShipIndex: c_int32, - ) -> c_bool: - ... + ) -> c_bool: ... # Not sure about this... mShips: Annotated[list[sGcShipData], Field(sGcShipData * 12, 0x58)] @@ -328,7 +327,7 @@ def DestroyShip( @partial_struct class cGcGameState(Structure): mPlayerState: Annotated[cGcPlayerState, 0xA950] - # Found in cGcGameState:: Update + # Found in cGcGameState::Update mPlayerShipOwnership: Annotated[cGcPlayerShipOwnership, 0xA2BD0] @function_hook("48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 88 54 24") @@ -626,11 +625,11 @@ class cGcPlayerEnvironment(Structure): mbInsidePlanetAtmosphere: Annotated[bool, Field(c_bool, 0x2EC)] meLocation: Annotated[ enums.EnvironmentLocation.Enum, - Field(c_enum32[enums.EnvironmentLocation.Enum], 0x458) + Field(c_enum32[enums.EnvironmentLocation.Enum], 0x458), ] meLocationStable: Annotated[ enums.EnvironmentLocation.Enum, - Field(c_enum32[enums.EnvironmentLocation.Enum], 0x464) + Field(c_enum32[enums.EnvironmentLocation.Enum], 0x464), ] @function_hook("48 83 EC ? 80 B9 ? ? ? ? ? C6 04 24") @@ -1310,7 +1309,15 @@ def GetPulseDriveFuelFactor( ) -> c_float: ... +@partial_struct class cGcSpaceshipWeapons(Structure): + # These can be found in cGcSpaceshipWeapons::GetHeatFactor and cGcSpaceshipWeapons::GetOverheatProgress + # This enum corresponds to the element in the following 3 arrays by index. + meWeaponMode: Annotated[c_enum32[enums.cGcShipWeapons], 0xA4] + mafWeaponHeat: Annotated[list[float], Field(c_float * 7, 0x5FA4)] + mafWeaponOverheatTimer: Annotated[list[float], Field(c_float * 7, 0x5FC0)] + mabWeaponOverheated: Annotated[list[bool], Field(c_bool * 7, 0x5FDC)] + @function_hook("48 63 81 ?? ?? 00 00 80 BC 08 ?? ?? 00 00 00 74 12") def GetOverheatProgress(self, this: "_Pointer[cGcSpaceshipWeapons]") -> c_float: ... @@ -1331,6 +1338,9 @@ def GetCurrentShootPoints( ) -> c_uint64: # cGcShootPoint * ... + @function_hook("48 63 81 ? ? ? ? F3 0F 10 84 81") + def GetHeatFactor(self, this: "_Pointer[cGcSpaceshipWeapons]") -> c_float: ... + class cGcPlayerCharacterComponent(Structure): @function_hook("48 8B C4 55 53 56 57 41 56 48 8D 68 A1 48 81 EC 90 00 00") diff --git a/pyproject.toml b/pyproject.toml index 5c1d8d5..44b0e8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ classifiers = [ dependencies = [ "pymhf[gui]>=0.2.1" ] -version = "155759.0" +version = "155983.0" [dependency-groups] dev = [ diff --git a/tools/data.json b/tools/data.json index a6dd786..b7bfb01 100644 --- a/tools/data.json +++ b/tools/data.json @@ -768,5 +768,10 @@ "name": "cGcPlayerShipOwnership::DestroyShip", "signature": "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC ? 48 8B 35 ? ? ? ? 8B DA", "mangled_name": "?DestroyShip@cGcPlayerShipOwnership@@QEAA_NH@Z" + }, + { + "name": "cGcSpaceshipWeapons::GetHeatFactor", + "signature": "48 63 81 ? ? ? ? F3 0F 10 84 81", + "mangled_name": "?GetHeatFactor@cGcSpaceshipWeapons@@UEBAMXZ" } ] \ No newline at end of file diff --git a/uv.lock b/uv.lock index 11ceb3c..b203dc3 100644 --- a/uv.lock +++ b/uv.lock @@ -643,7 +643,7 @@ wheels = [ [[package]] name = "nmspy" -version = "155759.0" +version = "155983.0" source = { editable = "." } dependencies = [ { name = "pymhf", extra = ["gui"] },