untoldwind / KontrolSystem2

Autopilot scripting system for KSP2
Other
54 stars 14 forks source link

sync fn and warp_to #133

Closed PhilouDS closed 1 month ago

PhilouDS commented 3 months ago

I have an error message with my script below used in the VAB. image I use it inside the .on_click method of my warp button. It's the first time I see this error. Is it caused by the last released version (0.5.4.1)?

This is my script. I have a comment line just before the problem

use { floor } from core::math
use { CONSOLE } from ksp::console
use { screen_size, open_window, Align } from ksp::ui
use { yield, current_time } from ksp::game
use { warp_to } from ksp::game::warp

pub fn main_editor () -> Unit = {
  CONSOLE.clear()

  create_window()
}

fn create_window() -> Unit = {
  const myScreen = screen_size()
  const window_width = myScreen.x/5
  const window_height = 50
  const text_font_size = 15
  const main_window = open_window("<b>CURRENT TIME</b>",
    (myScreen.x-window_width-10),
    myScreen.y - 40,
    window_width,
    window_height)

  const hours_per_day = 6

  const now_time: Cell<float> = Cell(current_time())

  const now_time_days: Cell<float> = Cell(0)
    now_time_days.value = floor(now_time.value / (hours_per_day*3600))
  const days_in_game: Cell<float> = Cell(0)
    days_in_game.value = now_time_days.value + 1
  const now_time_hours: Cell<float> = Cell(0)
    now_time_hours.value = floor((now_time.value - now_time_days.value*(hours_per_day*3600)) / 3600)
  const now_time_minutes: Cell<float> = Cell(0)
    now_time_minutes.value = floor((now_time.value - now_time_hours.value*3600) / 60)
  const now_time_seconds: Cell<float> = Cell(0) 
    now_time_seconds.value = floor(now_time.value - now_time_hours.value*3600 - now_time_minutes.value*60)

  const main_box = main_window.add_vertical(10.0, Align.Stretch)
  const detail_box = main_window.add_horizontal(10.0, Align.Center)

  const toggle_box = main_window.add_vertical(10.0, Align.Stretch)

  const warp_box = main_window.add_vertical(10.0, Align.Stretch)

  const now_time_label = main_box.add_label("Current time (s):", Align.Stretch)
  now_time_label.bind(now_time, "Current time: {0:N2} s")
  now_time_label.font_size = text_font_size

  const days_label = detail_box.add_label("day:", Align.Center)
  days_label.bind(days_in_game, "{0:N0} d")
  days_label.font_size = text_font_size

  const hours_label = detail_box.add_label("hour:", Align.Center)
  hours_label.bind(now_time_hours, "{0:N0} h")
  hours_label.font_size = text_font_size

  const minutes_label = detail_box.add_label("min:", Align.Center)
  minutes_label.bind(now_time_minutes, "{0:N0} min")
  minutes_label.font_size = text_font_size

  const seconds_label = detail_box.add_label("sec:", Align.Center)
  seconds_label.bind(now_time_seconds, "{0:N0} s")
  seconds_label.font_size = text_font_size

  const toggle_sec = toggle_box.add_toggle("WARP to Current time + DeltaT", Align.Start)
  toggle_sec.value = true
  toggle_sec.font_size = text_font_size
  const toggle_UT = toggle_box.add_toggle("WARP to new UT", Align.Start)
  toggle_UT.value = false
  toggle_UT.font_size = text_font_size

  const warp_input_box = warp_box.add_horizontal(10.0, Align.Stretch, 10.0)
  const warp_label = warp_input_box.add_label("Warp (deltaT in sec):", Align.Start)
  warp_label.font_size = text_font_size
  const warp_value = warp_input_box.add_float_input(Align.Start, 10.0)
  warp_value.value = 0

// -----------------------------------> PROBLEM HERE

  const warp_button = warp_box.add_button("WARP", Align.Center)
  warp_button.on_click(fn() -> {
    const now = current_time()
    const deltaT = warp_value.value
    if (toggle_sec.value) {
      warp_to(now + deltaT)
    }
    else {
      warp_to(deltaT)
    }
  })

  toggle_sec.on_change(fn(b) -> {
    if (toggle_sec.value) {
      toggle_UT.value = false
      warp_label.text = "Warp (deltaT in sec)"
    }
    CONSOLE.clear()
  })

  toggle_UT.on_change(fn(b) -> {
    if (toggle_UT.value) {
      toggle_sec.value = false
      warp_label.text = "Warp (UT in sec)"
    }
    CONSOLE.clear()
  })

  while(!main_window.is_closed) {
    now_time.value = current_time()
    now_time_days.value = floor(now_time.value / (hours_per_day*3600))
    days_in_game.value = now_time_days.value + 1
    now_time_hours.value = floor((now_time.value - now_time_days.value*(hours_per_day*3600)) / 3600)
    now_time_minutes.value = floor((now_time.value - now_time_days.value*(hours_per_day*3600) - now_time_hours.value*3600) / 60)
    now_time_seconds.value = floor(now_time.value - now_time_days.value*(hours_per_day*3600) - now_time_hours.value*3600 - now_time_minutes.value*60)

    toggle_sec.enabled = !toggle_sec.value
    toggle_UT.enabled = !toggle_UT.value

    yield()
  }
}
untoldwind commented 3 months ago

