From 428517d2d83350d53977db10a2fe6c7a254bca73 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sat, 1 Feb 2025 21:37:24 +0100 Subject: [PATCH 01/18] Sort functions in Python stubs in case-sensitive alphabetical order --- Scripts/Python/plasma/Plasma.py | 80 ++++++++++++++++----------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/Scripts/Python/plasma/Plasma.py b/Scripts/Python/plasma/Plasma.py index cb40de024e..ba0bcb920d 100644 --- a/Scripts/Python/plasma/Plasma.py +++ b/Scripts/Python/plasma/Plasma.py @@ -492,14 +492,14 @@ def PtGetMouseTurnSensitivity(): """Returns the sensitivity""" pass -def PtGetNPCCount(): - """This will return the number of NPCs in the current age""" - pass - def PtGetNPCByID(npcID): """This will return the NPC with a specific ID""" pass +def PtGetNPCCount(): + """This will return the number of NPCs in the current age""" + pass + def PtGetNumCameras(): """returns camera stack size""" pass @@ -2398,19 +2398,14 @@ def createScore(ownerID, scoreName, type, points=0, key=None): """Creates a new score for an arbitrary owner""" pass - @staticmethod - def findAgeScores(scoreName, key): - """Finds matching scores for this age""" - pass - @staticmethod def findAgeHighScores(name, maxScores, key): """Finds the highest matching scores for the current age's owners""" pass @staticmethod - def findGlobalScores(scoreName, key): - """Finds matching global scores""" + def findAgeScores(scoreName, key): + """Finds matching scores for this age""" pass @staticmethod @@ -2418,6 +2413,11 @@ def findGlobalHighScores(name, maxScores, key): """Finds the highest matching scores""" pass + @staticmethod + def findGlobalScores(scoreName, key): + """Finds matching global scores""" + pass + @staticmethod def findPlayerScores(scoreName, key): """Finds matching player scores""" @@ -4237,14 +4237,14 @@ def getBufferSize(self): """Returns the size of the buffer""" pass - def getCursor(self) -> int: - """Get the current position of the cursor in the encoded buffer.""" - ... - def getCurrentLink(self) -> int: """Returns the link the mouse is currently over.""" ... + def getCursor(self) -> int: + """Get the current position of the cursor in the encoded buffer.""" + ... + def getEncodedBuffer(self): """Returns the encoded buffer in a python buffer object.""" pass @@ -5024,14 +5024,14 @@ def getControlFromIndex(self,index): """Returns the ptKey of the control with the specified index (not tag ID!)""" pass - def getControlModFromIndex(self, index: int) -> ptGUIControl: - """Returns the ptGUIControl with the specified index (not tag ID!)""" - ... - def getControlFromTag(self,tagID): """Returns the ptKey of the control with the specified tag ID""" pass + def getControlModFromIndex(self, index: int) -> ptGUIControl: + """Returns the ptGUIControl with the specified index (not tag ID!)""" + ... + def getControlModFromTag(self, tagID: int) -> ptGUIControl: """Returns the GUI control with the specified tag ID""" ... @@ -5712,29 +5712,29 @@ def addResponderState(self,state): """Add a responder state event record to the notify message""" pass - def addVarKey(self,name,key): - """Add a ptKey variable event record to the Notify message -This event record is used to pass a ptKey variable to another python program""" - pass - - def addVarNumber(self,name,number): - """Add a number variable event record to the Notify message -Method will try to pick appropriate variable type -This event record is used to pass a number variable to another python program""" - pass - def addVarFloat(self,name,number): """Add a float variable event record to the Notify message This event record is used to pass a number variable to another python program""" pass - + def addVarInt(self,name,number): """Add a integer variable event record to the Notify message This event record is used to pass a number variable to another python program""" pass - + + def addVarKey(self,name,key): + """Add a ptKey variable event record to the Notify message +This event record is used to pass a ptKey variable to another python program""" + pass + def addVarNull(self,name): """Add a null (no data) variable event record to the Notify message +This event record is used to pass a number variable to another python program""" + pass + + def addVarNumber(self,name,number): + """Add a number variable event record to the Notify message +Method will try to pick appropriate variable type This event record is used to pass a number variable to another python program""" pass @@ -6071,6 +6071,10 @@ def findObject(self,name): """Find a particular object in just the sceneobjects that are attached""" pass + def getImageLibMods(self): + """Returns list of ptKeys of the image library modifiers attached to this sceneobject""" + pass + def getKey(self): """Get the ptKey of this sceneobject If there are more then one attached, get the first one""" @@ -6100,10 +6104,6 @@ def getPythonMods(self): """Returns list of ptKeys of the python modifiers attached to this sceneobject""" pass - def getImageLibMods(self): - """Returns list of ptKeys of the image library modifiers attached to this sceneobject""" - pass - def getResponderState(self): """Return the responder state (if we are a responder)""" pass @@ -9327,14 +9327,14 @@ def setID(self,id): """Sets ID of this ptVaultNode.""" pass - def setNoteType(self,type): - """Sets the type of text note for this text note node.""" - pass - def setNoteSubType(self,subType): """Sets the subtype of the this text note node.""" pass + def setNoteType(self,type): + """Sets the type of text note for this text note node.""" + pass + def setOwnerNodeID(self,id): """Set node ID of the owner of this node""" pass From d01b18d3e3200490f6a0faafa3ee3a7d3a7ae8b1 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sat, 1 Feb 2025 21:39:18 +0100 Subject: [PATCH 02/18] Sort classes in Python stubs in case-sensitive alphabetical order Except where class inheritance requires a different order. --- Scripts/Python/plasma/Plasma.py | 274 +++++++++++------------ Scripts/Python/plasma/PlasmaConstants.py | 32 +-- 2 files changed, 153 insertions(+), 153 deletions(-) diff --git a/Scripts/Python/plasma/Plasma.py b/Scripts/Python/plasma/Plasma.py index ba0bcb920d..72fb0b7116 100644 --- a/Scripts/Python/plasma/Plasma.py +++ b/Scripts/Python/plasma/Plasma.py @@ -2368,143 +2368,6 @@ def unsetWrapping(self): """Stop text wrapping""" pass -class ptGameScore: - """Plasma Game Score""" - def __init__(self): - """None""" - pass - - def addPoints(self, points, key=None): - """Adds points to the score""" - pass - - @staticmethod - def createAgeScore(scoreName, type, points=0, key=None): - """Creates a new score associated with this age""" - pass - - @staticmethod - def createGlobalScore(scoreName, type, points=0, key=None): - """Creates a new global score""" - pass - - @staticmethod - def createPlayerScore(scoreName, type, points=0, key=None): - """Creates a new score associated with this player""" - pass - - @staticmethod - def createScore(ownerID, scoreName, type, points=0, key=None): - """Creates a new score for an arbitrary owner""" - pass - - @staticmethod - def findAgeHighScores(name, maxScores, key): - """Finds the highest matching scores for the current age's owners""" - pass - - @staticmethod - def findAgeScores(scoreName, key): - """Finds matching scores for this age""" - pass - - @staticmethod - def findGlobalHighScores(name, maxScores, key): - """Finds the highest matching scores""" - pass - - @staticmethod - def findGlobalScores(scoreName, key): - """Finds matching global scores""" - pass - - @staticmethod - def findPlayerScores(scoreName, key): - """Finds matching player scores""" - pass - - @staticmethod - def findScores(ownerID, scoreName, key): - """Finds matching scores for an arbitrary owner""" - pass - - def getGameType(self): - """Returns the score game type.""" - pass - - def getName(self): - """Returns the score game name.""" - pass - - def getOwnerID(self): - """Returns the score game owner.""" - pass - - def getPoints(self): - """Returns the number of points in this score""" - pass - - def remove(self): - """Removes this score from the server""" - pass - - def setPoints(self): - """Sets the number of points in the score - Don't use to add/remove points, use only to reset values!""" - pass - - def transferPoints(self, dest, points=0, key=None): - """Transfers points from this score to another""" - pass - -class ptGameScoreMsg: - """Game Score operation callback message""" - def __init__(self): - """None""" - pass - -class ptGameScoreListMsg(ptGameScoreMsg): - """Game Score message for scores found on the server""" - def __init__(self): - """None""" - pass - - def getName(self): - """Returns the template score name""" - pass - - def getOwnerID(self): - """Returns the template score ownerID""" - pass - - def getScores(self): - """Returns a list of scores found by the server""" - pass - -class ptGameScoreTransferMsg(ptGameScoreMsg): - """Game Score message indicating a score point transfer""" - def __init__(self): - """None""" - pass - - def getDestination(self): - """Returns the score points were transferred to""" - pass - - def getSource(self): - """Returns the score points were transferred from""" - pass - -class ptGameScoreUpdateMsg(ptGameScoreMsg): - """Game Score message for a score update operation""" - def __init__(self): - """None""" - pass - - def getScore(self): - """Returns the updated game score""" - pass - class ptGUIControl: """Base class for all GUI controls""" def __init__(self,controlKey): @@ -5219,6 +5082,143 @@ def getKey(self): """Returns this object's ptKey""" pass +class ptGameScore: + """Plasma Game Score""" + def __init__(self): + """None""" + pass + + def addPoints(self, points, key=None): + """Adds points to the score""" + pass + + @staticmethod + def createAgeScore(scoreName, type, points=0, key=None): + """Creates a new score associated with this age""" + pass + + @staticmethod + def createGlobalScore(scoreName, type, points=0, key=None): + """Creates a new global score""" + pass + + @staticmethod + def createPlayerScore(scoreName, type, points=0, key=None): + """Creates a new score associated with this player""" + pass + + @staticmethod + def createScore(ownerID, scoreName, type, points=0, key=None): + """Creates a new score for an arbitrary owner""" + pass + + @staticmethod + def findAgeHighScores(name, maxScores, key): + """Finds the highest matching scores for the current age's owners""" + pass + + @staticmethod + def findAgeScores(scoreName, key): + """Finds matching scores for this age""" + pass + + @staticmethod + def findGlobalHighScores(name, maxScores, key): + """Finds the highest matching scores""" + pass + + @staticmethod + def findGlobalScores(scoreName, key): + """Finds matching global scores""" + pass + + @staticmethod + def findPlayerScores(scoreName, key): + """Finds matching player scores""" + pass + + @staticmethod + def findScores(ownerID, scoreName, key): + """Finds matching scores for an arbitrary owner""" + pass + + def getGameType(self): + """Returns the score game type.""" + pass + + def getName(self): + """Returns the score game name.""" + pass + + def getOwnerID(self): + """Returns the score game owner.""" + pass + + def getPoints(self): + """Returns the number of points in this score""" + pass + + def remove(self): + """Removes this score from the server""" + pass + + def setPoints(self): + """Sets the number of points in the score + Don't use to add/remove points, use only to reset values!""" + pass + + def transferPoints(self, dest, points=0, key=None): + """Transfers points from this score to another""" + pass + +class ptGameScoreMsg: + """Game Score operation callback message""" + def __init__(self): + """None""" + pass + +class ptGameScoreListMsg(ptGameScoreMsg): + """Game Score message for scores found on the server""" + def __init__(self): + """None""" + pass + + def getName(self): + """Returns the template score name""" + pass + + def getOwnerID(self): + """Returns the template score ownerID""" + pass + + def getScores(self): + """Returns a list of scores found by the server""" + pass + +class ptGameScoreTransferMsg(ptGameScoreMsg): + """Game Score message indicating a score point transfer""" + def __init__(self): + """None""" + pass + + def getDestination(self): + """Returns the score points were transferred to""" + pass + + def getSource(self): + """Returns the score points were transferred from""" + pass + +class ptGameScoreUpdateMsg(ptGameScoreMsg): + """Game Score message for a score update operation""" + def __init__(self): + """None""" + pass + + def getScore(self): + """Returns the updated game score""" + pass + class ptGrassShader: """Plasma Grass Shader class""" def __init__(self,key): diff --git a/Scripts/Python/plasma/PlasmaConstants.py b/Scripts/Python/plasma/PlasmaConstants.py index 6ae8b5dbc6..e102d1135b 100644 --- a/Scripts/Python/plasma/PlasmaConstants.py +++ b/Scripts/Python/plasma/PlasmaConstants.py @@ -114,6 +114,16 @@ class PtButtonNotifyTypes: kNotifyOnDown = 1 kNotifyOnUpAndDown = 2 +class PtCCRPetitionType: + """(none)""" + kGeneralHelp = 0 + kBug = 1 + kFeedback = 2 + kExploit = 3 + kHarass = 4 + kStuck = 5 + kTechnical = 6 + class PtConfirmationResult: OK = 1 Cancel = 0 @@ -128,16 +138,6 @@ class PtConfirmationType: ForceQuit = 2 YesNo = 3 -class PtCCRPetitionType: - """(none)""" - kGeneralHelp = 0 - kBug = 1 - kFeedback = 2 - kExploit = 3 - kHarass = 4 - kStuck = 5 - kTechnical = 6 - class PtEventType: """(none)""" kCollision = 1 @@ -155,6 +155,12 @@ class PtEventType: kOfferLinkingBook = 14 kBook = 15 +class PtFontFlags: + """(none)""" + kFontBold = 1 + kFontItalic = 2 + kFontShadowed = 4 + class PtGUIMultiLineDirection: """(none)""" kLineStart = 1 @@ -182,12 +188,6 @@ class PtJustify: kCenter = 1 kRightJustify = 2 -class PtFontFlags: - """(none)""" - kFontBold = 1 - kFontItalic = 2 - kFontShadowed = 4 - class PtLOSObjectType: """(none)""" kClickables = 0 From af3749c054b60ce154f05ffb0228f351e4c9a6f0 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sat, 1 Feb 2025 21:48:07 +0100 Subject: [PATCH 03/18] Sort enum constants in Python stubs by their numeric values And then in alphabetical order, if two enum constants have the same numeric value. --- Scripts/Python/plasma/PlasmaConstants.py | 30 +++++++++---------- Scripts/Python/plasma/PlasmaGameConstants.py | 2 +- Scripts/Python/plasma/PlasmaVaultConstants.py | 6 ++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Scripts/Python/plasma/PlasmaConstants.py b/Scripts/Python/plasma/PlasmaConstants.py index e102d1135b..5ec680a227 100644 --- a/Scripts/Python/plasma/PlasmaConstants.py +++ b/Scripts/Python/plasma/PlasmaConstants.py @@ -68,25 +68,25 @@ class PtAccountUpdateType: class PtBehaviorTypes: """(none)""" - kBehaviorTypeFall = 8192 - kBehaviorTypeIdle = 32 + kBehaviorTypeStandingJump = 1 kBehaviorTypeWalkingJump = 2 - kBehaviorTypeSidestepRight = 4096 kBehaviorTypeRunningJump = 4 - kBehaviorTypeLinkIn = 65536 kBehaviorTypeAnyJump = 7 kBehaviorTypeRunningImpact = 8 + kBehaviorTypeGroundImpact = 16 + kBehaviorTypeAnyImpact = 24 + kBehaviorTypeIdle = 32 kBehaviorTypeWalk = 64 kBehaviorTypeRun = 128 - kBehaviorTypeTurnRight = 1024 kBehaviorTypeWalkBack = 256 - kBehaviorTypeMovingTurnLeft = 16384 - kBehaviorTypeGroundImpact = 16 - kBehaviorTypeStandingJump = 1 kBehaviorTypeTurnLeft = 512 - kBehaviorTypeAnyImpact = 24 + kBehaviorTypeTurnRight = 1024 kBehaviorTypeSidestepLeft = 2048 + kBehaviorTypeSidestepRight = 4096 + kBehaviorTypeFall = 8192 + kBehaviorTypeMovingTurnLeft = 16384 kBehaviorTypeMovingTurnRight = 32768 + kBehaviorTypeLinkIn = 65536 kBehaviorTypeLinkOut = 131072 class PtBookEventTypes: @@ -125,11 +125,11 @@ class PtCCRPetitionType: kTechnical = 6 class PtConfirmationResult: - OK = 1 Cancel = 0 - Yes = 1 No = 0 + OK = 1 Quit = 1 + Yes = 1 Logout = 62 class PtConfirmationType: @@ -247,13 +247,14 @@ class PtNotifyDataType: class PtSDLReadWriteOptions: """(none)""" - kTimeStampOnRead = 16 kDirtyOnly = 1 kSkipNotificationInfo = 2 kBroadcast = 4 + kTimeStampOnRead = 16 class PtSDLVarType: """(none)""" + kNone = -1 kInt = 0 kFloat = 1 kBool = 2 @@ -270,7 +271,6 @@ class PtSDLVarType: kRGB = 52 kRGBA = 53 kQuaternion = 54 - kNone = -1 class PtScoreRankGroups: """(none)""" @@ -286,14 +286,14 @@ class PtScoreTimePeriods: class PtStatusLogFlags: """(none)""" - kDebugOutput = 32 kFilledBackground = 1 kAppendToLast = 2 kDontWriteFile = 4 kDeleteForMe = 8 + kAlignToTop = 16 + kDebugOutput = 32 kTimestamp = 64 kStdout = 128 kTimeInSeconds = 256 - kAlignToTop = 16 kTimeAsDouble = 512 diff --git a/Scripts/Python/plasma/PlasmaGameConstants.py b/Scripts/Python/plasma/PlasmaGameConstants.py index 2c61e8f16d..3a838b68ab 100644 --- a/Scripts/Python/plasma/PlasmaGameConstants.py +++ b/Scripts/Python/plasma/PlasmaGameConstants.py @@ -44,6 +44,7 @@ from __future__ import annotations class PtGameJoinError: + kGameJoinPending = -1 kGameJoinSuccess = 0 kGameJoinErrNotExist = 1 kGameJoinErrInitFailed = 2 @@ -53,7 +54,6 @@ class PtGameJoinError: kGameJoinErrAlreadyJoined = 6 kGameJoinErrNoInvite = 7 kNumGameJoinErrors = 8 - kGameJoinPending = -1 class PtMarkerGameType: diff --git a/Scripts/Python/plasma/PlasmaVaultConstants.py b/Scripts/Python/plasma/PlasmaVaultConstants.py index 85970a17f2..2df2b993d5 100644 --- a/Scripts/Python/plasma/PlasmaVaultConstants.py +++ b/Scripts/Python/plasma/PlasmaVaultConstants.py @@ -55,9 +55,6 @@ class PtVaultCallbackTypes: class PtVaultNodeTypes: """(none)""" kInvalidNode = 0 - kAgeInfoNode = 33 - kAgeInfoListNode = 34 - kMarkerGameNode = 35 kVNodeMgrPlayerNode = 2 kVNodeMgrAgeNode = 3 kFolderNode = 22 @@ -68,6 +65,9 @@ class PtVaultNodeTypes: kAgeLinkNode = 28 kChronicleNode = 29 kPlayerInfoListNode = 30 + kAgeInfoNode = 33 + kAgeInfoListNode = 34 + kMarkerGameNode = 35 class PtVaultNotifyTypes: """(none)""" From 9ccd592d453f61046b15067c087384d44f735bc0 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Wed, 29 Jan 2025 02:39:28 +0100 Subject: [PATCH 04/18] Add script for automatically generating the Python interface stubs Not quite finished yet, but it can generate a Plasma.py that's relatively close to what we currently have in the repo. --- Scripts/Python/plasma/generate_stubs.py | 336 ++++++++++++++++++ .../FeatureLib/pfPython/cyPythonInterface.cpp | 3 + 2 files changed, 339 insertions(+) create mode 100644 Scripts/Python/plasma/generate_stubs.py diff --git a/Scripts/Python/plasma/generate_stubs.py b/Scripts/Python/plasma/generate_stubs.py new file mode 100644 index 0000000000..e661366b31 --- /dev/null +++ b/Scripts/Python/plasma/generate_stubs.py @@ -0,0 +1,336 @@ +# -*- coding: utf-8 -*- +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ + +import os.path +import types + +from typing import Iterable, Literal + +import Plasma +import PlasmaConstants +import PlasmaGame +import PlasmaGameConstants +import PlasmaNetConstants +import PlasmaVaultConstants + +all_plasma_modules = [ + Plasma, + PlasmaConstants, + PlasmaGame, + PlasmaGameConstants, + PlasmaNetConstants, + PlasmaVaultConstants, +] + +docstring_params_prefix = "Params: " + +def attr_sort_key(item: tuple[str, object]) -> tuple[int, str]: + name, value = item + + # Put functions and classes after "simple" attributes (constants and instance properties). + # (Note: callable includes both functions and classes). + if callable(value): + order = 1 + else: + order = 0 + + # Generally, we sort attributes by name (case-sensitive). + return order, name + +def iter_attributes(obj: object) -> Iterable[tuple[str, object]]: + # Note: dir also includes class attributes inherited from superclasses! + # We currently want this, to match the existing stubs, + # but in the future we should switch to iterating over __dict__ instead + # so that only directly defined attributes are included. + # TODO Replace with __dict__ and manually add inherited methods + attrs = [(name, getattr(obj, name)) for name in dir(obj)] + attrs.sort(key=attr_sort_key) + + # Ensure that every class has its base classes defined first. + # We manage the iteration index manually + # because we intentionally process some indices more than once + # (and also because we reorder items during iteration). + already_seen_classes = set() + i = 0 + while i < len(attrs): + name, value = attrs[i] + + # Ignore dunder attributes for the purposes of discovering base class dependencies. + # Otherwise, the logic will get confused by special attributes + # whose value is a type object, such as __class__. + if not name.startswith("__") and isinstance(value, type): + # Find all base classes that haven't been seen yet + # and wrap them in (name, value) tuples. + missing_bases = [ + (base.__name__, base) for base in value.__bases__ + # Only check base classes from the same module. + # Classes from other modules are accessed by their fully qualified name + # and are assumed to be always defined already. + if base.__module__ == value.__module__ and base not in already_seen_classes + ] + + if missing_bases: + # Remove all of the missing base classes from their original list position... + for missing_base_item in missing_bases: + attrs.remove(missing_base_item) + # (...verify that we didn't somehow remove anything at or before index i...) + assert attrs[i] == (name, value) + # ...and re-add them right before the current class. + attrs[i:i] = missing_bases + + # We intentionally continue WITHOUT incrementing i here! + # The next iteration must process the classes that we just moved around, + # so that if the bases themselves have any missing bases, + # those are handled recursively. + # Iteration continues normally once the class at index i has no more missing bases. + continue + else: + # All base classes are already defined - nothing more to be done. + already_seen_classes.add(value) + + i += 1 + + return attrs + +def parse_params_from_doc(doc: str) -> tuple[str, str]: + if doc is None: + return "", "" + elif doc.startswith(docstring_params_prefix): + params_line, _, doc_body = doc.partition("\n") + params = params_line.removeprefix(docstring_params_prefix) + return params, doc_body + else: + # Assume that functions without a "Params: " line have no parameters. + # Safer would be to default to "*args, **kwargs" and require some explicit marker for no parameters, + # but this would require extensive manual updating of the docstrings. + return "", doc + +def format_qualified_name(cls: type, context_module_name: str) -> str: + if cls.__module__ in {"builtins", context_module_name}: + return cls.__qualname__ + else: + return cls.__module__ + "." + cls.__qualname__ + +def add_indents(indent: str, lines: Iterable[str]) -> Iterable[str]: + for line in lines: + if line: + yield indent + line + else: + # Don't add extra whitespace on blank lines. + yield "" + +FunctionKind = Literal["function", "method", "classmethod", "staticmethod", "property"] + +def generate_function_stub(kind: FunctionKind, name: str, params: str, doc: str) -> Iterable[str]: + if kind == "function": + decorator = None + self_param = None + elif kind == "method": + decorator = None + self_param = "self" + elif kind == "classmethod": + decorator = "@classmethod" + self_param = "cls" + elif kind == "staticmethod": + decorator = "@staticmethod" + self_param = None + elif kind == "property": + decorator = "@property" + self_param = "self" + else: + raise ValueError(f"Unsupported function kind: {kind!r}") + + all_params_parts = [] + if self_param is not None: + all_params_parts.append(self_param) + if params: + all_params_parts.append(params) + # No space after the comma for now, to match the existing stubs. + all_params = ",".join(all_params_parts) + + if decorator is not None: + yield decorator + yield f"def {name}({all_params}):" + if doc: + yield f' """{doc}"""' + yield " pass" + +def generate_class_stub(name: str, cls: type) -> Iterable[str]: + if tuple(cls.__bases__) == (object,): + class_parens = "" + else: + base_names = [] + for base in cls.__bases__: + base_names.append(format_qualified_name(base, cls.__module__)) + + class_parens = "(" + ", ".join(base_names) + ")" + + init_params, doc = parse_params_from_doc(cls.__doc__) + + yield f"class {name}{class_parens}:" + yield f' """{doc}"""' + + first = True + # This currently also includes attributes inherited from superclasses, to match the existing stubs. + # We should change this to only include attributes defined in the class itself. + # This would make stubs for subclasses a lot more readable. + for name, value in iter_attributes(cls): + if name == "__init__": + # Don't put a blank line between the class docstring and __init__, to match the existing stubs. + if not first: + yield "" + first = False + + # Special case for __init__: use the parameters from the class docstring. + # Output the string "None" as the docstring for all __init__ methods for now, to match the existing stubs. + yield from add_indents(" ", generate_function_stub("method", name, init_params, "None")) + continue + elif name.startswith("__"): + # Ignore all other special attributes. + continue + + first = False + yield "" + + if isinstance(value, type): + yield from add_indents(" ", generate_class_stub(name, value)) + elif callable(value): + kind: FunctionKind + if isinstance(value, types.BuiltinMethodType): + # C-defined non-instance methods have class builtin_function_or_method (aka types.BuiltinMethodType). + # This class is used for both static methods and class methods. + # It seems that we can tell them apart by looking at __self__, + # though this seems like an implementation detail and potentially brittle. + + # If this breaks at some point, + # I guess we could just treat all non-instance methods as static methods + # (from a caller's perspective, static methods and class methods are almost indistinguishable anyway). + + # A more reliable way might be to look at the class __dict__, + # where class methods show up as classmethod_descriptor (aka types.ClassMethodDescriptorType) objects + # and static methods as staticmethod objects. + + if getattr(value, "__self__", None) is None: + kind = "staticmethod" + else: + kind = "classmethod" + else: + # C-defined instance methods have two possible classes: + # either wrapper_descriptor (aka types.WrapperDescriptorType) for "special" methods (e. g. __init__), + # or method_descriptor (aka types.MethodDescriptorType) for "regular" methods. + # We don't need to care about these implementation details though + # and simply assume that all other callables are instance methods. + kind = "method" + + params, doc = parse_params_from_doc(value.__doc__) + yield from add_indents(" ", generate_function_stub(kind, name, params, doc)) + elif isinstance(value, types.GetSetDescriptorType): + # C-defined properties have class getset_descriptor (aka types.GetSetDescriptorType). + # (Note: There is also member_descriptor aka types.MemberDescriptorType, + # but we don't need to care about that, because Plasma doesn't use PyMemberDef.) + # We currently can't determine whether a C-defined property is read-only or read-write, + # because the Python glue implements read-only properties by defining a setter that always throws an error. + # (Perhaps we should set the setter to nullptr in that case? Perhaps Python can tell the difference then?) + # Right now it doesn't matter anyway, + # because the type annotation syntax doesn't differentiate between read-only and read-write attributes. + doc = value.__doc__ or "" + yield f" {name}: Any" + yield f' """{doc}"""' + else: + yield f" {name}: {format_qualified_name(type(value), cls.__module__)} = ... # = {value!r}" + +def generate_module_stub(module: types.ModuleType) -> Iterable[str]: + yield "# -*- coding: utf-8 -*-" + + # License header (copied from the stub generator script, *not* from the module being dumped): + yield f'"""{__doc__}"""' + + yield "" + yield "# NOTE: This stub file was generated automatically from Plasma's Python interface." + yield "# Do not edit this file manually." + yield "# To change any of the docstrings or function signatures," + yield "# edit the corresponding C++ glue code in pfPython." + yield "# If the Python interface has changed, regenerate these stubs" + yield "# by running the following call in the in-game Python console:" + yield '# >>> __import__("generate_stubs").run()' + yield "" + + # Now the actual module docstring (if one exists): + if getattr(module, "__doc__", None) is not None: + yield f'"""{module.__doc__}"""' + + yield "" + + # Hardcoded imports for type annotations: + yield "from __future__ import annotations" + yield "from typing import *" + + for name, value in iter_attributes(module): + if name.startswith("__"): + continue + + yield "" + if isinstance(value, type): + yield from generate_class_stub(name, value) + elif callable(value): + params, doc = parse_params_from_doc(value.__doc__) + yield from generate_function_stub("function", name, params, doc) + else: + yield f"{name}: {format_qualified_name(type(value), module.__name__)} = ... # = {value!r}" + +def run(dest_dir: str = "plasma_stubs_generated") -> None: + try: + os.mkdir(dest_dir) + except FileExistsError: + pass + + # Note: Keep this list in sync with the IInitBuiltinModule calls + # in PythonInterface::initPython in pfPython/cyPythonInterface.cpp! + for module in all_plasma_modules: + # Generate all the lines first and only write the file afterwards + # to avoid leaving behind a half-written file if generate_module_stub throws an exception. + lines = list(generate_module_stub(module)) + stub_file = os.path.join(dest_dir, module.__name__ + ".py") + with open(stub_file, "w", encoding="utf-8", newline="\n") as f: + for line in lines: + f.write(line + "\n") diff --git a/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp b/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp index 703d86b15f..944827661e 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp @@ -932,6 +932,9 @@ void PythonInterface::initPython() // Initialize built-in Plasma modules. For some reason, when using the append-inittab thingy, // we get complaints about these modules being leaked :( + // Note: If you add a new built-in module, + // please add it to the list in Scripts/Python/plasma/generate_stubs.py + // so that a stub will be generated for the new module. IInitBuiltinModule("Plasma", "Plasma 2.0 Game Library", dbgLog, AddPlasmaClasses, AddPlasmaMethods); IInitBuiltinModule("PlasmaConstants", "Plasma 2.0 Constants", dbgLog, AddPlasmaConstantsClasses); IInitBuiltinModule("PlasmaGame", "Plasma 2.0 GameMgr Library", dbgLog, AddPlasmaGameClasses); From 63fe68fb9e19d04d9d96a5e14eba62841ece32ed Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sat, 1 Feb 2025 16:13:22 +0100 Subject: [PATCH 05/18] Add Plasma enum support to stub generator --- Scripts/Python/plasma/generate_stubs.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Scripts/Python/plasma/generate_stubs.py b/Scripts/Python/plasma/generate_stubs.py index e661366b31..3a5f256420 100644 --- a/Scripts/Python/plasma/generate_stubs.py +++ b/Scripts/Python/plasma/generate_stubs.py @@ -67,9 +67,9 @@ def attr_sort_key(item: tuple[str, object]) -> tuple[int, str]: name, value = item - # Put functions and classes after "simple" attributes (constants and instance properties). + # Put functions, classes, and enums after "simple" attributes (constants and instance properties). # (Note: callable includes both functions and classes). - if callable(value): + if callable(value) or isinstance(value, PlasmaConstants.Enum): order = 1 else: order = 0 @@ -195,6 +195,16 @@ def generate_function_stub(kind: FunctionKind, name: str, params: str, doc: str) yield f' """{doc}"""' yield " pass" +def generate_enum_stub(name: str, enum_obj: PlasmaConstants.Enum) -> Iterable[str]: + yield f"class {name}:" + # Output the string "(none)" as the docstring for all enums for now, to match the existing stubs. + # (Plasma enums don't have docstrings.) + yield ' """(none)"""' + + # Output enum constants sorted by their int value. + for name, value in sorted(enum_obj.lookup.items(), key=lambda item: int(item[1])): + yield f" {name} = {int(value)}" + def generate_class_stub(name: str, cls: type) -> Iterable[str]: if tuple(cls.__bases__) == (object,): class_parens = "" @@ -310,7 +320,9 @@ def generate_module_stub(module: types.ModuleType) -> Iterable[str]: continue yield "" - if isinstance(value, type): + if isinstance(value, PlasmaConstants.Enum): + yield from generate_enum_stub(name, value) + elif isinstance(value, type): yield from generate_class_stub(name, value) elif callable(value): params, doc = parse_params_from_doc(value.__doc__) From f1b599e8e51b90051899d03ef81e225c56a83fa0 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sat, 1 Feb 2025 19:17:58 +0100 Subject: [PATCH 06/18] Don't output empty class and property docstrings into stubs --- Scripts/Python/plasma/generate_stubs.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Scripts/Python/plasma/generate_stubs.py b/Scripts/Python/plasma/generate_stubs.py index 3a5f256420..4fa57b62bb 100644 --- a/Scripts/Python/plasma/generate_stubs.py +++ b/Scripts/Python/plasma/generate_stubs.py @@ -218,7 +218,8 @@ def generate_class_stub(name: str, cls: type) -> Iterable[str]: init_params, doc = parse_params_from_doc(cls.__doc__) yield f"class {name}{class_parens}:" - yield f' """{doc}"""' + if doc: + yield f' """{doc}"""' first = True # This currently also includes attributes inherited from superclasses, to match the existing stubs. @@ -283,9 +284,10 @@ def generate_class_stub(name: str, cls: type) -> Iterable[str]: # (Perhaps we should set the setter to nullptr in that case? Perhaps Python can tell the difference then?) # Right now it doesn't matter anyway, # because the type annotation syntax doesn't differentiate between read-only and read-write attributes. - doc = value.__doc__ or "" + doc = value.__doc__ yield f" {name}: Any" - yield f' """{doc}"""' + if doc: + yield f' """{doc}"""' else: yield f" {name}: {format_qualified_name(type(value), cls.__module__)} = ... # = {value!r}" From d5dd90e9381a461e1459f2c15b5a1e78aacbe5e1 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sat, 1 Feb 2025 19:31:56 +0100 Subject: [PATCH 07/18] Disable stub module docstrings to avoid issues with __future__ imports --- Scripts/Python/plasma/generate_stubs.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Scripts/Python/plasma/generate_stubs.py b/Scripts/Python/plasma/generate_stubs.py index 4fa57b62bb..8fb876c4df 100644 --- a/Scripts/Python/plasma/generate_stubs.py +++ b/Scripts/Python/plasma/generate_stubs.py @@ -307,11 +307,14 @@ def generate_module_stub(module: types.ModuleType) -> Iterable[str]: yield '# >>> __import__("generate_stubs").run()' yield "" - # Now the actual module docstring (if one exists): - if getattr(module, "__doc__", None) is not None: + # Now the actual module docstring (if one exists). + # Temporarily disabled, because the docstring is currently occupied by the license header, + # so a string literal placed here would be considered a regular statement and not a docstring. + # This is also important for the __future__ import, + # which loses its effect if preceded by any statement other than a docstring. + if False and getattr(module, "__doc__", None) is not None: yield f'"""{module.__doc__}"""' - - yield "" + yield "" # Hardcoded imports for type annotations: yield "from __future__ import annotations" From bcb68d36560574628806f7b52026eea1d7ac3f4b Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sat, 1 Feb 2025 22:11:56 +0100 Subject: [PATCH 08/18] Define a stable order for enum constants with equal values --- Scripts/Python/plasma/generate_stubs.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Scripts/Python/plasma/generate_stubs.py b/Scripts/Python/plasma/generate_stubs.py index 8fb876c4df..a5af50307a 100644 --- a/Scripts/Python/plasma/generate_stubs.py +++ b/Scripts/Python/plasma/generate_stubs.py @@ -201,8 +201,9 @@ def generate_enum_stub(name: str, enum_obj: PlasmaConstants.Enum) -> Iterable[st # (Plasma enums don't have docstrings.) yield ' """(none)"""' - # Output enum constants sorted by their int value. - for name, value in sorted(enum_obj.lookup.items(), key=lambda item: int(item[1])): + # Output enum constants sorted by their int value, + # falling back to sorting by name (case-sensitive) for constants with equal values. + for name, value in sorted(enum_obj.lookup.items(), key=lambda item: (int(item[1]), item[0])): yield f" {name} = {int(value)}" def generate_class_stub(name: str, cls: type) -> Iterable[str]: From 0f4eecdea82d1eeb3b99bbba9ba79aec155b3d9d Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sun, 2 Feb 2025 03:06:00 +0100 Subject: [PATCH 09/18] Fix Python glue docstrings with missing space after "Params:" --- Sources/Plasma/FeatureLib/pfPython/pyClusterGlue.cpp | 4 ++-- Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfPython/pyClusterGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyClusterGlue.cpp index 2852ed5b68..d9372ee3ea 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyClusterGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyClusterGlue.cpp @@ -82,11 +82,11 @@ PYTHON_METHOD_DEFINITION(ptCluster, setVisible, args) } PYTHON_START_METHODS_TABLE(ptCluster) - PYTHON_METHOD(ptCluster, setVisible, "Params:visible\nShows or hides the cluster object"), + PYTHON_METHOD(ptCluster, setVisible, "Params: visible\nShows or hides the cluster object"), PYTHON_END_METHODS_TABLE; // Type structure definition -PLASMA_DEFAULT_TYPE(ptCluster, "Params:key\nCreates a new ptCluster"); +PLASMA_DEFAULT_TYPE(ptCluster, "Params: key\nCreates a new ptCluster"); // required functions for PyObject interoperability PyObject *pyCluster::New(plKey key) diff --git a/Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp index abf44c5e68..370e7a3511 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp @@ -251,7 +251,7 @@ PYTHON_START_METHODS_TABLE(ptWaveSet) PYTHON_END_METHODS_TABLE; // Type structure definition -PLASMA_DEFAULT_TYPE(ptWaveSet, "Params:key\nCreates a new ptWaveSet"); +PLASMA_DEFAULT_TYPE(ptWaveSet, "Params: key\nCreates a new ptWaveSet"); // required functions for PyObject interoperability PyObject *pyWaveSet::New(plKey key) From e23d7e07bad1cbf9efad17cbd635f3deac8e4b62 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sun, 2 Feb 2025 03:10:54 +0100 Subject: [PATCH 10/18] Fix uncapitalized "false" in Python glue parameter defaults --- Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp | 2 +- Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp | 4 ++-- Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp index 3aab3a215f..4d4352056b 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp @@ -421,7 +421,7 @@ PYTHON_GLOBAL_METHOD_DEFINITION(PtDumpLogs, args, "Params: folder\nDumps all cur PYTHON_RETURN_BOOL(cyMisc::DumpLogs(folder)); } -PYTHON_GLOBAL_METHOD_DEFINITION(PtCloneKey, args, "Params: key, loading=false\nCreates clone of key") +PYTHON_GLOBAL_METHOD_DEFINITION(PtCloneKey, args, "Params: key, loading=False\nCreates clone of key") { PyObject* keyObj = nullptr; char loading = 0; diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp index fe9f703868..aa4f0d40e0 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp @@ -281,7 +281,7 @@ PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetFrameDeltaTime, "Returns the amount return PyFloat_FromDouble(cyMisc::GetDelSysSeconds()); } -PYTHON_GLOBAL_METHOD_DEFINITION(PtPageInNode, args, "Params: nodeName, netForce=false, ageName=\"\"\nPages in node, or a list of nodes") +PYTHON_GLOBAL_METHOD_DEFINITION(PtPageInNode, args, "Params: nodeName, netForce=False, ageName=\"\"\nPages in node, or a list of nodes") { PyObject* nodeNameObj = nullptr; ST::string ageName; @@ -320,7 +320,7 @@ PYTHON_GLOBAL_METHOD_DEFINITION(PtPageInNode, args, "Params: nodeName, netForce= PYTHON_RETURN_NONE; } -PYTHON_GLOBAL_METHOD_DEFINITION(PtPageOutNode, args, "Params: nodeName, netForce = false\nPages out a node") +PYTHON_GLOBAL_METHOD_DEFINITION(PtPageOutNode, args, "Params: nodeName, netForce=False\nPages out a node") { ST::string nodeName; char netForce = 0; diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp index e4df581709..6483768cd9 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp @@ -220,7 +220,7 @@ PYTHON_GLOBAL_METHOD_DEFINITION(PtWasLocallyNotified, args, "Params: selfKey\nRe PYTHON_RETURN_BOOL(cyMisc::WasLocallyNotified(*key)); } -PYTHON_GLOBAL_METHOD_DEFINITION(PtAttachObject, args, "Params: child,parent,netForce=false\nAttach child to parent based on ptKey or ptSceneobject\n" +PYTHON_GLOBAL_METHOD_DEFINITION(PtAttachObject, args, "Params: child,parent,netForce=False\nAttach child to parent based on ptKey or ptSceneobject\n" "- childKey is the ptKey or ptSceneobject of the one being attached\n" "- parentKey is the ptKey or ptSceneobject of the one being attached to\n" "(both arguments must be ptKeys or ptSceneobjects, you cannot mix types)") @@ -253,7 +253,7 @@ PYTHON_GLOBAL_METHOD_DEFINITION(PtAttachObject, args, "Params: child,parent,netF PYTHON_RETURN_NONE; } -PYTHON_GLOBAL_METHOD_DEFINITION(PtDetachObject, args, "Params: child,parent,netForce=false\nDetach child from parent based on ptKey or ptSceneobject\n" +PYTHON_GLOBAL_METHOD_DEFINITION(PtDetachObject, args, "Params: child,parent,netForce=False\nDetach child from parent based on ptKey or ptSceneobject\n" "- child is the ptKey or ptSceneobject of the one being detached\n" "- parent is the ptKey or ptSceneobject of the one being detached from\n" "(both arguments must be ptKeys or ptSceneobjects, you cannot mix types)") From 5059827fd4120f5155470fefbeae112058ef83b0 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sun, 2 Feb 2025 03:36:18 +0100 Subject: [PATCH 11/18] Update C++ docstrings that got out of sync with stubs for no good reason --- .../FeatureLib/pfPython/pyAudioControlGlue.cpp | 14 +++++++------- .../pfPython/pyGUIControlMultiLineEditGlue.cpp | 4 ++-- .../Plasma/FeatureLib/pfPython/pyGameCliGlue.cpp | 8 ++++---- .../FeatureLib/pfPython/pyImageLibModGlue.cpp | 2 +- .../pfPython/pyVaultMarkerGameNodeGlue.cpp | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp index 817482de52..093f4e8802 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp @@ -411,17 +411,17 @@ PYTHON_START_METHODS_TABLE(ptAudioControl) "...Less of a performance hit, harder on memory."), PYTHON_METHOD(ptAudioControl, useEAXAcceleration, "Params: state\nEnables or disables EAX sound acceleration (requires hardware acceleration)."), PYTHON_METHOD_NOARGS(ptAudioControl, isUsingEAXAcceleration, "Is EAX sound acceleration enabled? Returns 1 if true otherwise returns 0."), - PYTHON_METHOD_NOARGS(ptAudioControl, isEAXSupported, "Is EAX acceleration supported by the current device?"), + PYTHON_METHOD_NOARGS(ptAudioControl, isEAXSupported, "Returns true or false based on whether or not a the device specified supports EAX"), PYTHON_BASIC_METHOD(ptAudioControl, muteAll, "Mutes all sounds."), PYTHON_BASIC_METHOD(ptAudioControl, unmuteAll, "Unmutes all sounds."), PYTHON_METHOD_NOARGS(ptAudioControl, isMuted, "Are all sounds muted? Returns 1 if true otherwise returns 0."), - PYTHON_BASIC_METHOD(ptAudioControl, enableSubtitles, "Enables audio subtitles."), - PYTHON_BASIC_METHOD(ptAudioControl, disableSubtitles, "Disables audio subtitles."), - PYTHON_METHOD_NOARGS(ptAudioControl, areSubtitlesEnabled, "Are audio subtitles enabled? Returns 1 if true otherwise returns 0."), + PYTHON_BASIC_METHOD(ptAudioControl, enableSubtitles, "Enables subtitles for audio."), + PYTHON_BASIC_METHOD(ptAudioControl, disableSubtitles, "Disables subtitles for audio."), + PYTHON_METHOD_NOARGS(ptAudioControl, areSubtitlesEnabled, "Are subtitles for the audio enabled? Returns 1 if true otherwise returns 0."), PYTHON_METHOD(ptAudioControl, setPlaybackDevice, "Params: devicename,restart\nSets audio system output device by name, and optionally restarts it"), - PYTHON_METHOD(ptAudioControl, getPlaybackDevice, "Gets the name for the device being used by the audio system"), - PYTHON_METHOD_NOARGS(ptAudioControl, getPlaybackDevices, "Gets the names of all available audio playback devices"), - PYTHON_METHOD(ptAudioControl, getFriendlyDeviceName, "Params: devicename\nReturns the provided device name without any OpenAL prefixes applied"), + PYTHON_METHOD(ptAudioControl, getPlaybackDevice, "Gets the name for the device being used by the audio system."), + PYTHON_METHOD_NOARGS(ptAudioControl, getPlaybackDevices, "Gets the names of all available audio playback devices."), + PYTHON_METHOD(ptAudioControl, getFriendlyDeviceName, "Params: devicename\nReturns the provided device name without any OpenAL prefixes applied."), PYTHON_METHOD_NOARGS(ptAudioControl, canSetMicLevel, "Can the microphone level be set? Returns 1 if true otherwise returns 0."), PYTHON_METHOD(ptAudioControl, setMicLevel, "Params: level\nSets the microphone recording level (0.0 to 1.0)."), diff --git a/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp index a453580e65..01ff93b4df 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp @@ -358,7 +358,7 @@ PYTHON_START_METHODS_TABLE(ptGUIControlMultiLineEdit) PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, unclickable, "Makes this listbox not clickable by the user.\n" "Useful when just displaying a list that is not really selectable."), PYTHON_METHOD(ptGUIControlMultiLineEdit, setScrollPosition, "Params: topLine\nSets the what line is the top line."), - PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getScrollPosition, "Gets what line is the top line."), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getScrollPosition, "Returns what line is the top line."), PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, isAtEnd, "Returns true if the end of the buffer has been reached."), PYTHON_METHOD(ptGUIControlMultiLineEdit, moveCursor, "Params: direction\nMove the cursor in the specified direction (see PtGUIMultiLineDirection)"), PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getCursor, "Get the current position of the cursor in the encoded buffer."), @@ -381,7 +381,7 @@ PYTHON_START_METHODS_TABLE(ptGUIControlMultiLineEdit) PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, isLocked, "Is the multi-line edit control locked? Returns 1 if true otherwise returns 0"), PYTHON_METHOD(ptGUIControlMultiLineEdit, setBufferLimit, "Params: bufferLimit\nSets the buffer max for the editbox"), PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getBufferLimit, "Returns the current buffer limit"), - PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getCurrentLink, "Returns the link the mouse is currently over"), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getCurrentLink, "Returns the link the mouse is currently over."), PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, enableScrollControl, "Enables the scroll control if there is one"), PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, disableScrollControl, "Disables the scroll control if there is one"), PYTHON_METHOD(ptGUIControlMultiLineEdit, deleteLinesFromTop, "Params: numLines\nDeletes the specified number of lines from the top of the text buffer"), diff --git a/Sources/Plasma/FeatureLib/pfPython/pyGameCliGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyGameCliGlue.cpp index 6d1e8fb70c..d86555bd70 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyGameCliGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyGameCliGlue.cpp @@ -77,10 +77,10 @@ PYTHON_GET_DEFINITION_PROXY(ptGameCli, ownerID, GetOwnerID) PYTHON_SET_DEFINITION_READONLY(ptGameCli, ownerID) PYTHON_START_GETSET_TABLE(ptGameCli) - PYTHON_GETSET(ptGameCli, gameID, "Gets the ID of the game instance on the server."), - PYTHON_GETSET(ptGameCli, handler, "Gets the game event handler."), - PYTHON_GETSET(ptGameCli, isLocallyOwned, "Gets whether or not we are the owner of this game instance."), - PYTHON_GETSET(ptGameCli, ownerID, "Gets the ID of the player who owns this game instance."), + PYTHON_GETSET(ptGameCli, gameID, "The ID of the game instance on the server."), + PYTHON_GETSET(ptGameCli, handler, "The game event handler."), + PYTHON_GETSET(ptGameCli, isLocallyOwned, "Whether or not we are the owner of this game instance."), + PYTHON_GETSET(ptGameCli, ownerID, "The ID of the player who owns this game instance."), PYTHON_END_GETSET_TABLE; // Type structure definition diff --git a/Sources/Plasma/FeatureLib/pfPython/pyImageLibModGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyImageLibModGlue.cpp index cf14b0f674..9fe9511062 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyImageLibModGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyImageLibModGlue.cpp @@ -104,7 +104,7 @@ PYTHON_METHOD_DEFINITION_NOARGS(ptImageLibMod, getNames) PYTHON_START_METHODS_TABLE(ptImageLibMod) PYTHON_METHOD(ptImageLibMod, getImage, "Params: name\nReturns the ptImage with the specified name"), PYTHON_METHOD_NOARGS(ptImageLibMod, getImages, "Returns a tuple of the library's ptImages"), - PYTHON_METHOD_NOARGS(ptImageLibMod, getNames, "Returns the list of image names"), + PYTHON_METHOD_NOARGS(ptImageLibMod, getNames, "Returns a tuple of the image names"), PYTHON_END_METHODS_TABLE; // Type structure definition diff --git a/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNodeGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNodeGlue.cpp index c47e90e1e5..aadd1bf9e5 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNodeGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNodeGlue.cpp @@ -161,11 +161,11 @@ PYTHON_METHOD_DEFINITION(ptVaultMarkerGameNode, setMarkers, args) } PYTHON_START_METHODS_TABLE(ptVaultMarkerGameNode) - PYTHON_METHOD_NOARGS(ptVaultMarkerGameNode, getGameGuid, ""), + PYTHON_METHOD_NOARGS(ptVaultMarkerGameNode, getGameGuid, "Returns the marker game's guid"), PYTHON_METHOD_NOARGS(ptVaultMarkerGameNode, getGameName, "Returns the marker game's name"), PYTHON_METHOD_NOARGS(ptVaultMarkerGameNode, getMarkers, "Returns a tuple of markers associated with this game"), PYTHON_METHOD_NOARGS(ptVaultMarkerGameNode, getReward, "Returns a string representing the reward for completing this game"), - PYTHON_METHOD(ptVaultMarkerGameNode, setGameGuid, ""), + PYTHON_METHOD(ptVaultMarkerGameNode, setGameGuid, "Params: guid\nSets the marker game's guid"), PYTHON_METHOD(ptVaultMarkerGameNode, setGameName, "Params: name\nSets marker game's name"), PYTHON_METHOD(ptVaultMarkerGameNode, setMarkers, "Params: markers\nSets markers associated with this game"), PYTHON_METHOD(ptVaultMarkerGameNode, setReward, "Params: reward\nSets the reward for completing this marker game"), From 759cf914110b4532d9cd302f5522470f74706491 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sun, 2 Feb 2025 03:46:47 +0100 Subject: [PATCH 12/18] Fix assorted missing parameter lists and defaults in Python glue --- Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp | 2 +- Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp | 2 +- .../FeatureLib/pfPython/pyAudioControlGlue.cpp | 2 +- .../pfPython/pyGUIControlMultiLineEditGlue.cpp | 4 ++-- .../Plasma/FeatureLib/pfPython/pyGameScoreGlue.cpp | 12 ++++++------ .../pfPython/pyVaultAgeInfoListNodeGlue.cpp | 2 +- .../FeatureLib/pfPython/pyVaultAgeInfoNodeGlue.cpp | 2 +- .../FeatureLib/pfPython/pyVaultAgeLinkNodeGlue.cpp | 2 +- .../FeatureLib/pfPython/pyVaultChronicleNodeGlue.cpp | 2 +- .../pfPython/pyVaultPlayerInfoListNodeGlue.cpp | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp index b08aedf3aa..70062e3d88 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp @@ -653,7 +653,7 @@ PYTHON_START_METHODS_TABLE(ptAvatar) PYTHON_METHOD(ptAvatar, oneShot, "Params: seekKey,duration,usePhysicsFlag,animationName,drivableFlag,reversibleFlag\nPlays a one-shot animation on the avatar"), PYTHON_METHOD(ptAvatar, runBehavior, "Params: behaviorKey,netForceFlag\nRuns a behavior on the avatar. Can be a single or multi-stage behavior."), PYTHON_METHOD(ptAvatar, runBehaviorSetNotify, "Params: behaviorKey,replyKey,netForceFlag\nSame as runBehavior, except send notifications to specified keyed object"), - PYTHON_METHOD(ptAvatar, runCoopAnim, "Params: targetKey,activeAvatarAnim,targetAvatarAnim,dist,move\nSeek near another avatar and run animations on both."), + PYTHON_METHOD(ptAvatar, runCoopAnim, "Params: targetKey,activeAvatarAnim,targetAvatarAnim,range=6,dist=3,move=1\nSeek near another avatar and run animations on both."), PYTHON_METHOD(ptAvatar, nextStage, "Params: behaviorKey,transitionTime,setTimeFlag,newTime,SetDirectionFlag,isForward,netForce\nTells a multistage behavior to go to the next stage (Why does Matt like so many parameters?)"), PYTHON_METHOD(ptAvatar, previousStage, "Params: behaviorKey,transitionTime,setTimeFlag,newTime,SetDirectionFlag,isForward,netForce\nTells a multistage behavior to go to the previous stage"), PYTHON_METHOD(ptAvatar, gotoStage, "Params: behaviorKey,stage,transitionTime,setTimeFlag,newTime,SetDirectionFlag,isForward,netForce\nTells a multistage behavior to go to a particular stage"), diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp index 4d4352056b..7e7d4a6659 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp @@ -196,7 +196,7 @@ PYTHON_GLOBAL_METHOD_DEFINITION(PtGetClientIDFromAvatarKey, args, "Params: avata return PyLong_FromLong(cyMisc::GetClientIDFromAvatarKey(*key)); } -PYTHON_GLOBAL_METHOD_DEFINITION(PtGetNPCByID, args, "This will return the NPC with a specific ID") +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetNPCByID, args, "Params: npcID\nThis will return the NPC with a specific ID") { int npcID; if (!PyArg_ParseTuple(args, "i", &npcID)) diff --git a/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp index 093f4e8802..5b86d53dcc 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp @@ -435,7 +435,7 @@ PYTHON_START_METHODS_TABLE(ptAudioControl) PYTHON_METHOD_NOARGS(ptAudioControl, getPriorityCutoff, "Returns current sound priority"), PYTHON_METHOD(ptAudioControl, setPriorityCutoff, "Params: priority\nSets the sound priority"), PYTHON_METHOD(ptAudioControl, enableVoiceChat, "Params: state\nEnables or disables voice chat."), - PYTHON_METHOD(ptAudioControl, setCaptureDevice, "Sets the audio capture device by name."), + PYTHON_METHOD(ptAudioControl, setCaptureDevice, "Params: devicename\nSets the audio capture device by name."), PYTHON_METHOD_NOARGS(ptAudioControl, getCaptureDevice, "Gets the name for the capture device being used by the audio system."), PYTHON_METHOD_NOARGS(ptAudioControl, getCaptureDevices, "Gets the name of all available audio capture devices."), diff --git a/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp index 01ff93b4df..c52da2073b 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp @@ -388,10 +388,10 @@ PYTHON_START_METHODS_TABLE(ptGUIControlMultiLineEdit) PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getFontSize, "Returns the current default font size"), PYTHON_METHOD(ptGUIControlMultiLineEdit, setFontSize, "Params: fontSize\nSets the default font size for the edit control"), PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, beginUpdate, "Signifies that the control will be updated heavily starting now, so suppress all redraws"), - PYTHON_METHOD(ptGUIControlMultiLineEdit, endUpdate, "Signifies that the massive updates are over. We can now redraw."), + PYTHON_METHOD(ptGUIControlMultiLineEdit, endUpdate, "Params: redraw=True\nSignifies that the massive updates are over. We can now redraw."), PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, isUpdating, "Is someone else already suppressing redraws of the control?"), PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getMargins, "Returns a tuple of (top, left, bottom, right) margins"), - PYTHON_METHOD_WKEY(ptGUIControlMultiLineEdit, setMargins, "Sets the control's margins"), + PYTHON_METHOD_WKEY(ptGUIControlMultiLineEdit, setMargins, "Params: top,left,bottom,right\nSets the control's margins"), PYTHON_END_METHODS_TABLE; // Type structure definition diff --git a/Sources/Plasma/FeatureLib/pfPython/pyGameScoreGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyGameScoreGlue.cpp index 97db348c6a..0f25b1cad8 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyGameScoreGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyGameScoreGlue.cpp @@ -368,13 +368,13 @@ PYTHON_START_METHODS_TABLE(ptGameScore) PYTHON_METHOD_NOARGS(ptGameScore, getOwnerID, "Returns the score owner."), PYTHON_METHOD_NOARGS(ptGameScore, getPoints, "Returns the number of points in this score"), PYTHON_METHOD_NOARGS(ptGameScore, remove, "Removes this score from the server"), - PYTHON_METHOD(ptGameScore, addPoints, "Params: points, key\nAdds points to the score"), - PYTHON_METHOD_WKEY(ptGameScore, transferPoints, "Params: dest, points, key\nTransfers points from this score to another"), + PYTHON_METHOD(ptGameScore, addPoints, "Params: points, key=None\nAdds points to the score"), + PYTHON_METHOD_WKEY(ptGameScore, transferPoints, "Params: dest, points=0, key=None\nTransfers points from this score to another"), PYTHON_METHOD(ptGameScore, setPoints, "Params: numPoints, key\nSets the number of points in the score\nDon't use to add/remove points, use only to reset values!"), - PYTHON_METHOD_STATIC_WKEY(ptGameScore, createAgeScore, "Params: scoreName, type, points, key\nCreates a new score associated with this age"), - PYTHON_METHOD_STATIC_WKEY(ptGameScore, createGlobalScore, "Params: scoreName, type, points, key\nCreates a new global score"), - PYTHON_METHOD_STATIC_WKEY(ptGameScore, createPlayerScore, "Params: scoreName, type, points, key\nCreates a new score associated with this player"), - PYTHON_METHOD_STATIC_WKEY(ptGameScore, createScore, "Params: ownerID, scoreName, type, points, key\nCreates a new score for an arbitrary owner"), + PYTHON_METHOD_STATIC_WKEY(ptGameScore, createAgeScore, "Params: scoreName, type, points=0, key=None\nCreates a new score associated with this age"), + PYTHON_METHOD_STATIC_WKEY(ptGameScore, createGlobalScore, "Params: scoreName, type, points=0, key=None\nCreates a new global score"), + PYTHON_METHOD_STATIC_WKEY(ptGameScore, createPlayerScore, "Params: scoreName, type, points=0, key=None\nCreates a new score associated with this player"), + PYTHON_METHOD_STATIC_WKEY(ptGameScore, createScore, "Params: ownerID, scoreName, type, points=0, key=None\nCreates a new score for an arbitrary owner"), PYTHON_METHOD_STATIC(ptGameScore, findAgeScores, "Params: scoreName, key\nFinds matching scores for this age"), PYTHON_METHOD_STATIC(ptGameScore, findGlobalScores, "Params: scoreName, key\nFinds matching global scores"), PYTHON_METHOD_STATIC(ptGameScore, findPlayerScores, "Params: scoreName, key\nFinds matching player scores"), diff --git a/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNodeGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNodeGlue.cpp index 8ca55cf983..3e65ecc27a 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNodeGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNodeGlue.cpp @@ -105,7 +105,7 @@ PYTHON_START_METHODS_TABLE(ptVaultAgeInfoListNode) PYTHON_END_METHODS_TABLE; // Type structure definition -PLASMA_DEFAULT_TYPE_WBASE(ptVaultAgeInfoListNode, pyVaultFolderNode, "Plasma vault age info list node"); +PLASMA_DEFAULT_TYPE_WBASE(ptVaultAgeInfoListNode, pyVaultFolderNode, "Params: n=0\nPlasma vault age info list node"); // required functions for PyObject interoperability PYTHON_CLASS_VAULT_NODE_NEW_IMPL(ptVaultAgeInfoListNode, pyVaultAgeInfoListNode); diff --git a/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNodeGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNodeGlue.cpp index 89b651a066..2787c440f8 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNodeGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNodeGlue.cpp @@ -282,7 +282,7 @@ PYTHON_START_METHODS_TABLE(ptVaultAgeInfoNode) PYTHON_END_METHODS_TABLE; // Type structure definition -PLASMA_DEFAULT_TYPE_WBASE(ptVaultAgeInfoNode, pyVaultNode, "Plasma vault age info node"); +PLASMA_DEFAULT_TYPE_WBASE(ptVaultAgeInfoNode, pyVaultNode, "Params: n=0\nPlasma vault age info node"); // required functions for PyObject interoperability PYTHON_CLASS_VAULT_NODE_NEW_IMPL(ptVaultAgeInfoNode, pyVaultAgeInfoNode); diff --git a/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNodeGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNodeGlue.cpp index e69de44330..89fc5397d6 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNodeGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNodeGlue.cpp @@ -194,7 +194,7 @@ PYTHON_START_METHODS_TABLE(ptVaultAgeLinkNode) PYTHON_END_METHODS_TABLE; // Type structure definition -PLASMA_DEFAULT_TYPE_WBASE(ptVaultAgeLinkNode, pyVaultNode, "Plasma vault age link node"); +PLASMA_DEFAULT_TYPE_WBASE(ptVaultAgeLinkNode, pyVaultNode, "Params: n=0\nPlasma vault age link node"); // required functions for PyObject interoperability PYTHON_CLASS_VAULT_NODE_NEW_IMPL(ptVaultAgeLinkNode, pyVaultAgeLinkNode) diff --git a/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNodeGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNodeGlue.cpp index 54540ef28b..ccacbbdb97 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNodeGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNodeGlue.cpp @@ -127,7 +127,7 @@ PYTHON_START_METHODS_TABLE(ptVaultChronicleNode) PYTHON_END_METHODS_TABLE; // Type structure definition -PLASMA_DEFAULT_TYPE_WBASE(ptVaultChronicleNode, pyVaultNode, "Plasma vault chronicle node"); +PLASMA_DEFAULT_TYPE_WBASE(ptVaultChronicleNode, pyVaultNode, "Params: n=0\nPlasma vault chronicle node"); // required functions for PyObject interoperability PYTHON_CLASS_VAULT_NODE_NEW_IMPL(ptVaultChronicleNode, pyVaultChronicleNode) diff --git a/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNodeGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNodeGlue.cpp index d6f5ffe635..a9d5d5802c 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNodeGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNodeGlue.cpp @@ -121,7 +121,7 @@ PYTHON_START_METHODS_TABLE(ptVaultPlayerInfoListNode) PYTHON_END_METHODS_TABLE; // Type structure definition -PLASMA_DEFAULT_TYPE_WBASE(ptVaultPlayerInfoListNode, pyVaultFolderNode, "Plasma vault player info list node"); +PLASMA_DEFAULT_TYPE_WBASE(ptVaultPlayerInfoListNode, pyVaultFolderNode, "Params: n=0\nPlasma vault player info list node"); // required functions for PyObject interoperability PYTHON_CLASS_VAULT_NODE_NEW_IMPL(ptVaultPlayerInfoListNode, pyVaultPlayerInfoListNode) From 0d90923c40e3d5ddbc55962aaec1d2608eb77fa4 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sun, 2 Feb 2025 15:38:49 +0100 Subject: [PATCH 13/18] Add type annotation support to stub generator --- Scripts/Python/plasma/generate_stubs.py | 68 ++++++++++++++++--------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/Scripts/Python/plasma/generate_stubs.py b/Scripts/Python/plasma/generate_stubs.py index a5af50307a..f122af3c96 100644 --- a/Scripts/Python/plasma/generate_stubs.py +++ b/Scripts/Python/plasma/generate_stubs.py @@ -62,6 +62,7 @@ PlasmaVaultConstants, ] +docstring_type_prefix = "Type: " docstring_params_prefix = "Params: " def attr_sort_key(item: tuple[str, object]) -> tuple[int, str]: @@ -132,17 +133,28 @@ def iter_attributes(obj: object) -> Iterable[tuple[str, object]]: return attrs -def parse_params_from_doc(doc: str) -> tuple[str, str]: +def parse_type_from_doc(doc: str) -> tuple[str, str]: if doc is None: - return "", "" + doc = "" + + if doc.startswith(docstring_type_prefix): + # The "Type: " prefix is used for both functions/methods and properties. + # For functions, the prefix is followed by a complete function signature, + # including parentheses, parameter names, and (optionally) parameter and return types. + # Example: "Type: (x: int, y: int) -> int" + # For properties, the prefix is followed by a single type. + # Example: "Type: Tuple[str, int]" + type_line, _, doc_body = doc.partition("\n") + type_string = type_line.removeprefix(docstring_type_prefix) + return type_string, doc_body elif doc.startswith(docstring_params_prefix): + # Cyan's original format for function/method signatures. + # The "Params: " prefix is followed by an unparenthesized list of parameter names. + # Example: "Params: x,y" params_line, _, doc_body = doc.partition("\n") params = params_line.removeprefix(docstring_params_prefix) - return params, doc_body + return f"({params})", doc_body else: - # Assume that functions without a "Params: " line have no parameters. - # Safer would be to default to "*args, **kwargs" and require some explicit marker for no parameters, - # but this would require extensive manual updating of the docstrings. return "", doc def format_qualified_name(cls: type, context_module_name: str) -> str: @@ -161,7 +173,7 @@ def add_indents(indent: str, lines: Iterable[str]) -> Iterable[str]: FunctionKind = Literal["function", "method", "classmethod", "staticmethod", "property"] -def generate_function_stub(kind: FunctionKind, name: str, params: str, doc: str) -> Iterable[str]: +def generate_function_stub(kind: FunctionKind, name: str, signature: str, doc: str) -> Iterable[str]: if kind == "function": decorator = None self_param = None @@ -180,17 +192,26 @@ def generate_function_stub(kind: FunctionKind, name: str, params: str, doc: str) else: raise ValueError(f"Unsupported function kind: {kind!r}") - all_params_parts = [] + if not signature: + # Assume that functions without a "Type: " or "Params: " line have no parameters. + # Safer would be to default to "*args, **kwargs" + # and require an explicit "Type: ()" for no parameters, + # but this would require extensive manual updating of the docstrings. + signature = "()" + if self_param is not None: - all_params_parts.append(self_param) - if params: - all_params_parts.append(params) - # No space after the comma for now, to match the existing stubs. - all_params = ",".join(all_params_parts) + # Insert the self parameter into the method signature. + if signature.startswith("()"): + signature = "(" + self_param + ")" + signature.removeprefix("()") + elif signature.startswith("("): + # No space after the comma for now, to match the existing stubs. + signature = "(" + self_param + "," + signature.removeprefix("(") + else: + raise ValueError(f"{docstring_type_prefix!r} declaration in method docstring doesn't look like a function signature: {signature!r}") if decorator is not None: yield decorator - yield f"def {name}({all_params}):" + yield f"def {name}{signature}:" if doc: yield f' """{doc}"""' yield " pass" @@ -216,7 +237,7 @@ def generate_class_stub(name: str, cls: type) -> Iterable[str]: class_parens = "(" + ", ".join(base_names) + ")" - init_params, doc = parse_params_from_doc(cls.__doc__) + init_signature, doc = parse_type_from_doc(cls.__doc__) yield f"class {name}{class_parens}:" if doc: @@ -233,9 +254,9 @@ def generate_class_stub(name: str, cls: type) -> Iterable[str]: yield "" first = False - # Special case for __init__: use the parameters from the class docstring. + # Special case for __init__: use the signature from the class docstring. # Output the string "None" as the docstring for all __init__ methods for now, to match the existing stubs. - yield from add_indents(" ", generate_function_stub("method", name, init_params, "None")) + yield from add_indents(" ", generate_function_stub("method", name, init_signature, "None")) continue elif name.startswith("__"): # Ignore all other special attributes. @@ -274,8 +295,8 @@ def generate_class_stub(name: str, cls: type) -> Iterable[str]: # and simply assume that all other callables are instance methods. kind = "method" - params, doc = parse_params_from_doc(value.__doc__) - yield from add_indents(" ", generate_function_stub(kind, name, params, doc)) + signature, doc = parse_type_from_doc(value.__doc__) + yield from add_indents(" ", generate_function_stub(kind, name, signature, doc)) elif isinstance(value, types.GetSetDescriptorType): # C-defined properties have class getset_descriptor (aka types.GetSetDescriptorType). # (Note: There is also member_descriptor aka types.MemberDescriptorType, @@ -285,8 +306,9 @@ def generate_class_stub(name: str, cls: type) -> Iterable[str]: # (Perhaps we should set the setter to nullptr in that case? Perhaps Python can tell the difference then?) # Right now it doesn't matter anyway, # because the type annotation syntax doesn't differentiate between read-only and read-write attributes. - doc = value.__doc__ - yield f" {name}: Any" + property_type, doc = parse_type_from_doc(value.__doc__) + property_type = property_type or "Any" + yield f" {name}: {property_type}" if doc: yield f' """{doc}"""' else: @@ -331,8 +353,8 @@ def generate_module_stub(module: types.ModuleType) -> Iterable[str]: elif isinstance(value, type): yield from generate_class_stub(name, value) elif callable(value): - params, doc = parse_params_from_doc(value.__doc__) - yield from generate_function_stub("function", name, params, doc) + signature, doc = parse_type_from_doc(value.__doc__) + yield from generate_function_stub("function", name, signature, doc) else: yield f"{name}: {format_qualified_name(type(value), module.__name__)} = ... # = {value!r}" From bb806e9e3fa1bf7a97a8fb6ddbecec9beeec63c3 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sun, 2 Feb 2025 16:03:02 +0100 Subject: [PATCH 14/18] Add existing type annotations from stubs to C++-defined docstrings --- .../FeatureLib/pfPython/cyAvatarGlue.cpp | 2 +- .../Plasma/FeatureLib/pfPython/cyMiscGlue.cpp | 2 +- .../FeatureLib/pfPython/cyMiscGlue2.cpp | 4 +-- .../FeatureLib/pfPython/cyMiscGlue4.cpp | 2 +- .../pyGUIControlMultiLineEditGlue.cpp | 14 +++++------ .../FeatureLib/pfPython/pyGUIDialogGlue.cpp | 4 +-- .../FeatureLib/pfPython/pyGameCliGlue.cpp | 10 ++++---- .../FeatureLib/pfPython/pyGmMarkerGlue.cpp | 25 ++++++++++--------- .../FeatureLib/pfPython/pyImageGlue.cpp | 2 +- .../FeatureLib/pfPython/pyLayerGlue.cpp | 6 ++--- .../FeatureLib/pfPython/pyVaultNodeGlue.cpp | 2 +- .../FeatureLib/pfPython/pyWaveSetGlue.cpp | 4 +-- 12 files changed, 39 insertions(+), 38 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp index 70062e3d88..452f5a0979 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp @@ -704,7 +704,7 @@ PYTHON_START_METHODS_TABLE(ptAvatar) PYTHON_METHOD(ptAvatar, saveClothingToFile, "Params: filename\nSave avatar clothing to a file"), PYTHON_METHOD(ptAvatar, loadClothingFromFile, "Params: filename\nLoad avatar clothing from a file"), - PYTHON_METHOD(ptAvatar, setDontPanicLink, "Params: value\nDisables panic linking to Personal Age (warps the avatar back to the start instead)"), + PYTHON_METHOD(ptAvatar, setDontPanicLink, "Type: (value: bool) -> None\nDisables panic linking to Personal Age (warps the avatar back to the start instead)"), PYTHON_END_METHODS_TABLE; PYTHON_GLOBAL_METHOD_DEFINITION(PtSetBehaviorLoopCount, args, "Params: behaviorKey,stage,loopCount,netForce\nThis will set the loop count for a particular stage in a multistage behavior") diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp index 7e7d4a6659..588a8617e7 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp @@ -135,7 +135,7 @@ PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetLocalPlayer, "Returns a ptPlayer obj return cyMisc::GetLocalPlayer(); } -PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsSolo, "Returns whether we are the only player in the Age") +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsSolo, "Type: () -> bool\nReturns whether we are the only player in the Age") { return plPython::ConvertFrom(cyMisc::IsSolo()); } diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp index aa4f0d40e0..30f6f0c6e1 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp @@ -69,7 +69,7 @@ namespace plPython }; PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtYesNoDialog, args, kwargs, - "Params: cb, message, /, dialogType\n" + "Type: (cb: Union[None, ptKey, Callable], message: str, /, dialogType: int = PtConfirmationType.YesNo) -> None\n" "This will display a confirmation dialog to the user with the text `message` " "This dialog _has_ to be answered by the user, " "and their answer will be returned in a Notify message or callback given by `cb`.") @@ -108,7 +108,7 @@ PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtYesNoDialog, args, kwargs, } PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtLocalizedYesNoDialog, args, kwargs, - "Params: cb, path, *args, /, *, dialogType\n" + "Type: (cb: Union[None, Callable, ptKey], path: str, *args, dialogType: int = PtConfirmationType.YesNo) -> None\n" "This will display a confirmation dialog to the user with the localized text `path` " "with any optional localization `args` applied. This dialog _has_ to be answered by the user, " "and their answer will be returned in a Notify message or callback given by `cb`.") diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp index 3843b10982..c449e2a8c9 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp @@ -611,7 +611,7 @@ PYTHON_GLOBAL_METHOD_DEFINITION(PtEnablePlanarReflections, args, "Params: on\nEn PYTHON_RETURN_NONE; } -PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtSupportsPlanarReflections, "Returns if planar reflections are supported") +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtSupportsPlanarReflections, "Type: () -> bool\nReturns if planar reflections are supported") { return PyBool_FromLong(cyMisc::ArePlanarReflectionsSupported() ? 1 : 0); } diff --git a/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp index c52da2073b..937ff923b8 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp @@ -361,7 +361,7 @@ PYTHON_START_METHODS_TABLE(ptGUIControlMultiLineEdit) PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getScrollPosition, "Returns what line is the top line."), PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, isAtEnd, "Returns true if the end of the buffer has been reached."), PYTHON_METHOD(ptGUIControlMultiLineEdit, moveCursor, "Params: direction\nMove the cursor in the specified direction (see PtGUIMultiLineDirection)"), - PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getCursor, "Get the current position of the cursor in the encoded buffer."), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getCursor, "Type: () -> int\nGet the current position of the cursor in the encoded buffer."), PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, clearBuffer, "Clears all text from the multi-line edit control."), PYTHON_METHOD(ptGUIControlMultiLineEdit, setString, "Params: text\nSets the multi-line edit control string."), PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getString, "Gets the string of the edit control."), @@ -373,15 +373,15 @@ PYTHON_START_METHODS_TABLE(ptGUIControlMultiLineEdit) PYTHON_METHOD(ptGUIControlMultiLineEdit, insertColor, "Params: color\nInserts an encoded color object at the current cursor position.\n" "'color' is a ptColor object."), PYTHON_METHOD(ptGUIControlMultiLineEdit, insertStyle, "Params: style\nInserts an encoded font style at the current cursor position."), - PYTHON_METHOD(ptGUIControlMultiLineEdit, insertLink, "Params: linkId\nInserts a link hotspot at the current cursor position."), - PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, clearLink, "Ends the hyperlink hotspot, if any, at the current cursor position."), + PYTHON_METHOD(ptGUIControlMultiLineEdit, insertLink, "Type: (linkId: int) -> None\nInserts a link hotspot at the current cursor position."), + PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, clearLink, "Type: () -> None\nEnds the hyperlink hotspot, if any, at the current cursor position."), PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, deleteChar, "Deletes a character at the current cursor position."), PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, lock, "Locks the multi-line edit control so the user cannot make changes."), PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, unlock, "Unlocks the multi-line edit control so that the user can make changes."), PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, isLocked, "Is the multi-line edit control locked? Returns 1 if true otherwise returns 0"), PYTHON_METHOD(ptGUIControlMultiLineEdit, setBufferLimit, "Params: bufferLimit\nSets the buffer max for the editbox"), PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getBufferLimit, "Returns the current buffer limit"), - PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getCurrentLink, "Returns the link the mouse is currently over."), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getCurrentLink, "Type: () -> int\nReturns the link the mouse is currently over."), PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, enableScrollControl, "Enables the scroll control if there is one"), PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, disableScrollControl, "Disables the scroll control if there is one"), PYTHON_METHOD(ptGUIControlMultiLineEdit, deleteLinesFromTop, "Params: numLines\nDeletes the specified number of lines from the top of the text buffer"), @@ -389,9 +389,9 @@ PYTHON_START_METHODS_TABLE(ptGUIControlMultiLineEdit) PYTHON_METHOD(ptGUIControlMultiLineEdit, setFontSize, "Params: fontSize\nSets the default font size for the edit control"), PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, beginUpdate, "Signifies that the control will be updated heavily starting now, so suppress all redraws"), PYTHON_METHOD(ptGUIControlMultiLineEdit, endUpdate, "Params: redraw=True\nSignifies that the massive updates are over. We can now redraw."), - PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, isUpdating, "Is someone else already suppressing redraws of the control?"), - PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getMargins, "Returns a tuple of (top, left, bottom, right) margins"), - PYTHON_METHOD_WKEY(ptGUIControlMultiLineEdit, setMargins, "Params: top,left,bottom,right\nSets the control's margins"), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, isUpdating, "Type: () -> bool\nIs someone else already suppressing redraws of the control?"), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getMargins, "Type: () -> Tuple[int, int, int, int]\nReturns a tuple of (top, left, bottom, right) margins"), + PYTHON_METHOD_WKEY(ptGUIControlMultiLineEdit, setMargins, "Type: (top: Optional[int] = None, left: Optional[int] = None, bottom: Optional[int] = None, right: Optional[int] = None) -> None\nSets the control's margins"), PYTHON_END_METHODS_TABLE; // Type structure definition diff --git a/Sources/Plasma/FeatureLib/pfPython/pyGUIDialogGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyGUIDialogGlue.cpp index 7ca87342ec..cb8d766957 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyGUIDialogGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyGUIDialogGlue.cpp @@ -313,14 +313,14 @@ PYTHON_START_METHODS_TABLE(ptGUIDialog) PYTHON_METHOD_NOARGS(ptGUIDialog, getVersion, "UNKNOWN"), PYTHON_METHOD_NOARGS(ptGUIDialog, getNumControls, "Returns the number of controls in this dialog"), PYTHON_METHOD(ptGUIDialog, getControlFromIndex, "Params: index\nReturns the ptKey of the control with the specified index (not tag ID!)"), - PYTHON_METHOD(ptGUIDialog, getControlModFromIndex, "Params: index\nReturns the ptGUIControl with the specified index (not tag ID!)"), + PYTHON_METHOD(ptGUIDialog, getControlModFromIndex, "Type: (index: int) -> ptGUIControl\nReturns the ptGUIControl with the specified index (not tag ID!)"), PYTHON_METHOD(ptGUIDialog, setFocus, "Params: ctrlKey\nSets the control that has input focus"), PYTHON_BASIC_METHOD(ptGUIDialog, noFocus, "Makes sure no control has input focus"), PYTHON_BASIC_METHOD(ptGUIDialog, show, "Shows the dialog"), PYTHON_BASIC_METHOD(ptGUIDialog, showNoReset, "Show dialog without resetting clickables"), PYTHON_BASIC_METHOD(ptGUIDialog, hide, "Hides the dialog"), PYTHON_METHOD(ptGUIDialog, getControlFromTag, "Params: tagID\nReturns the ptKey of the control with the specified tag ID"), - PYTHON_METHOD(ptGUIDialog, getControlModFromTag, "Params: tagID\nReturns the ptGUIControl with the specified tag ID"), + PYTHON_METHOD(ptGUIDialog, getControlModFromTag, "Type: (tagID: int) -> ptGUIControl\nReturns the ptGUIControl with the specified tag ID"), PYTHON_METHOD_NOARGS(ptGUIDialog, getForeColor, "Returns the fore color as a ptColor object"), PYTHON_METHOD_NOARGS(ptGUIDialog, getSelectColor, "Returns the select color as a ptColor object"), PYTHON_METHOD_NOARGS(ptGUIDialog, getBackColor, "Returns the back color as a ptColor object"), diff --git a/Sources/Plasma/FeatureLib/pfPython/pyGameCliGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyGameCliGlue.cpp index d86555bd70..29de961aa1 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyGameCliGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyGameCliGlue.cpp @@ -61,7 +61,7 @@ PYTHON_METHOD_DEFINITION_NOARGS(ptGameCli, leaveGame) } PYTHON_START_METHODS_TABLE(ptGameCli) - PYTHON_METHOD_NOARGS(ptGameCli, leaveGame, "Explicitly ask the server to allow us to leave the game."), + PYTHON_METHOD_NOARGS(ptGameCli, leaveGame, "Type: () -> None\nExplicitly ask the server to allow us to leave the game."), PYTHON_END_METHODS_TABLE; PYTHON_GET_DEFINITION_PROXY(ptGameCli, gameID, GetGameID) @@ -77,10 +77,10 @@ PYTHON_GET_DEFINITION_PROXY(ptGameCli, ownerID, GetOwnerID) PYTHON_SET_DEFINITION_READONLY(ptGameCli, ownerID) PYTHON_START_GETSET_TABLE(ptGameCli) - PYTHON_GETSET(ptGameCli, gameID, "The ID of the game instance on the server."), - PYTHON_GETSET(ptGameCli, handler, "The game event handler."), - PYTHON_GETSET(ptGameCli, isLocallyOwned, "Whether or not we are the owner of this game instance."), - PYTHON_GETSET(ptGameCli, ownerID, "The ID of the player who owns this game instance."), + PYTHON_GETSET(ptGameCli, gameID, "Type: int\nThe ID of the game instance on the server."), + PYTHON_GETSET(ptGameCli, handler, "Type: Any\nThe game event handler."), + PYTHON_GETSET(ptGameCli, isLocallyOwned, "Type: bool\nWhether or not we are the owner of this game instance."), + PYTHON_GETSET(ptGameCli, ownerID, "Type: int\nThe ID of the player who owns this game instance."), PYTHON_END_GETSET_TABLE; // Type structure definition diff --git a/Sources/Plasma/FeatureLib/pfPython/pyGmMarkerGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyGmMarkerGlue.cpp index 744d67b451..3ff2f3d2c5 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyGmMarkerGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyGmMarkerGlue.cpp @@ -184,19 +184,20 @@ PYTHON_METHOD_DEFINITION_STATIC_NOARGS(ptGmMarker, isSupported) } PYTHON_START_METHODS_TABLE(ptGmMarker) - PYTHON_METHOD_NOARGS(ptGmMarker, startGame, "Request for the server to start the marker game."), - PYTHON_METHOD_NOARGS(ptGmMarker, pauseGame, "Request for the server to pause the marker game."), - PYTHON_METHOD_NOARGS(ptGmMarker, resetGame, "Request for the server to clear all markers to the uncaptured state."), - PYTHON_METHOD(ptGmMarker, changeGameName, "Request for the server to change the internal marker game name."), - PYTHON_METHOD(ptGmMarker, changeTimeLimit, "Request for the server to change the marker game's time limit."), - PYTHON_METHOD_NOARGS(ptGmMarker, deleteGame, "Request for the server to delete all data associated with this game, including " + PYTHON_METHOD_NOARGS(ptGmMarker, startGame, "Type: () -> None\nRequest for the server to start the marker game."), + PYTHON_METHOD_NOARGS(ptGmMarker, pauseGame, "Type: () -> None\nRequest for the server to pause the marker game."), + PYTHON_METHOD_NOARGS(ptGmMarker, resetGame, "Type: () -> None\nRequest for the server to clear all markers to the uncaptured state."), + PYTHON_METHOD(ptGmMarker, changeGameName, "Type: (name: str) -> None\nRequest for the server to change the internal marker game name."), + PYTHON_METHOD(ptGmMarker, changeTimeLimit, "Type: (timeLimit: int) -> None\nRequest for the server to change the marker game's time limit."), + PYTHON_METHOD_NOARGS(ptGmMarker, deleteGame, "Type: () -> None\n" + "Request for the server to delete all data associated with this game, including " "the marker definitions and game name."), - PYTHON_METHOD_WKEY(ptGmMarker, addMarker, "Request for the server to add a new marker to the game."), - PYTHON_METHOD(ptGmMarker, deleteMarker, "Request for the server to delete a specific marker from the game."), - PYTHON_METHOD(ptGmMarker, changeMarkerName, "Request for the server to change the name of a specific marker from the game."), - PYTHON_METHOD(ptGmMarker, captureMarker, "Request for the server to register a capture of the specified marker for our team."), - PYTHON_METHOD_STATIC(ptGmMarker, create, "Initialize a new marker game client with the server."), - PYTHON_METHOD_STATIC_NOARGS(ptGmMarker, isSupported, "Checks for the presence of a server-side marker game manager."), + PYTHON_METHOD_WKEY(ptGmMarker, addMarker, "Type: (x: float, y: float, z: float, name: str, age: str) -> None\nRequest for the server to add a new marker to the game."), + PYTHON_METHOD(ptGmMarker, deleteMarker, "Type: (markerID: int) -> None\nRequest for the server to delete a specific marker from the game."), + PYTHON_METHOD(ptGmMarker, changeMarkerName, "Type: (markerID: int) -> None\nRequest for the server to change the name of a specific marker from the game."), + PYTHON_METHOD(ptGmMarker, captureMarker, "Type: (markerId: int) -> None\nRequest for the server to register a capture of the specified marker for our team."), + PYTHON_METHOD_STATIC(ptGmMarker, create, "Type: (handler: Any, gameType: int, templateId: Optional[str]) -> None\nInitialize a new marker game client with the server."), + PYTHON_METHOD_STATIC_NOARGS(ptGmMarker, isSupported, "Type: () -> bool\nChecks for the presence of a server-side marker game manager."), PYTHON_END_METHODS_TABLE; // Type structure definition diff --git a/Sources/Plasma/FeatureLib/pfPython/pyImageGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyImageGlue.cpp index b6d1fd5f29..8406031ac9 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyImageGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyImageGlue.cpp @@ -252,7 +252,7 @@ void pyImage::AddPlasmaClasses(PyObject *m) } #ifndef BUILDING_PYPLASMA -PYTHON_GLOBAL_METHOD_DEFINITION(PtFindImage, args, "Params: name\nFind an already loaded image by name.") +PYTHON_GLOBAL_METHOD_DEFINITION(PtFindImage, args, "Type: (name: str) -> Iterable[ptImage]\nFind an already loaded image by name.") { ST::string name; if (!PyArg_ParseTuple(args, "O&", PyUnicode_STStringConverter, &name)) { diff --git a/Sources/Plasma/FeatureLib/pfPython/pyLayerGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyLayerGlue.cpp index d89917c510..3d9f1bdc47 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyLayerGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyLayerGlue.cpp @@ -92,8 +92,8 @@ PYTHON_METHOD_DEFINITION(ptLayer, setTexture, args) } PYTHON_START_METHODS_TABLE(ptLayer) - PYTHON_METHOD_NOARGS(ptLayer, getTexture, "Returns the image texture of the layer"), - PYTHON_METHOD(ptLayer, setTexture, "Params: image\nSets the ptImage texture of the layer"), + PYTHON_METHOD_NOARGS(ptLayer, getTexture, "Type: () -> ptImage\nReturns the image texture of the layer"), + PYTHON_METHOD(ptLayer, setTexture, "Type: (image: ptImage) -> None\nSets the ptImage texture of the layer"), PYTHON_END_METHODS_TABLE; // Type structure definition @@ -147,7 +147,7 @@ void pyLayer::AddPlasmaClasses(PyObject* m) PYTHON_CLASS_IMPORT_END(m); } -PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtFindLayer, args, kwds, "Params: name\nFind a layer by name.") +PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtFindLayer, args, kwds, "Type: (name: str, age: str = \"\", page: str = \"\") -> Optional[ptLayer]\nFind a layer by name.") { const char* kwdlist[]{"name", "age", "page", nullptr}; ST::string name, age, page; diff --git a/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeGlue.cpp index d503b3447d..54f40f10c5 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeGlue.cpp @@ -487,7 +487,7 @@ PYTHON_START_METHODS_TABLE(ptVaultNode) PYTHON_BASIC_METHOD(ptVaultNode, removeAllNodes, "Removes all the child nodes on this node."), PYTHON_METHOD(ptVaultNode, hasNode, "Params: id\nReturns true if node if a child node"), PYTHON_METHOD(ptVaultNode, getNode, "Params: id\nReturns ptVaultNodeRef if is a child node, or None"), - PYTHON_METHOD_WKEY(ptVaultNode, findNode, "Params: templateNode, maxDepth=1\nReturns ptVaultNode if child node found matching template, or None"), + PYTHON_METHOD_WKEY(ptVaultNode, findNode, "Type: (templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]\nReturns ptVaultNode if child node found matching template, or None"), PYTHON_METHOD(ptVaultNode, addNode, "Params: node,cb=None,cbContext=0\nAdds 'node'(ptVaultNode) as a child to this node."), PYTHON_METHOD(ptVaultNode, linkToNode, "Params: nodeID,cb=None,cbContext=0\nAdds a link to the node designated by nodeID"), diff --git a/Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp index 370e7a3511..e310bbd2dc 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp @@ -246,8 +246,8 @@ PYTHON_START_METHODS_TABLE(ptWaveSet) WAVESET_OBJ(EnvCenter), WAVESET_FLOAT(EnvRadius), - PYTHON_METHOD(ptWaveSet, addBuoy, "Params: soKey\nAdds the specified object as a buoy"), - PYTHON_METHOD(ptWaveSet, removeBuoy, "Params: soKey\nRemoves the specified object as a buoy"), + PYTHON_METHOD(ptWaveSet, addBuoy, "Type: (soKey: ptKey) -> None\nAdds the specified object as a buoy"), + PYTHON_METHOD(ptWaveSet, removeBuoy, "Type: (soKey: ptKey) -> None\nRemoves the specified object as a buoy"), PYTHON_END_METHODS_TABLE; // Type structure definition From 281e484e87fa0f5d90a6b4f7b5865f080c46ac04 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sun, 2 Feb 2025 16:06:23 +0100 Subject: [PATCH 15/18] Add special case for Plasma needing an import of PlasmaConstants --- Scripts/Python/plasma/generate_stubs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Scripts/Python/plasma/generate_stubs.py b/Scripts/Python/plasma/generate_stubs.py index f122af3c96..03b480a288 100644 --- a/Scripts/Python/plasma/generate_stubs.py +++ b/Scripts/Python/plasma/generate_stubs.py @@ -341,6 +341,12 @@ def generate_module_stub(module: types.ModuleType) -> Iterable[str]: # Hardcoded imports for type annotations: yield "from __future__ import annotations" + if module.__name__ == "Plasma": + # Some parameter default values in the Plasma module + # use constant values from PlasmaConstants. + # Not sure how to implement a good generic solution to this problem, + # so for now, just hardcode this dependency... :( + yield "from PlasmaConstants import *" yield "from typing import *" for name, value in iter_attributes(module): From 74dba5fef252bc023a8340f53b84c9fbecc24507 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sun, 2 Feb 2025 16:10:05 +0100 Subject: [PATCH 16/18] Add missing period to C++-defined PtYesNoDialog docstring --- Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp index 30f6f0c6e1..eb1ca704e0 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp @@ -70,7 +70,7 @@ namespace plPython PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtYesNoDialog, args, kwargs, "Type: (cb: Union[None, ptKey, Callable], message: str, /, dialogType: int = PtConfirmationType.YesNo) -> None\n" - "This will display a confirmation dialog to the user with the text `message` " + "This will display a confirmation dialog to the user with the text `message`. " "This dialog _has_ to be answered by the user, " "and their answer will be returned in a Notify message or callback given by `cb`.") { From e76a5a809ddf4dd4164a4c7f687ed4a5fda7fa0c Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sun, 2 Feb 2025 16:11:13 +0100 Subject: [PATCH 17/18] Regenerate Python stubs using generate_stubs.py --- Scripts/Python/plasma/Plasma.py | 623 ++++++++++++------ Scripts/Python/plasma/PlasmaConstants.py | 22 +- Scripts/Python/plasma/PlasmaGame.py | 123 +++- Scripts/Python/plasma/PlasmaGameConstants.py | 15 +- Scripts/Python/plasma/PlasmaNetConstants.py | 13 +- Scripts/Python/plasma/PlasmaVaultConstants.py | 13 +- 6 files changed, 556 insertions(+), 253 deletions(-) diff --git a/Scripts/Python/plasma/Plasma.py b/Scripts/Python/plasma/Plasma.py index 72fb0b7116..07da486d36 100644 --- a/Scripts/Python/plasma/Plasma.py +++ b/Scripts/Python/plasma/Plasma.py @@ -41,9 +41,17 @@ *==LICENSE==* """ +# NOTE: This stub file was generated automatically from Plasma's Python interface. +# Do not edit this file manually. +# To change any of the docstrings or function signatures, +# edit the corresponding C++ glue code in pfPython. +# If the Python interface has changed, regenerate these stubs +# by running the following call in the in-game Python console: +# >>> __import__("generate_stubs").run() + from __future__ import annotations from PlasmaConstants import * -from typing import Callable, Iterable, Optional, Tuple, Union +from typing import * def PtAcceptInviteInGame(friendName,inviteKey): """Sends a VaultTask to the server to perform the invite""" @@ -60,7 +68,7 @@ def PtAtTimeCallback(selfkey,time,id): - 'id' is an integer id that will be returned in the OnTimer call""" pass -def PtAttachObject(child,parent): +def PtAttachObject(child,parent,netForce=False): """Attach child to parent based on ptKey or ptSceneobject - childKey is the ptKey or ptSceneobject of the one being attached - parentKey is the ptKey or ptSceneobject of the one being attached to @@ -87,10 +95,6 @@ def PtAvatarExitAFK(): """Tells the local avatar to exit AwayFromKeyboard idle loop (netpropagated)""" pass -def PtAvatarExitAnimMode(animName): - """Exit custom anim loop (netpropagated)""" - pass - def PtAvatarExitLookingAtKI(): """Tells the local avatar to exit looking at KI idle loop (netpropagated)""" pass @@ -147,6 +151,10 @@ def PtClearTimerCallbacks(key): """This will remove timer callbacks to the specified key""" pass +def PtCloneKey(key, loading=False): + """Creates clone of key""" + pass + def PtConsole(command): """This will execute 'command' as if it were typed into the Plasma console.""" pass @@ -172,15 +180,15 @@ def PtDebugAssert(cond, msg): """Debug only: Assert if condition is false.""" pass -def PtDebugPrint(*msgs, **kwargs): - """Prints msgs to the Python log given the message's level""" +def PtDebugPrint(*msgs, level, sep, end): + """Prints msgs to the Python log given the message's level, optionally separated and terminated by the given strings""" pass def PtDeletePlayer(playerInt): """Deletes a player associated with the current account""" pass -def PtDetachObject(child,parent): +def PtDetachObject(child,parent,netForce=False): """Detach child from parent based on ptKey or ptSceneobject - child is the ptKey or ptSceneobject of the one being detached - parent is the ptKey or ptSceneobject of the one being detached from @@ -306,19 +314,32 @@ def PtFileExists(filename): """Returns true if the specified file exists""" pass +def PtFindActivator(name): + """This will try to find an activator based on its name +- it will return a ptKey if found- it will return None if not found""" + pass + +def PtFindClones(key): + """Finds all clones""" + pass + def PtFindImage(name: str) -> Iterable[ptImage]: - """Find an already loaded image by name""" - ... + """Find an already loaded image by name.""" + pass def PtFindLayer(name: str, age: str = "", page: str = "") -> Optional[ptLayer]: - """Find a layer by name""" - ... + """Find a layer by name.""" + pass def PtFindSceneobject(name,ageName): """This will try to find a sceneobject based on its name and what age its in - it will return a ptSceneObject if found- if not found then a NameError exception will happen""" pass +def PtFindSceneobjects(name): + """This will try to find a any sceneobject containing string in name""" + pass + def PtFirstPerson(): """is the local avatar in first person mode""" pass @@ -353,6 +374,10 @@ def PtForceCursorShown(): Only call if other methods won't work. This is the only way to show the cursor after a call to PtForceMouseHidden()""" pass +def PtForceVaultNodeUpdate(nodeId): + """Forces a vault node to update""" + pass + def PtGMTtoDniTime(gtime): """Converts GMT time (passed in) to D'Ni time""" pass @@ -369,6 +394,10 @@ def PtGUICursorOn(): """Turns the GUI cursor on""" pass +def PtGetAIAvatarsByModelName(modelName): + """Returns a list of tuples representing the matching ai avatars""" + pass + def PtGetAccountName(): """Returns the account name for the current account""" pass @@ -497,7 +526,7 @@ def PtGetNPCByID(npcID): pass def PtGetNPCCount(): - """This will return the number of NPCs in the current age""" + """Returns the number of NPCs in the current age""" pass def PtGetNumCameras(): @@ -557,6 +586,10 @@ def PtGetUserPath(): """Returns the path to the client's root user directory.""" pass +def PtGuidGenerate(): + """Returns string representation for a new guid""" + pass + def PtHideDialog(dialogName): """Hide a GUI dialog by name (does not unload dialog)""" pass @@ -611,7 +644,7 @@ def PtIsSinglePlayerMode(): def PtIsSolo() -> bool: """Returns whether we are the only player in the Age""" - ... + pass def PtKillParticles(timeRemaining,pctToKill,particleSystem): """Tells particleSystem to kill pctToKill percent of its particles""" @@ -639,6 +672,11 @@ def PtLoadJPEGFromDisk(filename,width,height): Returns a pyImage of the specified file.""" pass +def PtLoadPNGFromDisk(filename,width,height): + """The image will be resized to fit the width and height arguments. Set to 0 if resizing is not desired. +Returns a pyImage of the specified file.""" + pass + def PtLocalAvatarIsMoving(): """Returns true if the local avatar is moving (a movement key is held down)""" pass @@ -648,10 +686,8 @@ def PtLocalAvatarRunKeyDown(): pass def PtLocalizedYesNoDialog(cb: Union[None, Callable, ptKey], path: str, *args, dialogType: int = PtConfirmationType.YesNo) -> None: - """This will display a confirmation dialog to the user with the localized text `path` - with any optional localization `args` applied. This dialog _has_ to be answered by the user, - and their answer will be returned in a Notify message or callback given by `cb`.""" - ... + """This will display a confirmation dialog to the user with the localized text `path` with any optional localization `args` applied. This dialog _has_ to be answered by the user, and their answer will be returned in a Notify message or callback given by `cb`.""" + pass def PtMaxListenDistSq(): """Returns the maximum distance (squared) of the listen range""" @@ -673,11 +709,11 @@ def PtNotifyOffererLinkRejected(offerer): """Tell the offerer that we rejected the link offer""" pass -def PtPageInNode(nodeName, ageName=""): +def PtPageInNode(nodeName, netForce=False, ageName=""): """Pages in node, or a list of nodes""" pass -def PtPageOutNode(nodeName): +def PtPageOutNode(nodeName, netForce=False): """Pages out a node""" pass @@ -709,6 +745,10 @@ def PtSendChatToCCR(message,CCRPlayerID): """Sends a chat message to a CCR that has contacted this player""" pass +def PtSendFriendInvite(emailAddress, toName = "Friend"): + """Sends an email with invite code""" + pass + def PtSendKIGZMarkerMsg(markerNumber,sender): """Same as PtSendKIMessageInt except 'sender' could get a notify message back """ @@ -720,10 +760,14 @@ def PtSendKIMessage(command,value): pass def PtSendKIMessageInt(command,value): - """Same as PtSendKIMessage except the value is guaranteed to be a UInt32 + """Same as PtSendKIMessage except the value is guaranteed to be a uint32_t (for things like player IDs)""" pass +def PtSendKIRegisterImagerMsg(imagerName, sender): + """Sends a message to the KI to register the specified imager""" + pass + def PtSendPetitionToCCR(message,reason=0,title=""): """Sends a petition with a message to the CCR group""" pass @@ -815,6 +859,10 @@ def PtSetShadowVisDistance(distance): """Set the maximum shadow visibility distance""" pass +def PtSetShareAgeInstanceGuid(instanceGuid): + """This sets the desired age instance guid for the receiver to link to""" + pass + def PtSetShareSpawnPoint(spawnPoint): """This sets the desired spawn point for the receiver to link to""" pass @@ -837,7 +885,7 @@ def PtStartScreenCapture(selfKey,width=800,height=600): def PtSupportsPlanarReflections() -> bool: """Returns if planar reflections are supported""" - ... + pass def PtToggleAvatarClickability(on): """Turns on and off our avatar's clickability""" @@ -848,7 +896,8 @@ def PtTransferParticlesToObject(objFrom, objTo, num): pass def PtUnLoadAvatarModel(avatarKey): - """Unloads the specified avatar model""" + """Forcibly unloads the specified avatar model. +Do not use this method unless you require fine-grained control of avatar unloading.""" pass def PtUnloadAllBookGUIs(): @@ -868,6 +917,10 @@ def PtValidateKey(key): otherwise returns false(0)""" pass +def PtVaultDownload(nodeId): + """Downloads the vault tree of the given nodeid""" + pass + def PtWasLocallyNotified(selfKey): """Returns 1 if the last notify was local or 0 if the notify originated on the network""" pass @@ -889,10 +942,8 @@ def PtWhatGUIControlType(guiKey): pass def PtYesNoDialog(cb: Union[None, ptKey, Callable], message: str, /, dialogType: int = PtConfirmationType.YesNo) -> None: - """This will display a confirmation dialog to the user with the text `message`. This dialog - _has_ to be answered by the user, and their answer will be returned in a Notify message - or callback given by `cb`.""" - ... + """This will display a confirmation dialog to the user with the text `message`. This dialog _has_ to be answered by the user, and their answer will be returned in a Notify message or callback given by `cb`.""" + pass class ptAgeInfoStruct: """Class to hold AgeInfo struct data""" @@ -904,6 +955,10 @@ def copyFrom(self,other): """Copies data from one ptAgeInfoStruct or ptAgeInfoStructRef to this one""" pass + def getAgeDescription(self): + """Gets the description part of the Age name""" + pass + def getAgeFilename(self): """Gets the Age's filename""" pass @@ -932,6 +987,10 @@ def getDisplayName(self): """Returns a string that is the displayable name of the age instance""" pass + def setAgeDescription(self,udName): + """Sets the description part of the Age""" + pass + def setAgeFilename(self,filename): """Sets the filename of the Age""" pass @@ -1325,7 +1384,7 @@ def getCaptureDevices(self): """Gets the name of all available audio capture devices.""" pass - def getFriendlyDeviceName(self, devicename): + def getFriendlyDeviceName(self,devicename): """Returns the provided device name without any OpenAL prefixes applied.""" pass @@ -1333,10 +1392,6 @@ def getGUIVolume(self): """Returns the volume (0.0 to 1.0) for the GUI dialogs.""" pass - def getHighestMode(self): - """Gets the highest possible audio system mode""" - pass - def getMicLevel(self): """Returns the microphone recording level (0.0 to 1.0).""" pass @@ -1349,10 +1404,6 @@ def getNPCVoiceVolume(self): """Returns the volume (0.0 to 1.0) for the NPC's voice.""" pass - def getNumAudioDevices(self): - """Returns the number of available audio devices.""" - pass - def getPlaybackDevice(self): """Gets the name for the device being used by the audio system.""" pass @@ -1410,7 +1461,7 @@ def setAmbienceVolume(self,volume): This only sets the volume for this game session.""" pass - def setCaptureDevice(self, devicename): + def setCaptureDevice(self,devicename): """Sets the audio capture device by name.""" pass @@ -1608,7 +1659,7 @@ def saveClothingToFile(self,filename): """Save avatar clothing to a file""" pass - def setDontPanicLink(self, value: bool) -> None: + def setDontPanicLink(self,value: bool) -> None: """Disables panic linking to Personal Age (warps the avatar back to the start instead)""" pass @@ -1721,121 +1772,6 @@ def show(self,startOpened): """Shows the book closed, or open if the the startOpened flag is true""" pass -class ptCCRAge: - """CCR only: CCR age info struct""" - def __init__(self): - """None""" - pass - -class ptCCRMgr: - """CCR only: accessor class to the CCR manager""" - def __init__(self): - """None""" - pass - - def banLinking(self,pid, banFlag): - """Set the ban linking flag for a player""" - pass - - def beginCommunication(self,pid, message): - """Begin a CCR communication with a player""" - pass - - def clippingOff(self): - """Disables clipping for this player""" - pass - - def clippingOn(self): - """Enables clipping for this player""" - pass - - def endCommunication(self,pid): - """End CCR communications with a player""" - pass - - def getClipping(self): - """Is clipping on for this player? Returns 1 if true otherwise returns 0""" - pass - - def getErrorString(self,errorNumber): - """Returns the error string that corresponds to 'errorNumber'""" - pass - - def getLevel(self): - """Returns the current CCR level for this player""" - pass - - def getPlayerInfo(self,player, cbObject, cbContext): - """Finds a player that matches 'player' (which is an id or name).""" - pass - - def linkPlayerHere(self,pid): - """Links player to where I am""" - pass - - def linkPlayerToAge(self,ageInfoStruct,pid): - """Links player to a specified age""" - pass - - def linkToAge(self,age,pid): - """Links to player's version of age""" - pass - - def linkToMyNeighborhoodAge(self,pid): - """Links this player to their neighborhood""" - pass - - def linkToMyPersonalAge(self,pid): - """Links this player to their personal Age.""" - pass - - def linkToPlayersAge(self,pid): - """Link to where the player is""" - pass - - def logMessage(self,message): - """Logs 'message' somewhere...?""" - pass - - def makeInvisible(self,level): - """Makes this player invisible to 'level'""" - pass - - def sendCommunication(self,pid, message): - """Send a CCR communication to a player""" - pass - - def setAwayStatus(self,awayFlag): - """Set the away flag for CCRs""" - pass - - def silencePlayer(self,pid, silenceFlag): - """Set the silence player flag for a player""" - pass - - def systemMessage(self): - """Params message -Send a system wide CCR message""" - pass - - def toggleClipping(self): - """Toggles clipping for this player""" - pass - - def warpPlayerHere(self,pid): - """warp the player to here""" - pass - - def warpToPlayer(self,pid): - """warp to where the player is""" - pass - -class ptCCRPlayerInfo: - """CCR only: CCR player info struct""" - def __init__(self): - """None""" - pass - class ptCamera: """Plasma camera class""" def __init__(self): @@ -1855,6 +1791,10 @@ def enableFirstPersonOverride(self): """Allows the user to override the camera and go to a first person camera.""" pass + def getAspectRatio(self): + """Get the global aspect ratio""" + pass + def getFOV(self): """Returns the current camera's FOV(h)""" pass @@ -1871,6 +1811,10 @@ def isWalkAndVerticalPan(self): """Returns true if we are walking and chewing gum""" pass + def refreshFOV(self): + """Refreshes the FOV""" + pass + def restore(self,cameraKey): """Restores camera to saved one""" pass @@ -1883,6 +1827,10 @@ def set(self,cameraKey,time,save): """DO NOT USE""" pass + def setAspectRatio(self,aspect): + """Set the global aspect ratio""" + pass + def setFOV(self,fov, time): """Sets the current cameras FOV (based on h)""" pass @@ -1906,11 +1854,11 @@ def undoFirstPerson(self): class ptCluster: """Creates a new ptCluster""" - def __init__(self,ey): + def __init__(self,key): """None""" pass - def setVisible(self,isible): + def setVisible(self,visible): """Shows or hides the cluster object""" pass @@ -2394,6 +2342,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -2458,6 +2410,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -2516,6 +2472,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -2588,6 +2548,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -2650,6 +2614,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -2722,6 +2690,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -2780,6 +2752,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -2856,6 +2832,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -2919,6 +2899,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -2987,6 +2971,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -3050,6 +3038,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -3118,6 +3110,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -3180,6 +3176,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -3253,6 +3253,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -3319,6 +3323,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -3407,6 +3415,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -3489,6 +3501,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -3569,6 +3585,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -3639,6 +3659,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -3719,6 +3743,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -3861,6 +3889,10 @@ def getElement(self,index): """Get the string of the item at 'index' in the listbox.""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -3970,6 +4002,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -4039,6 +4075,7 @@ def __init__(self,ctrlKey): def beginUpdate(self): """Signifies that the control will be updated heavily starting now, so suppress all redraws""" + pass def clearBuffer(self): """Clears all text from the multi-line edit control.""" @@ -4046,7 +4083,7 @@ def clearBuffer(self): def clearLink(self) -> None: """Ends the hyperlink hotspot, if any, at the current cursor position.""" - ... + pass def clickable(self): """Sets this listbox to be clickable by the user.""" @@ -4076,7 +4113,7 @@ def enableScrollControl(self): """Enables the scroll control if there is one""" pass - def endUpdate(self, redraw=True): + def endUpdate(self,redraw=True): """Signifies that the massive updates are over. We can now redraw.""" pass @@ -4102,16 +4139,20 @@ def getBufferSize(self): def getCurrentLink(self) -> int: """Returns the link the mouse is currently over.""" - ... + pass def getCursor(self) -> int: """Get the current position of the cursor in the encoded buffer.""" - ... + pass def getEncodedBuffer(self): """Returns the encoded buffer in a python buffer object.""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the current default font size""" pass @@ -4165,9 +4206,9 @@ def insertColor(self,color): 'color' is a ptColor object.""" pass - def insertLink(self, linkId: int) -> None: + def insertLink(self,linkId: int) -> None: """Inserts a link hotspot at the current cursor position.""" - ... + pass def insertString(self,string): """Inserts a string at the current cursor position.""" @@ -4178,7 +4219,7 @@ def insertStyle(self,style): pass def isAtEnd(self): - """Returns whether the cursor is at the end.""" + """Returns true if the end of the buffer has been reached.""" pass def isEnabled(self): @@ -4199,7 +4240,7 @@ def isLocked(self): def isUpdating(self) -> bool: """Is someone else already suppressing redraws of the control?""" - ... + pass def isVisible(self): """Returns whether this GUI control is visible""" @@ -4229,13 +4270,18 @@ def setBufferLimit(self,bufferLimit): """Sets the buffer max for the editbox""" pass - def setEncodedBufferW(self,bufferObject): + def setEncodedBuffer(self,bufferObject): """Sets the edit control to the encoded buffer in the python buffer object.""" + pass def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the default font size for the edit control""" pass @@ -4244,7 +4290,7 @@ def setForeColor(self,r,g,b,a): """Sets the foreground color""" pass - def setMargins(self, top: Optional[int] = None, left: Optional[int] = None, bottom: Optional[int] = None, right: Optional[int] = None) -> None: + def setMargins(self,top: Optional[int] = None, left: Optional[int] = None, bottom: Optional[int] = None, right: Optional[int] = None) -> None: """Sets the control's margins""" pass @@ -4264,7 +4310,7 @@ def setSelectColor(self,r,g,b,a): """Sets the selection color""" pass - def setString(self,asciiText): + def setString(self,text): """Sets the multi-line edit control string.""" pass @@ -4319,6 +4365,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -4399,6 +4449,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -4469,6 +4523,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -4537,6 +4595,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -4599,6 +4661,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -4671,6 +4737,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,size): """Don't use""" pass @@ -4737,6 +4807,10 @@ def getBackSelectColor(self): """Returns the background selection color""" pass + def getFontFlags(self): + """Returns the current fontflags""" + pass + def getFontSize(self): """Returns the font size""" pass @@ -4817,6 +4891,10 @@ def setFocus(self,state): """Sets the state of the focus of this GUI control""" pass + def setFontFlags(self,fontflags): + """Sets current fontflags""" + pass + def setFontSize(self,fontSize): """Sets the font size""" pass @@ -4891,13 +4969,13 @@ def getControlFromTag(self,tagID): """Returns the ptKey of the control with the specified tag ID""" pass - def getControlModFromIndex(self, index: int) -> ptGUIControl: + def getControlModFromIndex(self,index: int) -> ptGUIControl: """Returns the ptGUIControl with the specified index (not tag ID!)""" - ... + pass - def getControlModFromTag(self, tagID: int) -> ptGUIControl: - """Returns the GUI control with the specified tag ID""" - ... + def getControlModFromTag(self,tagID: int) -> ptGUIControl: + """Returns the ptGUIControl with the specified tag ID""" + pass def getFontSize(self): """Returns the font size""" @@ -5088,7 +5166,7 @@ def __init__(self): """None""" pass - def addPoints(self, points, key=None): + def addPoints(self,points, key=None): """Adds points to the score""" pass @@ -5151,7 +5229,7 @@ def getName(self): pass def getOwnerID(self): - """Returns the score game owner.""" + """Returns the score owner.""" pass def getPoints(self): @@ -5162,12 +5240,12 @@ def remove(self): """Removes this score from the server""" pass - def setPoints(self): + def setPoints(self,numPoints, key): """Sets the number of points in the score - Don't use to add/remove points, use only to reset values!""" +Don't use to add/remove points, use only to reset values!""" pass - def transferPoints(self, dest, points=0, key=None): + def transferPoints(self,dest, points=0, key=None): """Transfers points from this score to another""" pass @@ -5279,21 +5357,25 @@ def saveAsJPEG(self,filename,quality=75): """Saves this image to disk as a JPEG file""" pass + def saveAsPNG(self,filename): + """Saves this image to disk as a PNG file""" + pass + class ptImageLibMod: - """Plasma ImageLibraryModifier class""" - def __init__(self,imlkey): + """Plasma image library modifier class""" + def __init__(self,ilmKey): """None""" pass - def getImage(imageName): - """Returns the named image, if present""" + def getImage(self,name): + """Returns the ptImage with the specified name""" pass - def getImages(): - """Returns a tuple of ptImages""" + def getImages(self): + """Returns a tuple of the library's ptImages""" pass - def getNames(): + def getNames(self): """Returns a tuple of the image names""" pass @@ -5355,7 +5437,7 @@ def __init__(self): """None""" pass - def bindKey(self, key1, key2, action): + def bindKey(self,key1,key2,action): """Bind keys to an action""" pass @@ -5375,7 +5457,7 @@ def convertCharToVKey(self,charString): """Convert char string to virtual key""" pass - def convertControlCodeToString(self, controlCode): + def convertControlCodeToString(self,controlCode): """Convert control code to character string""" pass @@ -5383,11 +5465,11 @@ def convertVKeyToChar(self,virtualKey,flags): """Convert virtual key and shift flags to string""" pass - def getBindingFlags1(self, controlCode): + def getBindingFlags1(self,controlCode): """Returns modifier flags for controlCode""" pass - def getBindingFlags2(self, controlCode): + def getBindingFlags2(self,controlCode): """Returns modifier flags for controlCode""" pass @@ -5395,11 +5477,11 @@ def getBindingFlagsConsole(self,command): """Returns modifier flags for the console command mapping""" pass - def getBindingKey1(self, controlCode): + def getBindingKey1(self,controlCode): """Returns key code for controlCode""" pass - def getBindingKey2(self, controlCode): + def getBindingKey2(self,controlCode): """Returns key code for controlCode""" pass @@ -5412,18 +5494,18 @@ def writeKeyMap(self): pass class ptLayer: - """Plasma Layer class""" + """Plasma layer class""" def __init__(self,layerKey): """None""" pass def getTexture(self) -> ptImage: """Returns the image texture of the layer""" - ... + pass - def setTexture(self, image: ptImage) -> None: - """Sets the texture of the layer""" - ... + def setTexture(self,image: ptImage) -> None: + """Sets the ptImage texture of the layer""" + pass class ptMarkerMgr: """Marker manager accessor class""" @@ -5645,8 +5727,8 @@ def linkPlayerToAge(self,ageLink,pid): """Link player(pid) to ageLink""" pass - def linkToAge(self,ageLink): - """Links to ageLink (ptAgeLinkStruct)""" + def linkToAge(self,ageLink, linkAnim): + """Links to ageLink (ptAgeLinkStruct, string)""" pass def linkToMyNeighborhoodAge(self): @@ -5718,7 +5800,7 @@ def addVarFloat(self,name,number): pass def addVarInt(self,name,number): - """Add a integer variable event record to the Notify message + """Add a int variable event record to the Notify message This event record is used to pass a number variable to another python program""" pass @@ -5727,7 +5809,7 @@ def addVarKey(self,name,key): This event record is used to pass a ptKey variable to another python program""" pass - def addVarNull(self,name): + def addVarNull(self,name,number): """Add a null (no data) variable event record to the Notify message This event record is used to pass a number variable to another python program""" pass @@ -5887,6 +5969,14 @@ def rotate(self,radians,axis): """Rotates the attached sceneobject the specified radians around the specified axis""" pass + def setAngularVelocity(self,velocityVector): + """Sets the objects AngularVelocity to the specified vector""" + pass + + def setLinearVelocity(self,velocityVector): + """Sets the objects LinearVelocity to the specified vector""" + pass + def shiftMass(self,offsetVector): """Shifts the attached sceneobject's center to mass in the specified direction and distance""" pass @@ -6046,6 +6136,15 @@ def setFromDefaults(self,timeStampNow): class ptSceneobject: """Plasma Sceneobject class""" + + avatar: Any + + draw: Any + + particle: Any + + physics: Any + def __init__(self,objKey, selfKey): """None""" pass @@ -6206,11 +6305,10 @@ def volumeSensorIgnoreExtraEnters(self,ignore): """Tells the volume sensor attached to this object to ignore extra enters (default), or not (hack for garrison).""" pass - def volumeSensorNoArbitration(self, noArbitration): + def volumeSensorNoArbitration(self,noArbitration): """Tells the volume sensor attached to this object whether or not to negotiate exclusive locks with the server.""" pass - class ptSimpleStateVariable: """Basic SDL state data record class""" def __init__(self): @@ -6245,6 +6343,10 @@ def getInt(self,idx=0): """Returns an int variable's value""" pass + def getKey(self,idx=0): + """Returns a plKey variable's value""" + pass + def getShort(self,idx=0): """Returns a short variable's value""" pass @@ -6411,6 +6513,22 @@ def writelines(self,lines): class ptSwimCurrentInterface: """Creates a new ptSwimCurrentInterface""" + + farDistance: Any + """UNKNOWN""" + + farVelocity: Any + """UNKNOWN""" + + nearDistance: Any + """UNKNOWN""" + + nearVelocity: Any + """UNKNOWN""" + + rotation: Any + """UNKNOWN""" + def __init__(self,key): """None""" pass @@ -6601,10 +6719,14 @@ def addNode(self,node,cb=None,cbContext=0): """Adds 'node'(ptVaultNode) as a child to this node.""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getChildNodeCount(self): """Returns how many children this node has.""" pass @@ -6784,10 +6906,14 @@ def addNode(self,node,cb=None,cbContext=0): """Adds 'node'(ptVaultNode) as a child to this node.""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getChildNodeCount(self): """Returns how many children this node has.""" pass @@ -6987,10 +7113,14 @@ def addNode(self,node,cb=None,cbContext=0): """Adds 'node'(ptVaultNode) as a child to this node.""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getChildNodeCount(self): """Returns how many children this node has.""" pass @@ -7198,10 +7328,14 @@ def asAgeInfoStruct(self): """Returns this ptVaultAgeInfoNode as a ptAgeInfoStruct""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getAgeDescription(self): """Returns the description of the age""" pass @@ -7489,10 +7623,14 @@ def asAgeLinkStruct(self): """Returns this ptVaultAgeLinkNode as a ptAgeLinkStruct""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getAgeInfo(self): """Returns the ageInfo as a ptAgeInfoStruct""" pass @@ -7704,10 +7842,14 @@ def addNode(self,node,cb=None,cbContext=0): """Adds 'node'(ptVaultNode) as a child to this node.""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getChildNodeCount(self): """Returns how many children this node has.""" pass @@ -7911,10 +8053,14 @@ def addNode(self,node,cb=None,cbContext=0): """Adds 'node'(ptVaultNode) as a child to this node.""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getChildNodeCount(self): """Returns how many children this node has.""" pass @@ -8118,10 +8264,14 @@ def addNode(self,node,cb=None,cbContext=0): """Adds 'node'(ptVaultNode) as a child to this node.""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getChildNodeCount(self): """Returns how many children this node has.""" pass @@ -8174,6 +8324,10 @@ def getID(self): """Returns the unique ID of this ptVaultNode.""" pass + def getMarkers(self): + """Returns a tuple of markers associated with this game""" + pass + def getModifyTime(self): """Returns the modified time of this node, that is useable by python's time library.""" pass @@ -8251,6 +8405,10 @@ def setID(self,id): """Sets ID of this ptVaultNode.""" pass + def setMarkers(self,markers): + """Sets markers associated with this game""" + pass + def setOwnerNodeID(self,id): """Set node ID of the owner of this node""" pass @@ -8367,10 +8525,14 @@ def addPlayer(self,playerID): """Adds playerID player to this player info list node.""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getChildNodeCount(self): """Returns how many children this node has.""" pass @@ -8582,10 +8744,14 @@ def addNode(self,node,cb=None,cbContext=0): """Adds 'node'(ptVaultNode) as a child to this node.""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getChildNodeCount(self): """Returns how many children this node has.""" pass @@ -8809,10 +8975,14 @@ def addNode(self,node,cb=None,cbContext=0): """Adds 'node'(ptVaultNode) as a child to this node.""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getChildNodeCount(self): """Returns how many children this node has.""" pass @@ -9012,10 +9182,14 @@ def addNode(self,node,cb=None,cbContext=0): """Adds 'node'(ptVaultNode) as a child to this node.""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getChildNodeCount(self): """Returns how many children this node has.""" pass @@ -9195,10 +9369,14 @@ def addNode(self,node,cb=None,cbContext=0): """Adds 'node'(ptVaultNode) as a child to this node.""" pass - def findNode(self, templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: + def findNode(self,templateNode: ptVaultNode, /, maxDepth: int = 1) -> Optional[ptVaultNode]: """Returns ptVaultNode if child node found matching template, or None""" pass + def forceSave(self): + """Force the current node to save immediately""" + pass + def getChildNodeCount(self): """Returns how many children this node has.""" pass @@ -9279,6 +9457,11 @@ def getTitle(self): """Returns the title of this text note node.""" pass + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + def hasNode(self,id): """Returns true if node if a child node""" pass @@ -9347,6 +9530,10 @@ def setTitle(self,title): """Sets the title of this text note node.""" pass + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + def upcastToAgeInfoListNode(self): """Returns this ptVaultNode as ptVaultAgeInfoListNode""" pass @@ -9400,7 +9587,7 @@ def upcastToTextNoteNode(self): pass class ptVector3: - """Plasma Point class""" + """Plasma 3D Vector class""" def __init__(self,x=0, y=0, z=0): """None""" pass @@ -9472,11 +9659,11 @@ def zero(self): class ptWaveSet: """Creates a new ptWaveSet""" - def __init__(self,ey): + def __init__(self,key): """None""" pass - def addBuoy(self, soKey: ptKey) -> None: + def addBuoy(self,soKey: ptKey) -> None: """Adds the specified object as a buoy""" pass @@ -9608,7 +9795,7 @@ def getWindDir(self): """Returns the attribute's value""" pass - def removeBuoy(self, soKey: ptKey) -> None: + def removeBuoy(self,soKey: ptKey) -> None: """Removes the specified object as a buoy""" pass diff --git a/Scripts/Python/plasma/PlasmaConstants.py b/Scripts/Python/plasma/PlasmaConstants.py index 5ec680a227..aedead6e09 100644 --- a/Scripts/Python/plasma/PlasmaConstants.py +++ b/Scripts/Python/plasma/PlasmaConstants.py @@ -40,6 +40,18 @@ Mead, WA 99021 *==LICENSE==* """ + +# NOTE: This stub file was generated automatically from Plasma's Python interface. +# Do not edit this file manually. +# To change any of the docstrings or function signatures, +# edit the corresponding C++ glue code in pfPython. +# If the Python interface has changed, regenerate these stubs +# by running the following call in the in-game Python console: +# >>> __import__("generate_stubs").run() + +from __future__ import annotations +from typing import * + class Enum: """Enum base class""" def __init__(self): @@ -104,9 +116,10 @@ class PtBrainModes: kGeneric = 0 kLadder = 1 kSit = 2 - kEmote = 3 - kAFK = 4 - kNonGeneric = 5 + kSitOnGround = 3 + kEmote = 4 + kAFK = 5 + kNonGeneric = 6 class PtButtonNotifyTypes: """(none)""" @@ -125,6 +138,7 @@ class PtCCRPetitionType: kTechnical = 6 class PtConfirmationResult: + """(none)""" Cancel = 0 No = 0 OK = 1 @@ -133,6 +147,7 @@ class PtConfirmationResult: Logout = 62 class PtConfirmationType: + """(none)""" OK = 0 ConfirmQuit = 1 ForceQuit = 2 @@ -296,4 +311,3 @@ class PtStatusLogFlags: kStdout = 128 kTimeInSeconds = 256 kTimeAsDouble = 512 - diff --git a/Scripts/Python/plasma/PlasmaGame.py b/Scripts/Python/plasma/PlasmaGame.py index ba10c2c6a1..3d5d98e159 100644 --- a/Scripts/Python/plasma/PlasmaGame.py +++ b/Scripts/Python/plasma/PlasmaGame.py @@ -41,79 +41,150 @@ *==LICENSE==* """ +# NOTE: This stub file was generated automatically from Plasma's Python interface. +# Do not edit this file manually. +# To change any of the docstrings or function signatures, +# edit the corresponding C++ glue code in pfPython. +# If the Python interface has changed, regenerate these stubs +# by running the following call in the in-game Python console: +# >>> __import__("generate_stubs").run() + from __future__ import annotations from typing import * class ptGameCli: """Abstract bass class for legacy game clients.""" - gameID: int = ... + gameID: int + """The ID of the game instance on the server.""" + + handler: Any + """The game event handler.""" + + isLocallyOwned: bool + """Whether or not we are the owner of this game instance.""" + + ownerID: int + """The ID of the player who owns this game instance.""" + + def __init__(self): + """None""" + pass + + def leaveGame(self) -> None: + """Explicitly ask the server to allow us to leave the game.""" + pass + +class ptGmBlueSpiral(ptGameCli): + """Legacy blue spiral game client.""" + + gameID: int """The ID of the game instance on the server.""" - handler: Any = ... + handler: Any """The game event handler.""" - isLocallyOwned: bool = ... + isLocallyOwned: bool """Whether or not we are the owner of this game instance.""" - ownerID: int = ... + ownerID: int """The ID of the player who owns this game instance.""" + def __init__(self): + """None""" + pass + + def hitCloth(self): + """Request for the server to hit a specific cloth index and validate the correct sequence of cloth inputs.""" + pass + + @staticmethod + def isSupported(): + """Checks for the presence of a server-side blue spiral game manager.""" + pass + + @staticmethod + def join(): + """Join a common blue spiral game in the current Age.""" + pass + def leaveGame(self) -> None: """Explicitly ask the server to allow us to leave the game.""" - ... + pass + def startGame(self): + """Request for the server to start the game timer.""" + pass class ptGmMarker(ptGameCli): """Legacy marker game client.""" - def addMarker(self, x: float, y: float, z: float, name: str, age: str) -> None: + gameID: int + """The ID of the game instance on the server.""" + + handler: Any + """The game event handler.""" + + isLocallyOwned: bool + """Whether or not we are the owner of this game instance.""" + + ownerID: int + """The ID of the player who owns this game instance.""" + + def __init__(self): + """None""" + pass + + def addMarker(self,x: float, y: float, z: float, name: str, age: str) -> None: """Request for the server to add a new marker to the game.""" - ... + pass - def captureMarker(self, markerId: int) -> None: + def captureMarker(self,markerId: int) -> None: """Request for the server to register a capture of the specified marker for our team.""" - ... + pass - def changeGameName(self, name: str) -> None: + def changeGameName(self,name: str) -> None: """Request for the server to change the internal marker game name.""" - ... + pass - def changeMarkerName(self, markerID: int) -> None: + def changeMarkerName(self,markerID: int) -> None: """Request for the server to change the name of a specific marker from the game.""" - ... + pass - def changeTimeLimit(self, timeLimit: int) -> None: + def changeTimeLimit(self,timeLimit: int) -> None: """Request for the server to change the marker game's time limit.""" - ... + pass @staticmethod def create(handler: Any, gameType: int, templateId: Optional[str]) -> None: """Initialize a new marker game client with the server.""" - ... + pass def deleteGame(self) -> None: - """Request for the server to delete all data associated with this game, including - the marker definitions and game name. - """ - ... + """Request for the server to delete all data associated with this game, including the marker definitions and game name.""" + pass - def deleteMarker(self, markerID: int) -> None: + def deleteMarker(self,markerID: int) -> None: """Request for the server to delete a specific marker from the game.""" - ... + pass @staticmethod def isSupported() -> bool: - ... + """Checks for the presence of a server-side marker game manager.""" + pass + + def leaveGame(self) -> None: + """Explicitly ask the server to allow us to leave the game.""" + pass def pauseGame(self) -> None: """Request for the server to pause the marker game.""" - ... + pass def resetGame(self) -> None: """Request for the server to clear all markers to the uncaptured state.""" - ... + pass def startGame(self) -> None: """Request for the server to start the marker game.""" - ... + pass diff --git a/Scripts/Python/plasma/PlasmaGameConstants.py b/Scripts/Python/plasma/PlasmaGameConstants.py index 3a838b68ab..8532e4ca64 100644 --- a/Scripts/Python/plasma/PlasmaGameConstants.py +++ b/Scripts/Python/plasma/PlasmaGameConstants.py @@ -41,10 +41,19 @@ *==LICENSE==* """ +# NOTE: This stub file was generated automatically from Plasma's Python interface. +# Do not edit this file manually. +# To change any of the docstrings or function signatures, +# edit the corresponding C++ glue code in pfPython. +# If the Python interface has changed, regenerate these stubs +# by running the following call in the in-game Python console: +# >>> __import__("generate_stubs").run() + from __future__ import annotations +from typing import * class PtGameJoinError: - kGameJoinPending = -1 + """(none)""" kGameJoinSuccess = 0 kGameJoinErrNotExist = 1 kGameJoinErrInitFailed = 2 @@ -54,11 +63,11 @@ class PtGameJoinError: kGameJoinErrAlreadyJoined = 6 kGameJoinErrNoInvite = 7 kNumGameJoinErrors = 8 - + kGameJoinPending = 4294967295 class PtMarkerGameType: + """(none)""" kMarkerGameQuest = 0 kMarkerGameCGZ = 1 kMarkerGameCapture = 2 kMarkerGameCaptureAndHold = 3 - kNumMarkerGameTypes = 4 diff --git a/Scripts/Python/plasma/PlasmaNetConstants.py b/Scripts/Python/plasma/PlasmaNetConstants.py index d969c770cc..edba00a16b 100644 --- a/Scripts/Python/plasma/PlasmaNetConstants.py +++ b/Scripts/Python/plasma/PlasmaNetConstants.py @@ -40,6 +40,18 @@ Mead, WA 99021 *==LICENSE==* """ + +# NOTE: This stub file was generated automatically from Plasma's Python interface. +# Do not edit this file manually. +# To change any of the docstrings or function signatures, +# edit the corresponding C++ glue code in pfPython. +# If the Python interface has changed, regenerate these stubs +# by running the following call in the in-game Python console: +# >>> __import__("generate_stubs").run() + +from __future__ import annotations +from typing import * + class PtLinkingRules: """(none)""" kBasicLink = 0 @@ -48,4 +60,3 @@ class PtLinkingRules: kOwnedBook = 3 kVisitBook = 4 kChildAgeBook = 5 - diff --git a/Scripts/Python/plasma/PlasmaVaultConstants.py b/Scripts/Python/plasma/PlasmaVaultConstants.py index 2df2b993d5..5a65819ab8 100644 --- a/Scripts/Python/plasma/PlasmaVaultConstants.py +++ b/Scripts/Python/plasma/PlasmaVaultConstants.py @@ -40,6 +40,18 @@ Mead, WA 99021 *==LICENSE==* """ + +# NOTE: This stub file was generated automatically from Plasma's Python interface. +# Do not edit this file manually. +# To change any of the docstrings or function signatures, +# edit the corresponding C++ glue code in pfPython. +# If the Python interface has changed, regenerate these stubs +# by running the following call in the in-game Python console: +# >>> __import__("generate_stubs").run() + +from __future__ import annotations +from typing import * + class PtVaultCallbackTypes: """(none)""" kVaultConnected = 1 @@ -112,4 +124,3 @@ class PtVaultTextNoteTypes: """(none)""" kGeneric = 0 kCCRPetition = 1 - From 7e9ba51b98042d03db3e5f27e5fbd1f9a582a06d Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sun, 2 Feb 2025 16:20:07 +0100 Subject: [PATCH 18/18] Add default values for keyword arguments to PtDebugPrint docstrings Fixes CI errors because of the stub incorrectly indicating that these keyword arguments are required. --- Scripts/Python/plasma/Plasma.py | 2 +- Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Scripts/Python/plasma/Plasma.py b/Scripts/Python/plasma/Plasma.py index 07da486d36..d8c6f5f625 100644 --- a/Scripts/Python/plasma/Plasma.py +++ b/Scripts/Python/plasma/Plasma.py @@ -180,7 +180,7 @@ def PtDebugAssert(cond, msg): """Debug only: Assert if condition is false.""" pass -def PtDebugPrint(*msgs, level, sep, end): +def PtDebugPrint(*msgs, level=3, sep=" ", end="\n"): """Prints msgs to the Python log given the message's level, optionally separated and terminated by the given strings""" pass diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp index c449e2a8c9..508439f6cd 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp @@ -380,7 +380,7 @@ PYTHON_GLOBAL_METHOD_DEFINITION(PtDebugAssert, args, "Params: cond, msg\nDebug o PYTHON_RETURN_NONE; } -PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtDebugPrint, args, kwargs, "Params: *msgs, level, sep, end\n" +PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtDebugPrint, args, kwargs, "Params: *msgs, level=3, sep=\" \", end=\"\\n\"\n" "Prints msgs to the Python log given the message's level, " "optionally separated and terminated by the given strings") {