ludic.update: whether this copy can update itself is worked out once, when the updater is configured (the panel asked every frame, building the path to the executable each time); the notes are a list the state keeps, filled by update_notes_for when the version or the language changes, which the game calls from its update tick. The feed's address is declared (once, when a check starts). ludic.steps, ludic.effects, ludic.telemetry: what is left is made on an event and declared with its bound - an arc's tables, a chapter's columns, a step's fact, an effect's start, end and clear, the fact pool's growth, the player id, a props record's nesting stack - and two pushes into a caller's kept list, at most a chapter's steps and the ring's size. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
101 lines
3.9 KiB
Text
101 lines
3.9 KiB
Text
# install.ludic - where Velopack put this copy: its root, its packages, its updater, and whether
|
|
# it is an install at all. Windows: <root>\Update.exe, <root>\current\<game>.exe, <root>\packages.
|
|
# macOS: the .app IS the install and UpdateMac sits beside the binary in Contents/MacOS.
|
|
# "0.10.2" beats "0.9.9": compared a number at a time, not as text
|
|
export function update_newer(a: string, b: string) -> bool {
|
|
let pa = Text.split(a, ".")
|
|
let pb = Text.split(b, ".")
|
|
for i in 0 .. 3 {
|
|
var x = 0
|
|
var y = 0
|
|
if i < len(pa) { x = Text.to_int(pa[i]) }
|
|
if i < len(pb) { y = Text.to_int(pb[i]) }
|
|
if x != y { return x > y }
|
|
}
|
|
return false
|
|
}
|
|
|
|
# Velopack runs the game with --veloapp-install / -updated / -obsolete / -uninstall and kills it if
|
|
# it is still running 15-30 s later: a game that sees one exits at once
|
|
export function update_is_install_hook() -> bool {
|
|
let a = Os.args()
|
|
for i in 1 .. len(a) {
|
|
let s = a[i]
|
|
if len(s) >= 10 { if s[0..10] == "--veloapp-" { return true } }
|
|
}
|
|
return false
|
|
}
|
|
|
|
function update__mac() -> bool { return UpdateWorld.platform() == "macos" }
|
|
|
|
function update__sep() -> string {
|
|
if UpdateWorld.platform() == "windows" { return "\\" }
|
|
return "/"
|
|
}
|
|
|
|
# the directory above p, with no ".." left in it (UpdateMac is handed it as its root)
|
|
function update__up(p: string) -> string {
|
|
var cut = -1
|
|
for i in 0 .. len(p) { if p[i..i + 1] == "/" or p[i..i + 1] == "\\" { cut = i } }
|
|
if cut < 0 { return "." }
|
|
return p[0..cut]
|
|
}
|
|
|
|
# the executable's directory
|
|
export function update_dir() -> string { return update__up(UpdateWorld.exe()) }
|
|
|
|
export function update_root(update_st: UpdateState) -> string {
|
|
let c = update__conf(update_st)
|
|
if len(c.root) > 0 { return c.root }
|
|
if update__mac() { return update__up(update__up(update_dir())) }
|
|
return update_dir() + "\\.."
|
|
}
|
|
|
|
# where the packages live: never inside a macOS bundle, which is read-only and which apply replaces
|
|
export function update_packages(update_st: UpdateState) -> string {
|
|
let c = update__conf(update_st)
|
|
if len(c.packages) > 0 { return c.packages }
|
|
if update__mac() and len(c.mac_packages) > 0 { return c.mac_packages }
|
|
return update_root(update_st) + update__sep() + "packages"
|
|
}
|
|
|
|
function update__feed_file() -> string {
|
|
if update__mac() { return "releases.osx.json" }
|
|
return "releases.win.json"
|
|
}
|
|
|
|
function update__updater(update_st: UpdateState) -> string {
|
|
if update__mac() { return update_root(update_st) + "/Contents/MacOS/UpdateMac" }
|
|
return update_root(update_st) + update__sep() + "Update.exe"
|
|
}
|
|
|
|
# A quarantined Mac app still where it was downloaded runs from a read-only /AppTranslocation/
|
|
# image that vanishes when it exits: nothing there can be updated in place
|
|
export function update_translocated() -> bool {
|
|
return update__mac() and Text.contains(UpdateWorld.exe(), "/AppTranslocation/")
|
|
}
|
|
|
|
# can this copy update itself? A copy Velopack did not install downloads the game again instead.
|
|
export function update_installed(update_st: UpdateState) -> bool { return update_st.update__installed }
|
|
|
|
@alloc_ok("once, when the updater is configured")
|
|
function update__installed_now(update_st: UpdateState) -> bool {
|
|
if len(update__conf(update_st).root) > 0 { return true }
|
|
if update_translocated() { return false }
|
|
if update__mac() { return Fs.exists(update_dir() + "/UpdateMac") }
|
|
return Fs.exists(update_dir() + "/../Update.exe")
|
|
}
|
|
|
|
# a page in the player's browser (the site, the changelog)
|
|
export function update_open(url: string) -> void {
|
|
if update__mac() { System.run(`open "{url}"`) } else { System.run(`cmd /c start "" "{url}"`) }
|
|
}
|
|
|
|
# a path inside a PowerShell single-quoted string: a quote is written twice
|
|
function update__psq(p: string) -> string { return "'" + Text.replace(p, "'", "''") + "'" }
|
|
|
|
function update__powershell() -> string {
|
|
var root = "C:\\Windows"
|
|
if Os.has_env("SystemRoot") and len(Os.env("SystemRoot")) > 0 { root = Os.env("SystemRoot") }
|
|
return root + "\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
|
|
}
|