feat(ludic.update): a Velopack game updating itself, a frame at a time
latest.json asked once and polled (the newest version and its notes, in a language or English); then Velopack's releases.<os>.json, the plan (the delta chain when the installed full package is on disk, no delta is missing and the chain weighs less than the full one; the newest full package otherwise, named as the feed names it), each package streamed to the packages folder with its bytes on a bar and an eased pace, its SHA-256 by Get-FileHash or shasum in a child, Update patch per delta and Update apply --waitPid. macOS keeps its packages outside the bundle and names --rootDir and --packageDir; a translocated app is not installed. Ports with the runtime as every default: UpdateWorld (platform, exe, pid, now_ms), UpdateNet (get, download, poll, text, received, free), UpdateProc (spawn, poll, free). Config: feed, app_id, version, root, packages, mac_packages, scratch. It words nothing: states, UPDATE_ERR_* codes and numbers, and UPDATE_STARTED / _STOPPED / _APPLYING facts - the game exits on the last. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
63e29f024c
commit
3514d46954
13 changed files with 1103 additions and 0 deletions
73
packages/ludic.update/latest.ludic
Normal file
73
packages/ludic.update/latest.ludic
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# latest.ludic - "a new version is out": latest.json asked once, polled a frame at a time, so a
|
||||
# slow or absent network costs the frame nothing - it simply never says there is one.
|
||||
# latest.json is {"version": "0.5.1", "notes": {"en": ["...", ...], "tr": [...]}}
|
||||
var update__ask: int = 0
|
||||
var update__ask_h: int = -1
|
||||
var update__latest: string = ""
|
||||
var update__notes: Val = null
|
||||
|
||||
# once a frame on the screen that shows it; with show_current, the notes of the version being
|
||||
# played are kept too (a launcher shows them)
|
||||
export function update_check_tick(show_current: bool) -> void {
|
||||
if update__ask == UPDATE_ASK_IDLE {
|
||||
update__ask_h = UpdateNet.get(update__conf().feed + "/" + update__conf().latest)
|
||||
update__ask = UPDATE_ASKING
|
||||
if update__ask_h < 0 { update__ask = UPDATE_NONE }
|
||||
return
|
||||
}
|
||||
if update__ask != UPDATE_ASKING { return }
|
||||
let st = UpdateNet.poll(update__ask_h)
|
||||
if st < 0 { return }
|
||||
update__ask = UPDATE_NONE
|
||||
if st > 0 { update__latest_read(Json.parse(UpdateNet.text(update__ask_h)), show_current) }
|
||||
UpdateNet.free(update__ask_h)
|
||||
update__ask_h = -1
|
||||
}
|
||||
|
||||
function update__latest_read(v: Val, show_current: bool) -> void {
|
||||
if v == null or Value.kind(v) != 6 or Value.has(v, "version") == 0 { return }
|
||||
let ver = Value.as_str(Value.get(v, "version"))
|
||||
let mine = update__conf().version
|
||||
if update_newer(ver, mine) { update__ask = UPDATE_NEWER }
|
||||
else if ver == mine and show_current { update__ask = UPDATE_CURRENT }
|
||||
else { return }
|
||||
update__latest = ver
|
||||
if Value.has(v, "notes") != 0 { update__notes = Value.get(v, "notes") }
|
||||
}
|
||||
|
||||
export function update_ask_state() -> int { return update__ask }
|
||||
export function update_latest() -> string { return update__latest }
|
||||
export function update_shown() -> bool { return update__ask == UPDATE_NEWER or update__ask == UPDATE_CURRENT }
|
||||
export function update_fresh() -> bool { return update__ask == UPDATE_NEWER }
|
||||
|
||||
# the notes in this language, else English, else none
|
||||
export function update_notes(lang: string) -> []string {
|
||||
let out = new []string
|
||||
if update__notes == null or Value.kind(update__notes) != 6 { return out }
|
||||
var key = lang
|
||||
if Value.has(update__notes, key) == 0 { key = "en" }
|
||||
if Value.has(update__notes, key) == 0 { return out }
|
||||
let l = Value.get(update__notes, key)
|
||||
for i in 0 .. Value.count(l) { push(out, Value.as_str(Value.at(l, i))) }
|
||||
return out
|
||||
}
|
||||
|
||||
# back to before anything was asked or installed (tests)
|
||||
export function update_reset() -> void {
|
||||
if update__ask_h >= 0 { UpdateNet.free(update__ask_h) }
|
||||
update__ask_h = -1
|
||||
update__ask = UPDATE_ASK_IDLE
|
||||
update__latest = ""
|
||||
update__notes = null
|
||||
update__release()
|
||||
update__state = UPDATE_IDLE
|
||||
update__err = UPDATE_ERR_NONE
|
||||
update__target = ""
|
||||
update__files = null
|
||||
update__total_bytes = 0
|
||||
update__done_bytes = 0
|
||||
update__i = 0
|
||||
update__rate = 0
|
||||
update__rate_t = -1
|
||||
q_clear(update_facts())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue