Skip to content

Justin99x/bl-py-stubs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bl-py-stubs

Checkout the PythonSDK Mod DB for instructions on how to develop with the legacy Python SDK

A repo for generating stub files for all unreal objects accessible through the Python SDK for Borderlands 2. You can use these to get type checking, class/method signatures, and auto-complete features in your IDE when working with game objects.

Note on SDK versions

The files were created using the new unreal SDK nightly releases and were built by querying actual game objects, not decompressed UPK files. https://github.com/apple1417/willow-mod-manager/releases/tag/nightly

Due to minor differences in how SDK versions handle Unreal objects, two versions are provided to satisfy both legacy and new SDK functionality. Main differences are:

  • Return values for functions that have out params. New SDK always returns a Tuple with the function return value ( Ellipsis if return value is null) and any out params. The legacy SDK only returns a Tuple of the out params if the function returns null.
  • Stub structs, enums, and methods inherit from the relevant new SDK Python types (WrappedStruct, UnrealEnum, and BoundFunction). No such inheritance is supported in legacy.
  • Structs define a stub for the make_struct() function in the new SDK.

BL2/TPS Support

To make it possible to type hint when developing mods that work for BL2, TPS, or both, we set up a structure as follows:

  • Three namespaces are available - common, bl2, and tps
  • The common namespace contains all objects, properties, and methods that appear in both TPS and BL2. For methods, we also require the signatures to match.
  • bl2 and tps namespaces define all objects for each. For objects that also exist in common, we inherit from the common definition, but redefine all members of the class so that we can adjust input and return types.
  • When working with game specific objects (bl2/tps), all setters and method args are of the common type, when available. All return types and getters are of the game specific type. Thanks to apple1417 for this idea.
  • Developers should import from the namespace that matches the game(s) they're developing for.

Usage - New SDK

Download the gamestubs.zip file and extract it to desired location in your game folder. Can add bl2, common, and tps folders to .stubs folder that ships with the SDK to keep all stubs in one place.

Important: You must change BoundFunction stub in the stubs that ship with the SDK to inherit from typing.Protocol. Methods in these stubs inherit from BoundFunction, and it needs to be a Protocol to work correctly in both regular code and in hooks..

Basic usage

from __future__ import annotations  # Ensures type hints are ignored at runtime
from typing import TYPE_CHECKING, cast
from mods_base import get_pc

if TYPE_CHECKING:  # Only attempt import when type checking
    from common import WillowPlayerController  # from bl2 or from tps if making a mod for a specific game

# cast keeps type checker from complaining that get_pc returns a UObject.
# Alternatively, make your own get_pc that wraps the one from mods_base.
pc = cast("WillowPlayerController", get_pc())  

Hook usage

from __future__ import annotations  # Ensures type hints are ignored at runtime
from typing import TYPE_CHECKING
from mods_base import hook
from unrealsdk.hooks import Type
from unrealsdk.unreal import BoundFunction

if TYPE_CHECKING:
    from bl2 import WillowPlayerController


@hook("WillowGame.WillowPlayerController:SaveGame", Type.PRE)
def save_game(obj: WillowPlayerController,
              args: WillowPlayerController.SaveGame.args,
              ret: WillowPlayerController.SaveGame.ret,
              func: BoundFunction):
    # args is a WrappedStruct
    # ret is whatever type the function returns
    pass

Struct usage

Might be more boilerplate than it's worth but I'm leaving it in. Simple usage is just location: Object.Vector = make_struct(*args, **kwargs), and hover over Object.Vector to see the dosctring with arg info.

from __future__ import annotations
from typing import TYPE_CHECKING
from unrealsdk import make_struct

if TYPE_CHECKING:
    from bl2 import Object

    # For type checking we use our custom stubs with matching signature
    make_struct_vector = Object.Vector.make_struct
    make_struct_rotator = Object.Rotator.make_struct
else:
    # Runtime we're still using the unrealsdk make_struct
    make_struct_vector = make_struct_rotator = make_struct

# These are now type hinted
location = make_struct_vector('Vector', True, X=0, Y=0, Z=0)
rotation = make_struct_rotator('Rotator', True, Pitch=0, Yaw=0, Roll=0)

Enum usage

from __future__ import annotations  # Ensures type hints are ignored at runtime
from typing import TYPE_CHECKING, cast
from mods_base import get_pc
from unrealsdk import find_enum

if TYPE_CHECKING:
    from bl2 import WillowPlayerController
    find_enum_eisa = WillowPlayerController.EInstinctSkillActions
else:
    find_enum_eisa = find_enum

e_isa = find_enum_eisa('EInstinctSkillActions')

pc = cast("WillowPlayerController", get_pc())
pc.NotifyInstinctSkillAction(e_isa.ISA_KilledEnemy)  # Possible enum values are type hinted.

Additional Information

  • Namespaces are consistent with UnrealScript namespaces.
    • from Engine import Actor -> Resolves to the Actor class
    • from Engine.Actor import EMoveDir -> Resolves to EMoveDir enum defined in Actor class
  • All inheritances are preserved. WillowPlayerController -> GearboxPlayerController -> GamePlayerController -> PlayerController -> Controller -> Actor -> Object -> UObject
  • Unreal flags and types are converted to Python types
    • enum -> UnrealEnum
    • struct -> class
    • optional -> default argument
    • bool -> bool
    • byte -> int
    • const -> str
    • float -> float
    • name -> str
    • str -> str
    • map -> dict
    • array -> list
    • delegate -> Callable[]
    • state -> Ignored these for now as they provide overrides to other methods
  • Additionally, two custom types are defined for information purposes
    • Out[] is used to denote that a parameter is an out parameter
    • AttributeProperty[] is used to identify attribute properties
      • FloatAttributeProperty -> AttributeProperty[float]
      • IntAttributeProperty -> AttributeProperty[int]
      • ByteAttributeProperty -> AttributeProperty[int]

About

Python stubs for developing with Unreal objects

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages