Skip to content

Commit dd8cf73

Browse files
committed
Download objects and load unihex from store
1 parent 144a7c4 commit dd8cf73

File tree

12 files changed

+205
-88
lines changed

12 files changed

+205
-88
lines changed

polymer/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ if (UNIX)
3434
target_link_libraries(polymer PRIVATE glfw)
3535
elseif (WIN32)
3636
add_compile_definitions(WIN32_LEAN_AND_MEAN VK_USE_PLATFORM_WIN32_KHR)
37+
38+
target_link_libraries(polymer PRIVATE ../vcpkg_installed/x64-windows-static/lib/tomcrypt)
3739
endif()
3840

3941
target_include_directories(polymer PRIVATE ${CURL_INCLUDE_DIRS})

polymer/asset/asset_store.cpp

Lines changed: 135 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ constexpr const char* kVersionDescriptorUrl =
2020
"https://piston-meta.mojang.com/v1/packages/715ccf3330885e75b205124f09f8712542cbe7e0/1.20.1.json";
2121

2222
const HashSha1 kVersionDescriptorHash("715ccf3330885e75b205124f09f8712542cbe7e0");
23+
const String kResourceApi = POLY_STR("https://resources.download.minecraft.net/");
2324

2425
static json_object_s* FindJsonObjectElement(json_object_s* obj, const char* name) {
2526
json_object_element_s* element = obj->start;
@@ -94,15 +95,20 @@ inline char* GetAbsolutePath(MemoryArena& arena, const String& path, const char*
9495
return buffer;
9596
}
9697

97-
inline bool GetOrCreateFolder(Platform& platform, const char* path) {
98-
if (!platform.FolderExists(path)) {
99-
if (!platform.CreateFolder(path)) {
100-
fprintf(stderr, "Failed to create folder '%s'\n", path);
101-
return false;
102-
}
103-
}
98+
inline char* GetObjectUrl(MemoryArena& arena, HashSha1 hash) {
99+
char hash_str[3] = {};
100+
u8 hash_value = hash.hash[0];
101+
102+
sprintf(hash_str, "%02x", hash_value);
103+
104+
char full_hash[41];
105+
hash.ToString(full_hash);
106+
107+
char* buffer = (char*)arena.Allocate(kResourceApi.size + sizeof(full_hash) + sizeof(hash_str) + 1);
104108

105-
return true;
109+
sprintf(buffer, "%.*s%s/%s", (u32)kResourceApi.size, kResourceApi.data, hash_str, full_hash);
110+
111+
return buffer;
106112
}
107113

108114
void AssetStore::Initialize() {
@@ -112,33 +118,23 @@ void AssetStore::Initialize() {
112118

113119
// Check local store for version descriptor file
114120
if (HasAsset(version_info)) {
115-
ProcessVersionDescriptor(version_info);
121+
char* filename = GetAbsolutePath(trans_arena, path, "versions\\%s", kVersionDescriptor);
122+
123+
ProcessVersionDescriptor(filename);
116124
} else {
117125
net_queue.PushRequest(kVersionDescriptorUrl, this, [](NetworkRequest* request, NetworkResponse* response) {
118126
AssetStore* store = (AssetStore*)request->userp;
119127

120128
char* filename = GetAbsolutePath(store->trans_arena, store->path, "versions\\%s", kVersionDescriptor);
121129

122130
response->SaveToFile(filename);
123-
AssetInfo info;
124-
125-
info.type = AssetType::VersionDescriptor;
126-
info.exists = true;
127-
strcpy(info.path, filename);
128-
129-
store->ProcessVersionDescriptor(info);
131+
store->ProcessVersionDescriptor(filename);
130132
});
131133
}
132134
}
133135

134-
void AssetStore::ProcessVersionDescriptor(const AssetInfo& info) {
135-
//
136-
// Load json and grab client/index
137-
138-
// If not client, net_queue request
139-
// if not index, net_queue request
140-
141-
String contents = ReadEntireFile(info.path, trans_arena);
136+
void AssetStore::ProcessVersionDescriptor(const char* path) {
137+
String contents = ReadEntireFile(path, trans_arena);
142138

143139
json_value_s* root_value = json_parse(contents.data, contents.size);
144140
if (!root_value || root_value->type != json_type_object) {
@@ -215,86 +211,163 @@ void AssetStore::ProcessVersionDescriptor(const AssetInfo& info) {
215211
char* filename = GetAbsolutePath(store->trans_arena, store->path, "index\\%s", kVersionIndex);
216212

217213
response->SaveToFile(filename);
214+
store->ProcessIndex(filename);
215+
});
216+
} else {
217+
char* filename = GetAbsolutePath(trans_arena, this->path, "index\\%s", kVersionIndex);
218218

219-
AssetInfo index_info = {};
219+
ProcessIndex(filename);
220+
}
221+
}
222+
}
220223

221-
index_info.type = AssetType::Index;
222-
strcpy(index_info.path, filename);
224+
void AssetStore::ProcessIndex(const char* filename) {
225+
String contents = ReadEntireFile(filename, trans_arena);
223226

224-
store->ProcessIndex(index_info);
225-
});
226-
} else {
227-
char* filename = GetAbsolutePath(trans_arena, path, "index\\%s", kVersionIndex);
227+
json_value_s* root_value = json_parse(contents.data, contents.size);
228+
if (!root_value || root_value->type != json_type_object) {
229+
fprintf(stderr, "AssetStore: Failed to parse version index json.\n");
230+
exit(1);
231+
}
232+
233+
json_object_s* root = json_value_as_object(root_value);
234+
json_object_s* objects = FindJsonObjectElement(root, "objects");
235+
236+
if (!objects) {
237+
fprintf(stderr, "AssetStore: Invalid 'objects' element of version index. Expected object.\n");
238+
exit(1);
239+
}
240+
241+
json_object_element_s* object_element = objects->start;
242+
while (object_element) {
243+
String element_name(object_element->name->string, object_element->name->string_size);
244+
245+
json_object_element_s* current_obj_ele = object_element;
246+
247+
object_element = object_element->next;
248+
249+
// Skip over objects that aren't currently necessary.
250+
if (poly_contains(element_name, POLY_STR("sound"))) continue;
251+
if (poly_contains(element_name, POLY_STR("/lang/"))) continue;
252+
if (poly_contains(element_name, POLY_STR("icons/"))) continue;
253+
if (poly_contains(element_name, POLY_STR("/resourcepacks/"))) continue;
254+
255+
if (current_obj_ele->value->type == json_type_object) {
256+
json_object_s* obj = json_value_as_object(current_obj_ele->value);
257+
258+
String obj_hash_str = FindJsonStringValue(obj, "hash");
259+
260+
if (obj_hash_str.size > 0) {
261+
HashSha1 hash(obj_hash_str.data, obj_hash_str.size);
262+
263+
AssetInfo info = {};
264+
info.type = AssetType::Object;
265+
info.hash = hash;
266+
267+
asset_hash_map.Insert(element_name, hash);
268+
269+
// Check local store for item.
270+
// If not found, request from server.
271+
if (!HasAsset(info)) {
272+
char* url = GetObjectUrl(trans_arena, hash);
228273

229-
strcpy(index_info.path, filename);
274+
net_queue.PushRequest(url, this, [](NetworkRequest* request, NetworkResponse* response) {
275+
AssetStore* store = (AssetStore*)request->userp;
230276

231-
ProcessIndex(index_info);
277+
char* relative_name = request->url + kResourceApi.size;
278+
char* filename = GetAbsolutePath(store->trans_arena, store->path, "objects\\%s", relative_name);
279+
280+
response->SaveToFile(filename);
281+
});
282+
}
283+
}
232284
}
233285
}
234286
}
235287

236-
void AssetStore::ProcessIndex(const AssetInfo& info) {
237-
printf("TODO: Process index. (%s)\n", info.path);
288+
String AssetStore::LoadObject(MemoryArena& arena, String name) {
289+
HashSha1* hash = asset_hash_map.Find(name);
290+
291+
if (hash) {
292+
char hash_str[3] = {};
293+
u8 hash_value = hash->hash[0];
294+
295+
sprintf(hash_str, "%02x", hash_value);
296+
297+
char* objects_folder = GetAbsolutePath(trans_arena, path, "objects");
298+
if (platform.FolderExists(objects_folder)) {
299+
char* hash_folder = GetAbsolutePath(trans_arena, path, "objects\\%s", hash_str);
300+
301+
if (platform.FolderExists(hash_folder)) {
302+
char fullhash[41];
303+
304+
hash->ToString(fullhash);
305+
306+
char* filename = GetAbsolutePath(trans_arena, path, "objects\\%s\\%s", hash_str, fullhash);
307+
308+
return ReadEntireFile(filename, arena);
309+
}
310+
}
311+
}
312+
313+
fprintf(stderr, "AssetStore::GetAsset failed to find asset with name %.*s\n", (u32)name.size, name.data);
314+
return {};
238315
}
239316

240317
bool AssetStore::HasAsset(AssetInfo& info) {
318+
char* filename = nullptr;
319+
320+
ArenaSnapshot snapshot = trans_arena.GetSnapshot();
321+
241322
switch (info.type) {
242323
case AssetType::Client: {
243324
char* versions_folder = GetAbsolutePath(trans_arena, path, "versions");
244325
if (!platform.FolderExists(versions_folder)) return false;
245326

246-
char* filename = GetAbsolutePath(trans_arena, path, "versions\\%s", kVersionJar);
247-
HashSha1 existing_hash = GetFileSha1(trans_arena, filename);
248-
249-
return existing_hash == info.hash;
327+
filename = GetAbsolutePath(trans_arena, path, "versions\\%s", kVersionJar);
250328
} break;
251329
case AssetType::VersionDescriptor: {
252330
char* index_folder = GetAbsolutePath(trans_arena, path, "versions");
253331
if (!platform.FolderExists(index_folder)) return false;
254332

255-
char* filename = GetAbsolutePath(trans_arena, path, "versions\\%s", kVersionDescriptor);
256-
HashSha1 existing_hash = GetFileSha1(trans_arena, filename);
257-
258-
if (existing_hash != info.hash) {
259-
return false;
260-
}
261-
262-
strcpy(info.path, filename);
263-
return true;
333+
filename = GetAbsolutePath(trans_arena, path, "versions\\%s", kVersionDescriptor);
264334
} break;
265335
case AssetType::Index: {
266336
char* index_folder = GetAbsolutePath(trans_arena, path, "index");
267337
if (!platform.FolderExists(index_folder)) return false;
268338

269-
char* filename = GetAbsolutePath(trans_arena, path, "index\\%s", kVersionIndex);
270-
HashSha1 existing_hash = GetFileSha1(trans_arena, filename);
271-
272-
return existing_hash == info.hash;
339+
filename = GetAbsolutePath(trans_arena, path, "index\\%s", kVersionIndex);
273340
} break;
274341
case AssetType::Object: {
275-
char hash_str[3] = {};
342+
char minihash[3] = {};
276343
u8 hash_value = info.hash.hash[0];
277344

278-
sprintf(hash_str, "%02x", hash_value);
345+
sprintf(minihash, "%02x", hash_value);
346+
char fullhash[41];
347+
info.hash.ToString(fullhash);
279348

280349
char* objects_folder = GetAbsolutePath(trans_arena, path, "objects");
281350
if (!platform.FolderExists(objects_folder)) return false;
282351

283-
char* hash_folder = GetAbsolutePath(trans_arena, path, "objects\\%s", hash_str);
352+
char* hash_folder = GetAbsolutePath(trans_arena, path, "objects\\%s", minihash);
284353
if (!platform.FolderExists(hash_folder)) return false;
285354

286-
char* filename =
287-
GetAbsolutePath(trans_arena, path, "objects\\%s\\%.*s", hash_str, (u32)info.name.size, info.name.data);
288-
289-
HashSha1 existing_hash = GetFileSha1(trans_arena, filename);
290-
291-
return existing_hash == info.hash;
355+
filename = GetAbsolutePath(trans_arena, path, "objects\\%s\\%s", minihash, fullhash);
292356
} break;
293357
default: {
294358
} break;
295359
}
296360

297-
return false;
361+
bool exists = false;
362+
363+
if (filename) {
364+
HashSha1 existing_hash = GetFileSha1(trans_arena, filename);
365+
exists = existing_hash == info.hash;
366+
}
367+
368+
trans_arena.Revert(snapshot);
369+
370+
return exists;
298371
}
299372

300373
char* AssetStore::GetClientPath(MemoryArena& arena) {

polymer/asset/asset_store.h

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <polymer/platform/platform.h>
66
#include <polymer/types.h>
77

8+
#include <stdio.h>
89
#include <stdlib.h>
910
#include <string.h>
1011

@@ -39,18 +40,22 @@ struct HashSha1 {
3940
bool operator!=(const HashSha1& other) const {
4041
return !(*this == other);
4142
}
43+
44+
void ToString(char* out) {
45+
for (size_t i = 0; i < sizeof(hash); ++i) {
46+
sprintf(out + i * 2, "%02x", (int)hash[i]);
47+
}
48+
49+
out[40] = 0;
50+
}
4251
};
4352

4453
enum class AssetType : u8 { VersionDescriptor, Index, Object, Client };
4554

4655
struct AssetInfo {
4756
String name;
4857
HashSha1 hash;
49-
char path[2048];
50-
size_t size;
51-
5258
AssetType type;
53-
bool exists;
5459
};
5560

5661
// Keeps a local asset store synchronized with the remote store by using the index.
@@ -64,24 +69,26 @@ struct AssetStore {
6469
MemoryArena& perm_arena;
6570
MemoryArena& trans_arena;
6671
NetworkQueue& net_queue;
67-
HashMap<String, AssetInfo, MapStringHasher> asset_map;
72+
HashMap<String, HashSha1, MapStringHasher> asset_hash_map;
6873
String path;
6974

70-
AssetStore(Platform& platform, MemoryArena& perm_arena, MemoryArena& trans_arena, NetworkQueue& net_queue,
71-
String path)
72-
: platform(platform), perm_arena(perm_arena), trans_arena(trans_arena), asset_map(perm_arena),
73-
net_queue(net_queue), path(path) {}
75+
AssetStore(Platform& platform, MemoryArena& perm_arena, MemoryArena& trans_arena, NetworkQueue& net_queue)
76+
: platform(platform), perm_arena(perm_arena), trans_arena(trans_arena), asset_hash_map(perm_arena),
77+
net_queue(net_queue) {
78+
path = platform.GetAssetStorePath(trans_arena);
79+
}
7480

7581
void Initialize();
7682

7783
bool HasAsset(AssetInfo& info);
78-
bool StoreAsset(const AssetInfo& info);
7984

8085
char* GetClientPath(MemoryArena& arena);
8186

87+
String LoadObject(MemoryArena& arena, String name);
88+
8289
private:
83-
void ProcessVersionDescriptor(const AssetInfo& info);
84-
void ProcessIndex(const AssetInfo& info);
90+
void ProcessVersionDescriptor(const char* path);
91+
void ProcessIndex(const char* path);
8592
};
8693

8794
} // namespace asset

polymer/asset/asset_system.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,24 @@ bool AssetSystem::LoadFont(render::VulkanRenderer& renderer, MemoryArena& perm_a
8888

8989
UnihexFont font(glyph_size_table, kGlyphPageWidth, kGlyphPageWidth, kGlyphPageCount);
9090

91-
if (!font.Load("unifont.hex", perm_arena, trans_arena)) {
91+
String font_zip = asset_store->LoadObject(trans_arena, POLY_STR("minecraft/font/unifont.zip"));
92+
ZipArchive zip = {};
93+
94+
if (!zip.OpenFromMemory(font_zip)) {
95+
fprintf(stderr, "AssetSystem: Failed to open 'minecraft/font/unifont.zip' from memory.\n");
96+
return false;
97+
}
98+
99+
size_t unifont_size = 0;
100+
char* unifont_data = zip.ReadFile(&trans_arena, "unifont_all_no_pua-15.0.06.hex", &unifont_size);
101+
102+
if (!unifont_data) {
103+
fprintf(stderr, "AssetSystem: Failed to read 'unifont_all_no_pua-15.0.06.hex' in 'minecraft/font/unifont.zip'");
104+
trans_arena.Revert(snapshot);
105+
return false;
106+
}
107+
108+
if (!font.Load(perm_arena, trans_arena, String(unifont_data, unifont_size))) {
92109
trans_arena.Revert(snapshot);
93110
return false;
94111
}

polymer/asset/asset_system.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <polymer/types.h>
77
#include <polymer/world/block.h>
88

9+
#include <polymer/asset/asset_store.h>
910
#include <polymer/asset/block_assets.h>
1011

1112
namespace polymer {
@@ -23,6 +24,7 @@ struct AssetSystem {
2324
BlockAssets* block_assets = nullptr;
2425
render::TextureArray* glyph_page_texture = nullptr;
2526
u8* glyph_size_table = nullptr;
27+
AssetStore* asset_store = nullptr;
2628

2729
AssetSystem();
2830

0 commit comments

Comments
 (0)