From cd494bf29d6aab249064ffd826e5114a5adff78a Mon Sep 17 00:00:00 2001 From: Anton Kuzin Date: Fri, 17 Oct 2025 16:13:36 +0300 Subject: [PATCH 01/10] Just removed the checks --- kOS.props | 3 +++ src/kOS/Suffixed/PartModuleField/PartModuleFields.cs | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/kOS.props b/kOS.props index c14a0194e..7be60e4f5 100644 --- a/kOS.props +++ b/kOS.props @@ -5,4 +5,7 @@ 1.5.1.0 + + E:\Games\Kerbal Space Program RSS + \ No newline at end of file diff --git a/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs b/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs index 9ec14cdf6..83ca00d80 100644 --- a/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs +++ b/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs @@ -225,7 +225,7 @@ protected virtual ListValue AllFieldNames() /// true if it is on the PartModule, false if it is not public virtual BooleanValue HasField(StringValue fieldName) { - return FieldIsVisible(GetField(fieldName)); + return GetField(fieldName) != null; } /// @@ -443,8 +443,6 @@ protected Structure GetKSPFieldValue(StringValue suffixName) BaseField field = GetField(suffixName); if (field == null) throw new KOSLookupFailException("FIELD", suffixName, this); - if (!FieldIsVisible(field)) - throw new KOSLookupFailException("FIELD", suffixName, this, true); Structure obj = FromPrimitiveWithAssert(field.GetValue(partModule)); return obj; } From 8cbc2bdf6b413acbe6a915ece94f4a1390d4e55f Mon Sep 17 00:00:00 2001 From: Anton Kuzin Date: Mon, 20 Oct 2025 14:36:17 +0300 Subject: [PATCH 02/10] Refactor it by creating a 3 new suffixes. --- .../RemoteTechAntennaModuleFields.cs | 4 +- .../PartModuleField/PartModuleFields.cs | 53 +++++++++++++++---- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/kOS/AddOns/RemoteTech/RemoteTechAntennaModuleFields.cs b/src/kOS/AddOns/RemoteTech/RemoteTechAntennaModuleFields.cs index ff9042fec..e228cbd23 100644 --- a/src/kOS/AddOns/RemoteTech/RemoteTechAntennaModuleFields.cs +++ b/src/kOS/AddOns/RemoteTech/RemoteTechAntennaModuleFields.cs @@ -1,4 +1,4 @@ -using kOS.Safe.Encapsulation; +using kOS.Safe.Encapsulation; using kOS.Safe.Encapsulation.Suffixes; using kOS.Safe.Exceptions; using kOS.Suffixed; @@ -113,7 +113,7 @@ protected override ListValue AllFieldNames() // just print the guid if we can't figure out what it is return new StringValue(guid.ToString()); } - return base.GetKSPFieldValue(suffixName); + return base.GetKSPFieldValue(suffixName, base.FieldIsVisible); } private Guid GetTargetGuid(Structure target) diff --git a/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs b/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs index 83ca00d80..0b69377c9 100644 --- a/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs +++ b/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs @@ -199,6 +199,27 @@ protected virtual ListValue AllFields(string formatter) return returnValue; } + /// + /// Return a list of all the strings of all KSPfields registered to this PartModule + /// which are currently NOT showing on the part's RMB menu. + /// + /// List of all the strings field names. + protected ListValue AllHiddenFields(string formatter) + { + var returnValue = new ListValue(); + + IEnumerable hiddenFields = partModule.Fields.Cast().Where((field) => !FieldIsVisible(field)); + + foreach (BaseField field in hiddenFields) + { + returnValue.Add(new StringValue(string.Format(formatter, + "get-only", + GetFieldName(field).ToLower(), + Utilities.Utils.KOSType(field.FieldInfo.FieldType)))); + } + return returnValue; + } + /// /// Return a list of all the strings of all KSPfields registered to this PartModule /// which are currently showing on the part's RMB menu, without formating. @@ -225,7 +246,18 @@ protected virtual ListValue AllFieldNames() /// true if it is on the PartModule, false if it is not public virtual BooleanValue HasField(StringValue fieldName) { - return GetField(fieldName) != null; + return FieldIsVisible(GetField(fieldName, FieldIsVisible)); + } + + /// + /// Determine if the Partmodule has this KSPField on it, which is currently + /// NOT showing on the part's RMB menu: + /// + /// The field to search for + /// true if it is on the PartModule, false if it is not + public virtual BooleanValue HasHiddenField(StringValue fieldName) + { + return GetField(fieldName, field => !FieldIsVisible(field)) != null; } /// @@ -233,7 +265,7 @@ public virtual BooleanValue HasField(StringValue fieldName) /// /// The case-insensitive guiName (or name if guiname is empty) of the field. /// a BaseField - a KSP type that can be used to get the value, or its GUI name or its reflection info. - protected BaseField GetField(string cookedGuiName) + protected BaseField GetField(string cookedGuiName, Func visibilityFilterPredicate) { // Conceptually this should be a single hit using FirstOrDefault(), because there should only // be one Field with the given GUI name. But Issue #2666 forced kOS to change it to an array of hits @@ -251,7 +283,7 @@ protected BaseField GetField(string cookedGuiName) // Issue #2666 is handled here. kOS should not return the invisible field when there's // a visible one of the same name it could have picked instead. Only return an invisible // field if there's no visible one to pick. - BaseField preferredMatch = allMatches.FirstOrDefault(field => FieldIsVisible(field)); + BaseField preferredMatch = allMatches.FirstOrDefault(visibilityFilterPredicate); return preferredMatch ?? allMatches.First(); } @@ -413,18 +445,21 @@ private void InitializeSuffixesAfterConstruction() AddSuffix("ALLACTIONS", new Suffix(() => AllActions("({0}) {1}, is {2}"))); AddSuffix("ALLACTIONNAMES", new Suffix(AllActionNames)); AddSuffix("HASACTION", new OneArgsSuffix(HasAction)); - AddSuffix("GETFIELD", new OneArgsSuffix(GetKSPFieldValue)); + AddSuffix("GETFIELD", new OneArgsSuffix(argument => GetKSPFieldValue(argument, field => FieldIsVisible(field)))); AddSuffix("SETFIELD", new TwoArgsSuffix(SetKSPFieldValue)); AddSuffix("DOEVENT", new OneArgsSuffix(CallKSPEvent)); AddSuffix("DOACTION", new TwoArgsSuffix(CallKSPAction)); + AddSuffix("ALLHIDDENFIELDS", new Suffix(() => AllHiddenFields("({0}) {1}, is {2}"))); + AddSuffix("HASHIDDENFIELD", new OneArgsSuffix(HasHiddenField)); + AddSuffix("GETHIDDENFIELD", new OneArgsSuffix(argument => GetKSPFieldValue(argument, field => !FieldIsVisible(field)))); } - private static bool FieldIsVisible(BaseField field) + protected bool FieldIsVisible(BaseField field) { return (field != null) && (HighLogic.LoadedSceneIsEditor ? field.guiActiveEditor : field.guiActive); } - private static bool EventIsVisible(BaseEvent evt) + private bool EventIsVisible(BaseEvent evt) { return (evt != null) && ( (HighLogic.LoadedSceneIsEditor ? evt.guiActiveEditor : evt.guiActive) && @@ -438,9 +473,9 @@ private static bool EventIsVisible(BaseEvent evt) /// /// /// - protected Structure GetKSPFieldValue(StringValue suffixName) + protected Structure GetKSPFieldValue(StringValue suffixName, Func visibilityFilterPredicate) { - BaseField field = GetField(suffixName); + BaseField field = GetField(suffixName, visibilityFilterPredicate); if (field == null) throw new KOSLookupFailException("FIELD", suffixName, this); Structure obj = FromPrimitiveWithAssert(field.GetValue(partModule)); @@ -455,7 +490,7 @@ protected Structure GetKSPFieldValue(StringValue suffixName) protected virtual void SetKSPFieldValue(StringValue suffixName, Structure newValue) { ThrowIfNotCPUVessel(); - BaseField field = GetField(suffixName); + BaseField field = GetField(suffixName, FieldIsVisible); if (field == null) throw new KOSLookupFailException("FIELD", suffixName, this); if (!FieldIsVisible(field)) From b40b60d9255e6ff8bf732d0d5ee93e834069deef Mon Sep 17 00:00:00 2001 From: Anton Kuzin Date: Mon, 20 Oct 2025 19:53:41 +0300 Subject: [PATCH 03/10] Revert changes to the solution properties file --- kOS.props | 3 --- 1 file changed, 3 deletions(-) diff --git a/kOS.props b/kOS.props index 7be60e4f5..c14a0194e 100644 --- a/kOS.props +++ b/kOS.props @@ -5,7 +5,4 @@ 1.5.1.0 - - E:\Games\Kerbal Space Program RSS - \ No newline at end of file From 5d7eb18cbfdbf5fc36a605ec1ef915989b93b7a9 Mon Sep 17 00:00:00 2001 From: Anton Kuzin Date: Mon, 20 Oct 2025 20:01:01 +0300 Subject: [PATCH 04/10] No need to make HasHiddenField virtual --- src/kOS/Suffixed/PartModuleField/PartModuleFields.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs b/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs index 0b69377c9..9a6ed3eac 100644 --- a/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs +++ b/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs @@ -255,7 +255,7 @@ public virtual BooleanValue HasField(StringValue fieldName) /// /// The field to search for /// true if it is on the PartModule, false if it is not - public virtual BooleanValue HasHiddenField(StringValue fieldName) + public BooleanValue HasHiddenField(StringValue fieldName) { return GetField(fieldName, field => !FieldIsVisible(field)) != null; } From 7880fcef0b94edf114e50b2db9d7368ddec38af1 Mon Sep 17 00:00:00 2001 From: Anton Kuzin Date: Tue, 21 Oct 2025 14:41:19 +0300 Subject: [PATCH 05/10] Added the GEtAllHiddenFieldNames suffix. Refactored some inherited methods using function overload to avoid unnecessary changes in the child classes --- .../RemoteTechAntennaModuleFields.cs | 2 +- .../PartModuleField/PartModuleFields.cs | 28 +++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/kOS/AddOns/RemoteTech/RemoteTechAntennaModuleFields.cs b/src/kOS/AddOns/RemoteTech/RemoteTechAntennaModuleFields.cs index e228cbd23..8266420a5 100644 --- a/src/kOS/AddOns/RemoteTech/RemoteTechAntennaModuleFields.cs +++ b/src/kOS/AddOns/RemoteTech/RemoteTechAntennaModuleFields.cs @@ -113,7 +113,7 @@ protected override ListValue AllFieldNames() // just print the guid if we can't figure out what it is return new StringValue(guid.ToString()); } - return base.GetKSPFieldValue(suffixName, base.FieldIsVisible); + return base.GetKSPFieldValue(suffixName); } private Guid GetTargetGuid(Structure target) diff --git a/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs b/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs index 9a6ed3eac..2f3c6ce63 100644 --- a/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs +++ b/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs @@ -226,10 +226,15 @@ protected ListValue AllHiddenFields(string formatter) /// /// List of all the strings field names. protected virtual ListValue AllFieldNames() + { + return AllFieldNames(FieldIsVisible); + } + + private ListValue AllFieldNames(Func visibilityFilterPredicate) { var returnValue = new ListValue(); - IEnumerable visibleFields = partModule.Fields.Cast().Where(FieldIsVisible); + IEnumerable visibleFields = partModule.Fields.Cast().Where(visibilityFilterPredicate); foreach (BaseField field in visibleFields) { @@ -246,7 +251,7 @@ protected virtual ListValue AllFieldNames() /// true if it is on the PartModule, false if it is not public virtual BooleanValue HasField(StringValue fieldName) { - return FieldIsVisible(GetField(fieldName, FieldIsVisible)); + return FieldIsVisible(GetField(fieldName)); } /// @@ -265,7 +270,12 @@ public BooleanValue HasHiddenField(StringValue fieldName) /// /// The case-insensitive guiName (or name if guiname is empty) of the field. /// a BaseField - a KSP type that can be used to get the value, or its GUI name or its reflection info. - protected BaseField GetField(string cookedGuiName, Func visibilityFilterPredicate) + protected BaseField GetField(string cookedGuiName) + { + return GetField(cookedGuiName, FieldIsVisible); + } + + private BaseField GetField(string cookedGuiName, Func visibilityFilterPredicate) { // Conceptually this should be a single hit using FirstOrDefault(), because there should only // be one Field with the given GUI name. But Issue #2666 forced kOS to change it to an array of hits @@ -445,11 +455,12 @@ private void InitializeSuffixesAfterConstruction() AddSuffix("ALLACTIONS", new Suffix(() => AllActions("({0}) {1}, is {2}"))); AddSuffix("ALLACTIONNAMES", new Suffix(AllActionNames)); AddSuffix("HASACTION", new OneArgsSuffix(HasAction)); - AddSuffix("GETFIELD", new OneArgsSuffix(argument => GetKSPFieldValue(argument, field => FieldIsVisible(field)))); + AddSuffix("GETFIELD", new OneArgsSuffix(GetKSPFieldValue)); AddSuffix("SETFIELD", new TwoArgsSuffix(SetKSPFieldValue)); AddSuffix("DOEVENT", new OneArgsSuffix(CallKSPEvent)); AddSuffix("DOACTION", new TwoArgsSuffix(CallKSPAction)); AddSuffix("ALLHIDDENFIELDS", new Suffix(() => AllHiddenFields("({0}) {1}, is {2}"))); + AddSuffix("ALLHIDDENFIELDNAMES", new Suffix(() => AllFieldNames(field => !FieldIsVisible(field)))); AddSuffix("HASHIDDENFIELD", new OneArgsSuffix(HasHiddenField)); AddSuffix("GETHIDDENFIELD", new OneArgsSuffix(argument => GetKSPFieldValue(argument, field => !FieldIsVisible(field)))); } @@ -473,7 +484,12 @@ private bool EventIsVisible(BaseEvent evt) /// /// /// - protected Structure GetKSPFieldValue(StringValue suffixName, Func visibilityFilterPredicate) + protected Structure GetKSPFieldValue(StringValue suffixName) + { + return GetKSPFieldValue(suffixName, FieldIsVisible); + } + + private Structure GetKSPFieldValue(StringValue suffixName, Func visibilityFilterPredicate) { BaseField field = GetField(suffixName, visibilityFilterPredicate); if (field == null) @@ -490,7 +506,7 @@ protected Structure GetKSPFieldValue(StringValue suffixName, Func Date: Tue, 21 Oct 2025 15:06:56 +0300 Subject: [PATCH 06/10] Refactor FieldISVisible method to prevent nulls from sneaking into the list. --- .../PartModuleField/PartModuleFields.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs b/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs index 2f3c6ce63..57356f1ec 100644 --- a/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs +++ b/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs @@ -183,7 +183,7 @@ protected virtual ListValue AllFields(string formatter) { var returnValue = new ListValue(); - IEnumerable visibleFields = partModule.Fields.Cast().Where(FieldIsVisible); + IEnumerable visibleFields = partModule.Fields.Cast().Where(field => FieldIsVisible(field)); foreach (BaseField field in visibleFields) { @@ -227,7 +227,7 @@ protected ListValue AllHiddenFields(string formatter) /// List of all the strings field names. protected virtual ListValue AllFieldNames() { - return AllFieldNames(FieldIsVisible); + return AllFieldNames(field => FieldIsVisible(field)); } private ListValue AllFieldNames(Func visibilityFilterPredicate) @@ -262,7 +262,7 @@ public virtual BooleanValue HasField(StringValue fieldName) /// true if it is on the PartModule, false if it is not public BooleanValue HasHiddenField(StringValue fieldName) { - return GetField(fieldName, field => !FieldIsVisible(field)) != null; + return GetField(fieldName, field => FieldIsVisible(field, false)) != null; } /// @@ -272,7 +272,7 @@ public BooleanValue HasHiddenField(StringValue fieldName) /// a BaseField - a KSP type that can be used to get the value, or its GUI name or its reflection info. protected BaseField GetField(string cookedGuiName) { - return GetField(cookedGuiName, FieldIsVisible); + return GetField(cookedGuiName, field => FieldIsVisible(field)); } private BaseField GetField(string cookedGuiName, Func visibilityFilterPredicate) @@ -460,14 +460,14 @@ private void InitializeSuffixesAfterConstruction() AddSuffix("DOEVENT", new OneArgsSuffix(CallKSPEvent)); AddSuffix("DOACTION", new TwoArgsSuffix(CallKSPAction)); AddSuffix("ALLHIDDENFIELDS", new Suffix(() => AllHiddenFields("({0}) {1}, is {2}"))); - AddSuffix("ALLHIDDENFIELDNAMES", new Suffix(() => AllFieldNames(field => !FieldIsVisible(field)))); + AddSuffix("ALLHIDDENFIELDNAMES", new Suffix(() => AllFieldNames(field => FieldIsVisible(field, false)))); AddSuffix("HASHIDDENFIELD", new OneArgsSuffix(HasHiddenField)); - AddSuffix("GETHIDDENFIELD", new OneArgsSuffix(argument => GetKSPFieldValue(argument, field => !FieldIsVisible(field)))); + AddSuffix("GETHIDDENFIELD", new OneArgsSuffix(argument => GetKSPFieldValue(argument, field => FieldIsVisible(field, false)))); } - protected bool FieldIsVisible(BaseField field) + private bool FieldIsVisible(BaseField field, bool isVisible = true) { - return (field != null) && (HighLogic.LoadedSceneIsEditor ? field.guiActiveEditor : field.guiActive); + return (field != null) && (HighLogic.LoadedSceneIsEditor ? field.guiActiveEditor == isVisible : field.guiActive == isVisible); } private bool EventIsVisible(BaseEvent evt) @@ -486,7 +486,7 @@ private bool EventIsVisible(BaseEvent evt) /// protected Structure GetKSPFieldValue(StringValue suffixName) { - return GetKSPFieldValue(suffixName, FieldIsVisible); + return GetKSPFieldValue(suffixName, field => FieldIsVisible(field)); } private Structure GetKSPFieldValue(StringValue suffixName, Func visibilityFilterPredicate) From 8f8d0d44b418ceb810f32a70b298e74259b13f7c Mon Sep 17 00:00:00 2001 From: Anton Kuzin Date: Tue, 21 Oct 2025 15:10:56 +0300 Subject: [PATCH 07/10] Forgot to update this. --- src/kOS/Suffixed/PartModuleField/PartModuleFields.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs b/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs index 57356f1ec..f2dc78c69 100644 --- a/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs +++ b/src/kOS/Suffixed/PartModuleField/PartModuleFields.cs @@ -208,7 +208,7 @@ protected ListValue AllHiddenFields(string formatter) { var returnValue = new ListValue(); - IEnumerable hiddenFields = partModule.Fields.Cast().Where((field) => !FieldIsVisible(field)); + IEnumerable hiddenFields = partModule.Fields.Cast().Where(field => FieldIsVisible(field, false)); foreach (BaseField field in hiddenFields) { From 40bab4633563b109f90ed36fffd52dafcadb97f5 Mon Sep 17 00:00:00 2001 From: Anton Kuzin Date: Tue, 21 Oct 2025 16:24:09 +0300 Subject: [PATCH 08/10] Update the docs --- doc/source/structures/vessels/partmodule.rst | 45 +++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/doc/source/structures/vessels/partmodule.rst b/doc/source/structures/vessels/partmodule.rst index 162eb9910..86ad2d03f 100644 --- a/doc/source/structures/vessels/partmodule.rst +++ b/doc/source/structures/vessels/partmodule.rst @@ -29,9 +29,15 @@ Once you have a :struct:`PartModule`, you can use it to invoke the behaviors tha * - :attr:`ALLFIELDS` - :struct:`List` of strings - Accessible fields + * - :attr:`ALLHIDDENFIELDS` + - :struct:`List` of strings + - Fields not notmally accessible via UI * - :attr:`ALLFIELDNAMES` - :struct:`List` of strings - Accessible fields (name only) + * - :attr:`ALLHIDDENFIELDNAMES` + - :struct:`List` of strings + - Fields not notmally accessible via UI (name only) * - :attr:`ALLEVENTS` - :struct:`List` of strings - Triggerable events @@ -47,6 +53,9 @@ Once you have a :struct:`PartModule`, you can use it to invoke the behaviors tha * - :meth:`GETFIELD(name)` - - Get value of a field by name + * - :meth:`GETHIDDENFIELD(name)` + - + - Get value of a hidden field by name * - :meth:`SETFIELD(name,value)` - - Set value of a field by name @@ -59,6 +68,9 @@ Once you have a :struct:`PartModule`, you can use it to invoke the behaviors tha * - :meth:`HASFIELD(name)` - :struct:`Boolean` - Check if field exists + * - :meth:`HASHIDDENFIELD(name)` + - :struct:`Boolean` + - Check if a hidden field exists * - :meth:`HASEVENT(name)` - :struct:`Boolean` - Check if event exists @@ -90,12 +102,26 @@ Once you have a :struct:`PartModule`, you can use it to invoke the behaviors tha Get a list of all the names of KSPFields on this PartModule that the kos script is CURRENTLY allowed to get or set with :GETFIELD or :SETFIELD. Note the Security access comments below. This list can become obsolete as the game continues running depending on what the PartModule chooses to do. +.. attribute:: PartModule:ALLHIDDENFIELDS + + :access: Get only + :test: :struct:`List` of strings + + Get a list of all the names of KSPFields on this PartModule that are not normally displayed in the GUI. The returned field names should be used with :GETHIDDENFIELD and cannot be used with :SETFIELD. Note the Security access comments below. This list can become obsolete as the game continues running depending on what the PartModule chooses to do. + .. attribute:: PartModule:ALLFIELDNAMES :access: Get only :test: :struct:`List` of strings Similar to :ALLFIELDS except that it returns the string without the formatting to make it easier to use in a script. This list can become obsolete as the game continues running depending on what the PartModule chooses to do. + +.. attribute:: PartModule:ALLHIDDENFIELDNAMES + + :access: Get only + :test: :struct:`List` of strings + + Similar to :ALLHIDDENFIELDS except that it returns the string without the formatting to make it easier to use in a script. This list can become obsolete as the game continues running depending on what the PartModule chooses to do. .. attribute:: PartModule:ALLEVENTS @@ -132,6 +158,13 @@ Once you have a :struct:`PartModule`, you can use it to invoke the behaviors tha Get the value of one of the fields that this PartModule has placed onto the right-click menu for the part. Note the Security comments below. +.. method:: PartModule:GETHIDDENFIELD(name) + + :parameter name: (:struct:`String`) Name of the field + :return: varies + + Get the value of one of the hidden(internal) PartModule fields that aren't normally displayed in the right-click menu for the part. Note the Security comments below. + .. method:: PartModule:SETFIELD(name,value) :parameter name: (:struct:`String`) Name of the field @@ -177,6 +210,13 @@ Once you have a :struct:`PartModule`, you can use it to invoke the behaviors tha Return true if the given field name is currently available for use with :GETFIELD or :SETFIELD on this PartModule, false otherwise. +.. method:: PartModule:HASHIDDENFIELD(name) + + :parameter name: (:struct:`String`) Name of the field + :return: :struct:`Boolean` + + Return true if the given hidden(internal) field is present in the PartModule, false otherwise. + .. method:: PartModule:HASEVENT(name) :parameter name: (:struct:`String`) Name of the event @@ -197,19 +237,20 @@ Notes ----- In all the above cases where there is a name being passed in to :GETFIELD, :SETFIELD, :DOEVENT, or :DOACTION, the name is meant to be the name that is seen by you, the user, in the GUI screen, and NOT necessarily the actual name of the variable that the programmer of that PartModule chose to call the value behind the scenes. This is so that you can view the GUI right-click menu to see what to call things in your script. +When it comes to the hidden fields, they can be inspected using the :ALLHIDDENFIELDS method, or by looking into the source code. Generally, manipulating the hidden fields is recommended for experienced users. .. note:: **Security and Respecting other Mod Authors** - There are often a lot more fields and events and actions that a partmodule can do than are usable via kOS. In designing kOS, the kOS developers have deliberately chosen NOT to expose any "hidden" fields of a partmodule that are not normally shown to the user, without the express permission of a mod's author to do so. + It was decided that providing the kOS users with a read-only access to the part module's hidden fields would allow to create even better scripts and wouldn't do any harm. Obviously, manipulating the module's internal state would result in all kind of troubles, so this is forbidden. The access rules that kOS uses are as follows: KSPFields ~~~~~~~~~ -Is this a value that the user can normally see on the right-click context menu for a part? If so, then let kOS scripts GET the value. Is this a value that the user can normally manipulate via "tweakable" adjustments on the right-click context menu for a part, AND, is that tweakable a CURRENTLY enabled one? If so, then let KOS scripts SET the value, BUT they must set it to one of the values that the GUI would normally allow, according to the following rules. +Is this a value that the user can normally see on the right-click context menu for a part? If so, then let kOS scripts GET the value using the :GETFIELD method. Is this a value that represents the part's internal state and not normally displayed in the GUI? Then the :GETHIDDENFIELD method should be used. Is this a value that the user can normally manipulate via "tweakable" adjustments on the right-click context menu for a part, AND, is that tweakable a CURRENTLY enabled one? If so, then let KOS scripts SET the value, BUT they must set it to one of the values that the GUI would normally allow, according to the following rules. - If the KSPField is boolean: - The value must be true, false, or 0 or 1. From ce0b21637cec52f0fd9553cb19b5512803b52194 Mon Sep 17 00:00:00 2001 From: Anton Kuzin Date: Tue, 21 Oct 2025 16:27:27 +0300 Subject: [PATCH 09/10] Use GUI instead of UI everywhere --- doc/source/structures/vessels/partmodule.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/structures/vessels/partmodule.rst b/doc/source/structures/vessels/partmodule.rst index 86ad2d03f..3ec9cfcca 100644 --- a/doc/source/structures/vessels/partmodule.rst +++ b/doc/source/structures/vessels/partmodule.rst @@ -31,13 +31,13 @@ Once you have a :struct:`PartModule`, you can use it to invoke the behaviors tha - Accessible fields * - :attr:`ALLHIDDENFIELDS` - :struct:`List` of strings - - Fields not notmally accessible via UI + - Fields not notmally accessible via GUI * - :attr:`ALLFIELDNAMES` - :struct:`List` of strings - Accessible fields (name only) * - :attr:`ALLHIDDENFIELDNAMES` - :struct:`List` of strings - - Fields not notmally accessible via UI (name only) + - Fields not notmally accessible via GUI (name only) * - :attr:`ALLEVENTS` - :struct:`List` of strings - Triggerable events From 1ea7ebea5fd570a1aab0bd16e8cdab015b695b9c Mon Sep 17 00:00:00 2001 From: Anton Kuzin Date: Tue, 21 Oct 2025 20:37:58 +0300 Subject: [PATCH 10/10] Fix typos. --- doc/source/structures/vessels/partmodule.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/structures/vessels/partmodule.rst b/doc/source/structures/vessels/partmodule.rst index 3ec9cfcca..bf4abe489 100644 --- a/doc/source/structures/vessels/partmodule.rst +++ b/doc/source/structures/vessels/partmodule.rst @@ -31,13 +31,13 @@ Once you have a :struct:`PartModule`, you can use it to invoke the behaviors tha - Accessible fields * - :attr:`ALLHIDDENFIELDS` - :struct:`List` of strings - - Fields not notmally accessible via GUI + - Fields not normally accessible via GUI * - :attr:`ALLFIELDNAMES` - :struct:`List` of strings - Accessible fields (name only) * - :attr:`ALLHIDDENFIELDNAMES` - :struct:`List` of strings - - Fields not notmally accessible via GUI (name only) + - Fields not normally accessible via GUI (name only) * - :attr:`ALLEVENTS` - :struct:`List` of strings - Triggerable events @@ -237,7 +237,7 @@ Notes ----- In all the above cases where there is a name being passed in to :GETFIELD, :SETFIELD, :DOEVENT, or :DOACTION, the name is meant to be the name that is seen by you, the user, in the GUI screen, and NOT necessarily the actual name of the variable that the programmer of that PartModule chose to call the value behind the scenes. This is so that you can view the GUI right-click menu to see what to call things in your script. -When it comes to the hidden fields, they can be inspected using the :ALLHIDDENFIELDS method, or by looking into the source code. Generally, manipulating the hidden fields is recommended for experienced users. +When it comes to the hidden fields, they can be inspected using the :ALLHIDDENFIELDS method, or by looking into the source code. Generally, manipulating the hidden fields is recommended for experienced users only. .. note::