Yes, warp_to has recently changed to async because I had noticed some very strange behavior when using it without properly surrounding it with a yield in certain situations (i.e. wait for a FixedUpdate to happen).

But I can see your use-case and triggering a warp in a UI callback most like will not cause any problem. As a quick-fix I added a sync_warp_to function to ksp::game::warp with the previous functionality in this pre-release: https://github.com/untoldwind/KontrolSystem2/releases/tag/v0.5.5.1

... I guess the proper way to fix this would be to allow for async lambda functions, which I tried to avoid so far.

PhilouDS commented 3 months ago

I tried this and it worked fine. Could I write my scrip differently so I can continue to use warp_to instead of sync_warp_to? I have no problem to use sync_warp_to, it's just to understand.

(btw, the last vsix extension (0.0.27) for visual studio is great too)

PhilouDS commented 3 months ago

Just a thing about the vsix extension. I have this "error" message with the use of format. image

untoldwind commented 3 months ago

The vsix extension (or rather the underlying lsp-server) still has some issues with generics, I made a small tweak so that at least no error is shown if the concrete type is still in question.

Concerning the warp_to: As a rule of thumb, use warp_to whenever possible unless there is this sync context error (e.g. inside at button callback).

Unluckily the underlying problem is quite complicated.

In the unity engine every component might have an Update or FixedUpdate method which is called either on every frame refresh or as often as possible. In KSP2 these are used to do a lot of calculations and internal updates.

For the scripting part the problem boils down to: How many changes can you make to the internal game state inbetween those update cycles without the entire state getting wonky. E.g. changing a maneuver node and triggering a warp_to in the same cycle has created some pretty unpredictable results, therefore the change.

So the new warp_to will do:

Unluckily this currently only works, when the script is in an async context (like in the fn main_flight function). Inside a button callback this is not so easy, because it is an UI event listener and "Waiting for the next FixedUpdate" would most likely freeze the UI.

I am still not sure about the best solution for this, eventually though there should be only one warp_to function that works in both cases.

untoldwind commented 3 months ago

In 0.5.5.3 https://github.com/untoldwind/KontrolSystem2/releases/tag/v0.5.5.3

the two version of war_to should be merge again, i.e. there is just one warp_to that can be use in sync and async (under the hood there are still two functions that are picked based on the context ... in short it is the task of the compiler to worry about what version to use).

PhilouDS commented 3 months ago

Nice 👍

github-actions[bot] commented 1 month ago

This issue is stale because it has been open for 60 days with no activity.

github-actions[bot] commented 1 month ago

This issue was closed because it has been inactive for 14 days since being marked as stale.