# tick.ludic - once a frame while an update runs: whatever the step waits on, and the next step export function update_tick(update_st: mut UpdateState) -> void { update__rate_tick(update_st) if update_st.update__state == UPDATE_FEED { update__feed_tick(update_st) } else if update_st.update__state == UPDATE_FETCH { update__fetch_tick(update_st) } else if update_st.update__state == UPDATE_CHECK { update__check_tick(update_st) } else if update_st.update__state == UPDATE_PATCH { update__patch_tick(update_st) } } function update__feed_tick(update_st: mut UpdateState) -> void { let st = UpdateNet.poll(update_st.update__h) if st < 0 { return } var feed: Val = null if st > 0 { feed = Json.parse(UpdateNet.text(update_st.update__h)) } UpdateNet.free(update_st.update__h) update_st.update__h = -1 if feed == null { update__fail(update_st, UPDATE_ERR_UNREACHABLE) return } if not update_plan(update_st, feed) { update__fail(update_st, UPDATE_ERR_NOTHING) return } update__fact(update_st, UPDATE_STARTED) update__fetch(update_st, 0) } function update__fetch_tick(update_st: mut UpdateState) -> void { let st = UpdateNet.poll(update_st.update__h) if st < 0 { return } UpdateNet.free(update_st.update__h) update_st.update__h = -1 if st == 0 { Fs.remove(update__path(update_st, update_st.update__i)) update__fail(update_st, UPDATE_ERR_DOWNLOAD) return } update__check(update_st, update_st.update__i) } # a package rebuilt from deltas is not compared with the full one's hash: patch re-assembles the # archive, so the same files come out as different bytes function update__check_tick(update_st: mut UpdateState) -> void { let code = UpdateProc.poll(update_st.update__proc) if code < 0 { return } UpdateProc.free(update_st.update__proc) update_st.update__proc = -1 if code != 0 or update__hash_read(update_st) != Text.upper(update_st.update__sha[update_st.update__i]) { Fs.remove(update__path(update_st, update_st.update__i)) update__fail(update_st, UPDATE_ERR_CORRUPT) return } update_st.update__done_bytes += update_st.update__size[update_st.update__i] if update_st.update__delta[update_st.update__i] { update__patch(update_st, update_st.update__i) } else { update__apply(update_st, update__path(update_st, update_st.update__i)) } } function update__patch(update_st: mut UpdateState, i: int) -> void { let out = update_packages(update_st) + update__sep() + update_st.update__full[i] if Fs.exists(out) { Fs.remove(out) } let args = new []string push(args, "patch") push(args, "--silent") push(args, "--old") push(args, update_st.update__old) push(args, "--delta") push(args, update__path(update_st, i)) push(args, "--output") push(args, out) update_st.update__proc = UpdateProc.spawn(update__updater(update_st), args) if update_st.update__proc < 0 { update__fail(update_st, UPDATE_ERR_PATCH_START) return } update_st.update__old = out update_st.update__state = UPDATE_PATCH update_st.update__step_t0 = UpdateWorld.now_ms() } function update__patch_tick(update_st: mut UpdateState) -> void { let code = UpdateProc.poll(update_st.update__proc) if code < 0 { return } UpdateProc.free(update_st.update__proc) update_st.update__proc = -1 if code != 0 or not Fs.exists(update_st.update__old) { update__fail(update_st, UPDATE_ERR_PATCH) return } if update_st.update__i + 1 < len(update_st.update__files) { update__fetch(update_st, update_st.update__i + 1) } else { update__apply(update_st, update_st.update__old) } } # Velopack waits for this process to be gone, swaps the install and starts the new version; the # game hears UPDATE_APPLYING and exits. On macOS neither the root nor the packages are where # UpdateMac would look, so both are named. function update__apply(update_st: mut UpdateState, package: string) -> void { update_st.update__package = package let args = new []string push(args, "apply") push(args, "--silent") push(args, "--waitPid") push(args, `{UpdateWorld.pid()}`) push(args, "--package") push(args, package) if update__mac() { push(args, "--rootDir") push(args, update_root(update_st)) push(args, "--packageDir") push(args, update_packages(update_st)) } update_st.update__proc = UpdateProc.spawn(update__updater(update_st), args) if update_st.update__proc < 0 { update__fail(update_st, UPDATE_ERR_APPLY) return } update_st.update__state = UPDATE_APPLY update__fact(update_st, UPDATE_APPLYING) }