KSP-KOS / KOS

Fully programmable autopilot mod for KSP. Originally By Nivekk
Other
691 stars 229 forks source link

SetSuffix<> does not work with ListValue<> #2745

Closed PiezPiedPy closed 3 years ago

PiezPiedPy commented 4 years ago

Re #2263 While updating the addon code for Trajectories I used SetSuffix<> with ListValue<> as a parameter.

AddSuffix("DESCENTMODES", new SetSuffix<ListValue<BooleanValue>>(GetProfileModes, SetProfileModes, "Descent profile modes, true = AoA, false = Horizon, List(entry, high altitude, low altitude, final approach)."));

The getter works fine but the setter complains and throws an error stating that ListValue<> is not IConvertible

set addons:tr:descentmodes to List(true,false,true,false).
Object must implement IConvertible.

I have tried to make ListValue<> IConvertible compliant but failed miserably.

A fix is not necessary for me to continue the addon update as I have separated the SetSuffix<> commands into a Suffix<> getter and a OneArgsSuffix<> setter but I thought I would make an issue in case this can be fixed for future use.

Dunbaratu commented 4 years ago

Can you show me the error message with its stack trace from the Player.log so I know where within the kOS code the error gets thrown?

Also, one possible alternative is to use VarArgsSuffix, which lets you make a suffix method that takes varying number of arguments. You can see examples of using it in the code already. A good example to go from is StringValue:TOSCALAR, which has an optional second arg for supplying a default error value if the string doesn't form a valid number. The method that implements such a suffix takes a params Structure [] args argument and then you can examine the contents of the args array in the usual way in the body of the implementing method. This may be a way to make a suffix that can take some arbitrary amount of boolean parameters.

PiezPiedPy commented 4 years ago
kOS: At interpreter, line 1
set addons:tr:descentprofilemodes to list(true,false,true,true).
                                                          ^

UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
ModuleManager.UnityLogHandle.InterceptLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
kOS.KSPLogger:Log(Exception)
kOS.Safe.Execution.CPU:KOSFixedUpdate(Double)
kOS.Safe.UpdateHandler:UpdateFixedObservers(Double)
kOS.Module.kOSProcessor:UpdateFixedObservers()
kOS.Module.kOSProcessor:FixedUpdate()

(Filename: C:\buildslave\unity\build\Runtime/Export/Debug/Debug.bindings.h Line: 35)

System.InvalidCastException: Object must implement IConvertible.
  at System.Convert.ChangeType (System.Object value, System.Type conversionType, System.IFormatProvider provider) [0x00055] in <ad04dee02e7e4a85a1299c7ee81c79f6>:0 
  at System.Convert.ChangeType (System.Object value, System.Type conversionType) [0x0000c] in <ad04dee02e7e4a85a1299c7ee81c79f6>:0 
  at kOS.Safe.Encapsulation.Suffixes.SetSuffix`1[TValue].Set (System.Object value) [0x00021] in <3cbdfc30d71a45d3a4a8ee82583dd198>:0 
  at kOS.Safe.Encapsulation.Structure.ProcessSetSuffix (System.Collections.Generic.IDictionary`2[TKey,TValue] suffixes, System.String suffixName, System.Object value, System.Boolean failOkay) [0x0001f] in <3cbdfc30d71a45d3a4a8ee82583dd198>:0 
  at kOS.Safe.Encapsulation.Structure.SetSuffix (System.String suffixName, System.Object value, System.Boolean failOkay) [0x00026] in <3cbdfc30d71a45d3a4a8ee82583dd198>:0 
  at kOS.Safe.Compilation.OpcodeSetMember.Execute (kOS.Safe.Execution.ICpu cpu) [0x0003f] in <3cbdfc30d71a45d3a4a8ee82583dd198>:0 
  at kOS.Safe.Execution.CPU.ExecuteInstruction (kOS.Safe.Execution.ProgramContext context, System.Boolean doProfiling) [0x00227] in <3cbdfc30d71a45d3a4a8ee82583dd198>:0 
  at kOS.Safe.Execution.CPU.ContinueExecution (System.Boolean doProfiling) [0x00038] in <3cbdfc30d71a45d3a4a8ee82583dd198>:0 
  at kOS.Safe.Execution.CPU.KOSFixedUpdate (System.Double deltaTime) [0x000cf] in <3cbdfc30d71a45d3a4a8ee82583dd198>:0 
UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
ModuleManager.UnityLogHandle.InterceptLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
kOS.KSPLogger:Log(Exception)
kOS.Safe.Execution.CPU:KOSFixedUpdate(Double)
kOS.Safe.UpdateHandler:UpdateFixedObservers(Double)
kOS.Module.kOSProcessor:UpdateFixedObservers()
kOS.Module.kOSProcessor:FixedUpdate()

(Filename: C:\buildslave\unity\build\Runtime/Export/Debug/Debug.bindings.h Line: 35)

Code Fragment
File                 Line:Col IP   label   opcode operand
====                 ====:=== ==== ================================  
                        0:0   0000         jump +25 
[built-in]              0:0   0001 @LR00   pushscope -999 0 
[built-in]              0:0   0002 @LR01   storelocal $runonce 
[built-in]              0:0   0003 @LR02   storelocal $filename 
[built-in]              0:0   0004 @LR03   push _KOSArgMarker_ 
[built-in]              0:0   0005 @LR04   push $filename 
[built-in]              0:0   0006 @LR05   eval 
[built-in]              0:0   0007 @LR06   push True 
[built-in]              0:0   0008 @LR07   push null 
[built-in]              0:0   0009 @LR08   call load() 
[built-in]              0:0   0010 @LR09   br.false +10 
[built-in]              0:0   0011 @LR10   push $runonce 
[built-in]              0:0   0012 @LR11   br.false +8 
[built-in]              0:0   0013 @LR12   pop 
[built-in]              0:0   0014 @LR13   testargbottom 
[built-in]              0:0   0015 @LR14   br.true +2 
[built-in]              0:0   0016 @LR15   jump -3 
[built-in]              0:0   0017 @LR16   pop 
[built-in]              0:0   0018 @LR17   push 0 
[built-in]              0:0   0019 @LR18   return 1 deep 
[built-in]              0:0   0020 @LR19   storelocal $entrypoint 
[built-in]              0:0   0021 @LR20   call $entrypoint 
[built-in]              0:0   0022 @LR21   pop 
[built-in]              0:0   0023 @LR22   push 0 
[built-in]              0:0   0024 @LR23   return 1 deep 
interpreter             1:1   0025 @0001   pushscope 1 0 
interpreter             1:5   0026 @0002   push $addons 
interpreter             1:5   0027 @0003   getmember tr 
interpreter             1:42  0028 @0004   push _KOSArgMarker_ 
interpreter             1:43  0029 @0005   push True 
interpreter             1:48  0030 @0006   push False 
interpreter             1:54  0031 @0007   push True 
interpreter             1:59  0032 @0008   push True 
interpreter             1:59  0033 @0009   call list() 
interpreter             1:59  0034 @0010   setmember descentprofilemodes <<--INSTRUCTION POINTER--
interpreter             1:0   0035 @0011   popscope 1 
                        0:0   0036         EOF 

UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
ModuleManager.UnityLogHandle.InterceptLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
kOS.KSPLogger:Log(Exception)
kOS.Safe.Execution.CPU:KOSFixedUpdate(Double)
kOS.Safe.UpdateHandler:UpdateFixedObservers(Double)
kOS.Module.kOSProcessor:UpdateFixedObservers()
kOS.Module.kOSProcessor:FixedUpdate()

(Filename: C:\buildslave\unity\build\Runtime/Export/Debug/Debug.bindings.h Line: 35)

kOS: Argument Stack dump:
Argument Stack: count = 0
Scope Stack: count = 1
000 SP-> kOS.Safe.Execution.VariableScope (type: VariableScope)

          ScopeId=1, ParentScopeId=0, IsClosure=False

UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
ModuleManager.UnityLogHandle.InterceptLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
kOS.KSPLogger:Log(String)
kOS.Safe.Execution.CPU:KOSFixedUpdate(Double)
kOS.Safe.UpdateHandler:UpdateFixedObservers(Double)
kOS.Module.kOSProcessor:UpdateFixedObservers()
kOS.Module.kOSProcessor:FixedUpdate()

(Filename: C:\buildslave\unity\build\Runtime/Export/Debug/Debug.bindings.h Line: 35)
Dunbaratu commented 4 years ago

Here's an idea to try. In the AddSuffix line (and in the definition of both GetProfileModes(), and SetProfileModes()) Try relaxing the required data type from ListValue<BooleanValue> to just the general ListValue (not mentioning that it must contain BooleanValues). In the kerboscript itself, all ListValue's end up being open-ended, allowing any kind of Structure (i.e. it's legal to mix types within a List like so: set foo to LIST(false, 100, "string"). The IConvertable error you got might have been in the attempt to change the parameter from the more generic ListValue that the kerboscript program makes to the more specific ListValue<BooleanValue> that your implementing methods want to see.

I've hit this problem before, where the C# code can work with specific type-limited ListValues (using the <...> forms), but the script code cannot, so anything going to the script needs to create a ListValue that doesn't specify element type, and anything reading from the script needs to expect a ListValue that doesn't limit element type (and thus the individual elements need to be tested for type and if they aren't the right type, make an exception the user will see.)

PiezPiedPy commented 4 years ago

Thanks I'll give it a go using ListValue instead of ListValue<...>

[edit] So I've tried the above and seems to be working fine 😎

Dunbaratu commented 3 years ago

Closing the issue because it's kind of supplementally about a PR rather than being its own issue, and it looks like it got resolved in that PR that will get merged later.