How do resources work for liveupdates?

I want to put all the atlas resources in separate archives so that the player does not download what has not changed every time I update the game. So I have a few questions:

  1. If the atlases are unchanged, but only the code changes, do the atlas resources, their hexDigest, remain unchanged in a new build?

  2. If atlas resources were built in one version of the engine and the rest of the resources are from another version, will there be problems with this?

From what I observed, the resource hexDigest will not be changed in a new build, event if the resource content is modified. Not sure when we change the engine version.

1 Like

Correct, a resource which hasn’t changed will have the same hexDigest

It depends. If the data format has changed it will obviously be a problem, but otherwise not. We very rarely change the data format, but things have changed a couple of time recently since we introduced both multi-page atlases and multi-texture support recently. We leave it up to the developer to validate both the integrity of the archive, resources and that resources are compatible with the version of the engine the game is using.

2 Likes

The hash of the file will be different for sure.
Perhaps you were looking at the path hash, which will only change if the path to the file changes?

I remember that I did a test and see it unchanged but it seems I was wrong (just retested)

Since you are also working on separating archives, I have a script you may need it sometimes. It collects only different resources compared with the old graph.json.
One thing I’m noticing is that there are some resources that have been changed hexDigest but I haven’t changed its content.

make_update.py
import os
import sys
import json
import shutil

current_dir = os.path.dirname(os.path.realpath(__file__))
extract_dir = os.path.join(current_dir, 'tmp1')
working_dir = os.path.join(current_dir, 'tmp2')


if __name__ == '__main__':

	# requirement: python 3
	# usage: python make_update.py [old_graph_filename] [new_graph_filename] [source_zip]
	# example: python make_update.py v10.graph.json v11.graph.json v11_full.zip
	# output: update.zip (contains only new & changed files)

	old_graph_filename = sys.argv[1]
	new_graph_filename = sys.argv[2]
	source_zip = sys.argv[3]

	with open(old_graph_filename) as user_file:
		contents = json.loads(user_file.read())
		old_graph = {}
		for el in contents:
			old_graph[el['hexDigest']] = el

	with open(new_graph_filename) as user_file:
		contents = json.loads(user_file.read())
		new_graph = {}
		for el in contents:
			new_graph[el['hexDigest']] = el

	changed_list = []
	for key in new_graph:
		if key not in old_graph and new_graph[key]['isInMainBundle'] == False:
			changed_list.append(new_graph[key])

	if len(changed_list) > 0:
		shutil.rmtree(extract_dir, ignore_errors=True)
		shutil.rmtree(working_dir, ignore_errors=True)
		os.makedirs(working_dir)
		shutil.unpack_archive(source_zip, extract_dir)
		for el in changed_list:
			print(el['hexDigest'] + ' - ' + el['path'])
			shutil.copy(extract_dir + '/' + el['hexDigest'], working_dir)
		shutil.copy(extract_dir + '/liveupdate.game.dmanifest', working_dir)
		shutil.make_archive('update', 'zip', working_dir)
		shutil.rmtree(extract_dir)
		shutil.rmtree(working_dir)
1 Like

Can you give an example?

Sure, this is the output of my last command, I only made changes in /scenes/guard/scene_guard.guic

Output
PS D:\Defold\Projects\KyHoang\tools> python make_update.py old.graph.json new.graph.json all.zip
92570b8b3e17a5e3d7537db2c455a94bcb269df4 - /i18n/interpolate.luac
9a4d678a8b5e167c95d35a1b076dde5a667aed2c - /scripts/log.luac
391058f0c412650df162664da0bed8d3ed8eb68d - /translations/en.luac
f117f22e184940c27004d6daff4b880ad8f78064 - /translations/vi.luac
d55fe4d06d547cfbb6b15c0ad86cc5815ee8e92e - /scenes/guard/scene_guard.guic
3e974abc289c2d02e0fd25bf717607ea8cfed499 - /scenes/guard/scene_guard.gui_scriptc
a886f758d972b949499dabd585bbbc40c32a74aa - /colyseus/messagepack/MessagePack.luac
0776be2661856dbd29bb8f787a153482ab6daeaf - /colyseus/protocol.luac
557deeeae7369e896eeb2e17b8bbd04b96ecd5b4 - /colyseus/serialization/schema/schema.luac
eacbe4b218a8bf84cfb4ddf325173cd877c7a945 - /colyseus/serialization/schema/constants.luac
61f3a91319ecdf0dc2fb5dcc99337bef457e622c - /colyseus/serialization/fossil_delta/fossil_delta.luac
8bbf2a2bea0a6d2ede0cf250e9e7d88f546c33c9 - /colyseus/serialization/json.luac
87f02371542aeab32465dd2c853b8b3c574562e4 - /colyseus/utils/url.luac
c38b9240035b5084290b58282b15caaf3c83199f - /scripts/layout.luac
7312e3cbb7515c49687d53dc7aadb2cf2347ea97 - /pigeon/letters.luac
fac66390928a332a2986d6118aee581328dcef43 - /druid/const.luac
161b6b8076a57b2b565e1d32c19eec8dc7f6c7d9 - /druid/component.luac
31cfaa3d1a55e9d0ee9f57118a0287cce17ac4d8 - /druid/base/text.luac
6fb4eeef363105e6829cf334e203fa172a2f62bf - /druid/styles/default/style.luac
f63e14774aa7485076d650715dbb642515f1df2f - /scripts/druid/input.luac
77c486f211d23eb103ba7a853b2a4b7a479eedc9 - /commons/ui/ui_avatar_frame.luac
40ccd823de4e5b8351b4daa6d5b671b2b9242ea6 - /scenes/loading/scene_loading.scriptc
9b760c546ab3a49214ba271c3285cd08e73c8974 - /scenes/lobby/scene_lobby.gui_scriptc
39b42963e02bd87fcc4b579c101a4e50ad394c3d - /scenes/lobby/ui/ui_panel_category.luac
30e083db666279923f25ca61e668fa4bfaed35a1 - /scenes/lobby/modal_hot_matches.gui_scriptc
eda16a0802725cc552959bfd59e5ca2b06dc4e73 - /scripts/conquest.luac
7cef2857b6ff622eed536e1d3775c37599f13d4e - /scenes/lobby/modal_players.gui_scriptc
1157d6b14f21030e052bd19de6269702d87dde56 - /commons/modal_bag.gui_scriptc
f0c987baeb4ec38028f788e167ce95242e55df4f - /scenes/lobby/modal_notice_board.gui_scriptc
9f47e1f4e6a59926a708d48e12085af854deef55 - /scenes/lobby/modal_quest_center.gui_scriptc
759d62f46db37bdae29e2b91c7fb08e7042e3854 - /scenes/lobby/modal_ranking_board.gui_scriptc
cafe4fde87cbcb207bfbbabae4a7b5ce86bebfed - /scenes/lobby/modal_ranking_reward.gui_scriptc
8c2fd5978f92d185cf5985e665554359551f0f81 - /scenes/lobby/modal_avatar_select.gui_scriptc
a2b057d9f60f8727238eccef8c32d325cee4cf11 - /scenes/lobby/modal_followers.gui_scriptc
4022dd884ce2f6a73d6470e3519fe9ffb04b11c5 - /scenes/lobby/modal_invite_play_response.gui_scriptc
1f70fd03a98c9c7c48a85dad3a33b9571be46b26 - /scenes/lobby/modal_settings.gui_scriptc
f7dd2de681b3abcbc326eafb3678a6de411e40ff - /scenes/lobby/modal_delete_account.gui_scriptc
4ed9e6f97da8f9cbc5b917382d2ece86781a593f - /scenes/lobby/modal_choose_music.gui_scriptc
22f11d24e1dcf43d10b43f39d88641dc675920b7 - /scenes/lobby/modal_buy_coins.gui_scriptc
786cf9bcb01df12c2a160e4c47d5c2f51acc03fa - /scenes/lobby/modal_clubs.gui_scriptc
2f7cf2f5da6ea16c9d4fb15af294aa28fa52cf68 - /scenes/lobby/modal_create_club.gui_scriptc
b4d533cdc9d991bce68ad6b864d42fc864c65322 - /scenes/match/modal_players.gui_scriptc
4da8ced997a00eafb2dc8bc1f84cb833ec64db7a - /commons/battle.scriptc
c543c869f4fd859d025054663496ef05616e65ff - /scenes/conquest/scene_conquest.gui_scriptc
fec798c3d0f66e39a8efa499e45c855fe63af578 - /commons/ui/ui_conquest_place.luac
55a8160d73249186094ed4707faabbbc93b4fa21 - /scenes/conquest/modal_battles.gui_scriptc
931629ee93bd4b55027afd64fc36aaf5a177434b - /scenes/conquest_battle/scene_conquest_battle.gui_scriptc
e7ee808a37ad68e0aa9b99e2bb06e841c19daec8 - /scenes/conquest_battle/modal_records.gui_scriptc
e35596bbcef6a86959ee5c9afcbe777e65ee68c8 - /scenes/conquest_battle/modal_replay.gui_scriptc
0961a36ea9740ebbc957ea8cdd1f655b5eb64680 - /scenes/conquest_battle/modal_battles.gui_scriptc
2eea62d0077984e2d907366bc2485717e698f295 - /scenes/conquest_create_battle/scene_conquest_create_battle.gui_scriptc
5adfcc5db62c49bc7e1f6187a3683796673ee14a - /scenes/conquest_create_battle/modal_save.gui_scriptc
16fb8b14930c7046af322383534d4b0e8ba9063d - /scenes/club_lobby/modal_members.gui_scriptc
868012afbfddaf48fca75b9d09bf9131573eff89 - /scenes/club_lobby/modal_edit_message.gui_scriptc
92b502edba59edc11b84cb0cdb0dcb9a7b78794f - /scenes/club_lobby/modal_edit_club.gui_scriptc
a325b505fbc0f7fcf57eefc82268390ddfa37495 - /scenes/club_match/scene_club_match.gui_scriptc
e924878fa042599d08af704a706720412c6808d0 - /scenes/club_match/modal_players.gui_scriptc
7866298435a68ee1d174107b7c9b21ed083beadc - /scenes/club_match/modal_match_info.gui_scriptc

I double checked some items and see they actually changed. I guess it related to fetching libraries when making bundle?