Merge branch 'Dev' into merge-duckie-main-2026-05-06

This commit is contained in:
DuckieTM
2026-05-08 07:45:17 +02:00
committed by GitHub
18 changed files with 1512 additions and 418 deletions
+166 -16
View File
@@ -1,9 +1,9 @@
(() => {
const ASSET_KEY = new TextEncoder().encode("slogga-dist-assets-2026");
const MODE_DEFAULTS = {
distObfuscationEnabled: true,
secureAssetsEnabled: true,
secureApiEnabled: true
distObfuscationEnabled: false,
secureAssetsEnabled: false,
secureApiEnabled: false
};
const isDebug = () => {
@@ -37,6 +37,9 @@
};
const getBase = () => {
if(typeof window.__nitroLoaderBase === "string" && window.__nitroLoaderBase) {
try { return new URL(window.__nitroLoaderBase); } catch {}
}
const source = document.currentScript?.src || location.href;
return new URL(".", source);
};
@@ -81,10 +84,17 @@
return [...new Map(urls.map(url => [url.href, url])).values()];
};
const expandAssetCandidates = (path) => {
const base = getBase();
if(/^https?:\/\//i.test(path)) return [new URL(path)];
if(path.startsWith("/")) return [new URL(path, base.origin + "/")];
return resolveAssetCandidates(path);
};
const fetchBytes = async (path) => {
let error = null;
debug("loader: fetching " + path);
for(const candidate of resolveAssetCandidates(path)) {
for(const candidate of expandAssetCandidates(path)) {
try {
debug("loader: try " + candidate.href);
const response = await fetch(withCacheBust(candidate), { cache: "no-store" });
@@ -110,9 +120,39 @@
debug("loader: css injected from dat");
};
const matchesContentType = (contentType, accepted) => {
if(!contentType) return true;
return accepted.some(token => contentType.indexOf(token) !== -1);
};
const probePlainAsset = async (path, accepted) => {
let lastError = null;
for(const candidate of expandAssetCandidates(path)) {
try {
debug("loader: probe " + candidate.href);
const response = await fetch(withCacheBust(candidate), { cache: "no-store" });
if(!response.ok) {
lastError = new Error("asset " + candidate.pathname + " " + response.status);
continue;
}
const contentType = (response.headers.get("content-type") || "").toLowerCase();
if(!matchesContentType(contentType, accepted)) {
lastError = new Error("asset " + candidate.pathname + " wrong type " + contentType);
continue;
}
debug("loader: probe ok " + candidate.href);
const url = new URL(candidate.href);
url.searchParams.set("v", Date.now().toString(36));
return url;
} catch(caught) {
lastError = caught;
}
}
throw lastError || new Error("asset " + path + " not found");
};
const loadPlainCss = async (path) => {
const href = resolveAssetCandidates(path)[0];
href.searchParams.set("v", Date.now().toString(36));
const href = await probePlainAsset(path, ["text/css"]);
await new Promise((resolve, reject) => {
const link = document.createElement("link");
link.rel = "stylesheet";
@@ -136,9 +176,8 @@
};
const importPlainJs = async (path) => {
const href = resolveAssetCandidates(path)[0];
href.searchParams.set("v", Date.now().toString(36));
debug("loader: importing plain js");
const href = await probePlainAsset(path, ["javascript", "ecmascript"]);
debug("loader: importing plain js " + href.href);
await import(href.href);
debug("loader: plain js imported");
};
@@ -164,24 +203,135 @@
}
};
const fetchManifest = async () => {
const base = getBase();
const candidates = [
new URL(".vite/manifest.json", base.origin + "/"),
new URL("manifest.json", base.origin + "/"),
new URL(".vite/manifest.json", base),
new URL("manifest.json", base)
];
const seen = new Set();
for(const candidate of candidates) {
if(seen.has(candidate.href)) continue;
seen.add(candidate.href);
try {
const response = await fetch(withCacheBust(new URL(candidate.href)), { cache: "no-store" });
if(!response.ok) continue;
const json = await response.json();
if(json && typeof json === "object") {
debug("loader: manifest from " + candidate.href);
return { manifest: json, base: new URL(".", candidate.href) };
}
} catch {}
}
return null;
};
const findEntryFromManifest = (manifest) => {
let bootstrap = null;
for(const key of Object.keys(manifest)) {
const entry = manifest[key];
if(!entry || typeof entry !== "object" || !entry.isEntry) continue;
if(/bootstrap\./.test(key) || /bootstrap\./.test(entry.file || "")) {
bootstrap = entry;
break;
}
if(!bootstrap) bootstrap = entry;
}
if(!bootstrap) return null;
const css = Array.isArray(bootstrap.css) ? bootstrap.css.slice() : [];
return { js: bootstrap.file, css };
};
const resolveManifestPath = (manifestBase, file) => {
if(/^https?:\/\//i.test(file)) return file;
if(file.startsWith("/")) return file;
return new URL(file, manifestBase.origin + "/").pathname;
};
const isLoaderUrl = (href) => /(?:^|\/)bootstrap\.js(?:$|\?|#)/i.test(href) || /(?:^|\/)asset-loader\.js(?:$|\?|#)/i.test(href);
const fetchEntryFromIndexHtml = async () => {
const base = getBase();
const candidates = [
new URL("/index.html", base.origin + "/"),
new URL("/", base.origin + "/")
];
for(const candidate of candidates) {
try {
const response = await fetch(withCacheBust(new URL(candidate.href)), { cache: "no-store" });
if(!response.ok) continue;
const contentType = (response.headers.get("content-type") || "").toLowerCase();
if(contentType && contentType.indexOf("html") === -1) continue;
const html = await response.text();
const doc = new DOMParser().parseFromString(html, "text/html");
if(!doc) continue;
const resolveAttr = (raw) => {
if(!raw) return "";
if(/^https?:\/\//i.test(raw)) return raw;
try { return new URL(raw, candidate.href).pathname; }
catch { return raw; }
};
const scriptNode = Array.from(doc.querySelectorAll('script[type="module"][src]'))
.map(node => node.getAttribute("src") || "")
.find(src => src && !isLoaderUrl(src));
if(!scriptNode) continue;
const cssNodes = Array.from(doc.querySelectorAll('link[rel="stylesheet"][href]'))
.map(node => node.getAttribute("href") || "")
.filter(href => href && !isLoaderUrl(href));
const jsAbs = resolveAttr(scriptNode);
const cssAbs = cssNodes.map(resolveAttr);
debug("loader: entry from index.html " + jsAbs);
return { js: jsAbs, css: cssAbs };
} catch {}
}
return null;
};
(async () => {
debug("loader: start");
renderShell();
const mode = await readClientMode();
let jsPath = null;
let cssPaths = [];
const manifestResult = await fetchManifest();
if(manifestResult) {
const entry = findEntryFromManifest(manifestResult.manifest);
if(entry) {
jsPath = resolveManifestPath(manifestResult.base, entry.js);
if(entry.css.length) cssPaths = entry.css.map(file => resolveManifestPath(manifestResult.base, file));
debug("loader: entry from manifest " + jsPath);
}
}
if(!jsPath) {
const indexEntry = await fetchEntryFromIndexHtml();
if(indexEntry) {
jsPath = indexEntry.js;
if(indexEntry.css.length) cssPaths = indexEntry.css;
}
}
if(!jsPath) {
jsPath = "./assets/app.js";
cssPaths = ["./assets/app.css"];
debug("loader: entry fallback to app.js/app.css");
}
if(mode.distObfuscationEnabled) {
const [cssBytes, jsBytes] = await Promise.all([
loadDatAsset("./assets/app.css.dat"),
loadDatAsset("./assets/app.js.dat")
const [cssBytesList, jsBytes] = await Promise.all([
Promise.all(cssPaths.map(path => loadDatAsset(path + ".dat"))),
loadDatAsset(jsPath + ".dat")
]);
injectCssText(cssBytes);
cssBytesList.forEach(bytes => injectCssText(bytes));
await importBytes(jsBytes);
return;
}
await loadPlainCss("./assets/app.css");
await importPlainJs("./assets/app.js");
for(const css of cssPaths) await loadPlainCss(css);
await importPlainJs(jsPath);
})().catch(error => {
console.error(error);
debug("loader: failed " + (error?.message || error));
document.body.textContent = "Unable to load client.";
});
})();
})();
+37 -9
View File
@@ -12,12 +12,16 @@
};
ensureMobileViewport();
const FALLBACK_API_BASE = "";
const getBase = () => {
const source = document.currentScript?.src || location.href;
return new URL(".", source);
};
const LOADER_BASE = getBase();
window.__nitroLoaderBase = LOADER_BASE.href;
const withCacheBust = (url) => {
url.searchParams.set("v", Date.now().toString(36));
return url;
@@ -81,18 +85,34 @@
}
};
const fetchPlainClientMode = async () => {
try {
const url = withCacheBust(new URL("./client-mode.json", LOADER_BASE));
const response = await fetch(url, { cache: "no-store" });
if(!response.ok) throw new Error("HTTP " + response.status);
const payload = await response.json();
if(payload && typeof payload === "object") {
window.__nitroClientMode = payload;
return payload;
}
} catch(error) {
console.warn("[Nitro] client-mode fetch failed:", error?.message || error);
}
return null;
};
const loadPlainBootstrap = async () => {
const url = withCacheBust(new URL("./asset-loader.js", getBase()));
const url = withCacheBust(new URL("./asset-loader.js", LOADER_BASE));
await import(url.href);
};
const loadSecureBootstrap = async () => {
if(!API_BASE) throw new Error("Missing apiBaseUrl for secure bootstrap.");
const loadSecureBootstrap = async (apiBase) => {
if(!apiBase) throw new Error("Missing apiBaseUrl for secure bootstrap.");
const pair = await crypto.subtle.generateKey({ name: "ECDH", namedCurve: "P-256" }, true, ["deriveBits"]);
const publicKeyBuffer = await crypto.subtle.exportKey("spki", pair.publicKey);
const publicKey = bytesToBase64(publicKeyBuffer);
const base = API_BASE.replace(/\/$/, "");
const base = apiBase.replace(/\/$/, "");
const bootstrapResponse = await fetch(base + "/nitro-sec/bootstrap", {
method: "POST",
headers: { "Content-Type": "application/json" },
@@ -132,12 +152,20 @@
};
(async () => {
try {
await loadSecureBootstrap();
} catch(error) {
console.warn("[Nitro] Secure bootstrap fallback:", error?.message || error);
await loadPlainBootstrap();
const mode = await fetchPlainClientMode();
const wantsSecure = !!(mode && mode.secureAssetsEnabled);
const apiBase = (mode && typeof mode.apiBaseUrl === "string" && mode.apiBaseUrl) || FALLBACK_API_BASE;
if(wantsSecure) {
try {
await loadSecureBootstrap(apiBase);
return;
} catch(error) {
console.warn("[Nitro] Secure bootstrap fallback:", error?.message || error);
}
}
await loadPlainBootstrap();
})().catch(error => {
console.error(error);
document.body.textContent = "Unable to load client.";
@@ -0,0 +1,712 @@
{
"backgrounds.data": [
{
"backgroundId": 0
},
{
"backgroundId": 1
},
{
"backgroundId": 2
},
{
"backgroundId": 3
},
{
"backgroundId": 4
},
{
"backgroundId": 5
},
{
"backgroundId": 6
},
{
"backgroundId": 7
},
{
"backgroundId": 8
},
{
"backgroundId": 9
},
{
"backgroundId": 10
},
{
"backgroundId": 11
},
{
"backgroundId": 12
},
{
"backgroundId": 13
},
{
"backgroundId": 14
},
{
"backgroundId": 15
},
{
"backgroundId": 16
},
{
"backgroundId": 17
},
{
"backgroundId": 18
},
{
"backgroundId": 19
},
{
"backgroundId": 20
},
{
"backgroundId": 21
},
{
"backgroundId": 22
},
{
"backgroundId": 23
},
{
"backgroundId": 24
},
{
"backgroundId": 25
},
{
"backgroundId": 26
},
{
"backgroundId": 27
},
{
"backgroundId": 28
},
{
"backgroundId": 29
},
{
"backgroundId": 30
},
{
"backgroundId": 31
},
{
"backgroundId": 32
},
{
"backgroundId": 33
},
{
"backgroundId": 34
},
{
"backgroundId": 35
},
{
"backgroundId": 36
},
{
"backgroundId": 37
},
{
"backgroundId": 38
},
{
"backgroundId": 39
},
{
"backgroundId": 40
},
{
"backgroundId": 41
},
{
"backgroundId": 42
},
{
"backgroundId": 43
},
{
"backgroundId": 44
},
{
"backgroundId": 45
},
{
"backgroundId": 46
},
{
"backgroundId": 47
},
{
"backgroundId": 48
},
{
"backgroundId": 49
},
{
"backgroundId": 50
},
{
"backgroundId": 51
},
{
"backgroundId": 52
},
{
"backgroundId": 53
},
{
"backgroundId": 54
},
{
"backgroundId": 55
},
{
"backgroundId": 56
},
{
"backgroundId": 57
},
{
"backgroundId": 58
},
{
"backgroundId": 59
},
{
"backgroundId": 60
},
{
"backgroundId": 61
},
{
"backgroundId": 62
},
{
"backgroundId": 63
},
{
"backgroundId": 64
},
{
"backgroundId": 65
},
{
"backgroundId": 66
},
{
"backgroundId": 67
},
{
"backgroundId": 68
},
{
"backgroundId": 69
},
{
"backgroundId": 70
},
{
"backgroundId": 71
},
{
"backgroundId": 72
},
{
"backgroundId": 73
},
{
"backgroundId": 74
},
{
"backgroundId": 75
},
{
"backgroundId": 76
},
{
"backgroundId": 77
},
{
"backgroundId": 78
},
{
"backgroundId": 79
},
{
"backgroundId": 80
},
{
"backgroundId": 81
},
{
"backgroundId": 82
},
{
"backgroundId": 83
},
{
"backgroundId": 84
},
{
"backgroundId": 85
},
{
"backgroundId": 86
},
{
"backgroundId": 87
},
{
"backgroundId": 88
},
{
"backgroundId": 89
},
{
"backgroundId": 90
},
{
"backgroundId": 91
},
{
"backgroundId": 92
},
{
"backgroundId": 93
},
{
"backgroundId": 94
},
{
"backgroundId": 95
},
{
"backgroundId": 96
},
{
"backgroundId": 97
},
{
"backgroundId": 98
},
{
"backgroundId": 99
},
{
"backgroundId": 100
},
{
"backgroundId": 101
},
{
"backgroundId": 102
},
{
"backgroundId": 103
},
{
"backgroundId": 104
},
{
"backgroundId": 105
},
{
"backgroundId": 106
},
{
"backgroundId": 107
},
{
"backgroundId": 108
},
{
"backgroundId": 109
},
{
"backgroundId": 110
},
{
"backgroundId": 111
},
{
"backgroundId": 112
},
{
"backgroundId": 113
},
{
"backgroundId": 114
},
{
"backgroundId": 115
},
{
"backgroundId": 116
},
{
"backgroundId": 117
},
{
"backgroundId": 118
},
{
"backgroundId": 119
},
{
"backgroundId": 120
},
{
"backgroundId": 121
},
{
"backgroundId": 122
},
{
"backgroundId": 123
},
{
"backgroundId": 124
},
{
"backgroundId": 125
},
{
"backgroundId": 126
},
{
"backgroundId": 127
},
{
"backgroundId": 128
},
{
"backgroundId": 129
},
{
"backgroundId": 130
},
{
"backgroundId": 131
},
{
"backgroundId": 132
},
{
"backgroundId": 133
},
{
"backgroundId": 134
},
{
"backgroundId": 135
},
{
"backgroundId": 136
},
{
"backgroundId": 137
},
{
"backgroundId": 138
},
{
"backgroundId": 139
},
{
"backgroundId": 140
},
{
"backgroundId": 141
},
{
"backgroundId": 142
},
{
"backgroundId": 143
},
{
"backgroundId": 144
},
{
"backgroundId": 145
},
{
"backgroundId": 146
},
{
"backgroundId": 147
},
{
"backgroundId": 148
},
{
"backgroundId": 149
},
{
"backgroundId": 150
},
{
"backgroundId": 151
},
{
"backgroundId": 152
},
{
"backgroundId": 153
},
{
"backgroundId": 154
},
{
"backgroundId": 155
},
{
"backgroundId": 156
},
{
"backgroundId": 157
},
{
"backgroundId": 158
},
{
"backgroundId": 159
},
{
"backgroundId": 160
},
{
"backgroundId": 161
},
{
"backgroundId": 162
},
{
"backgroundId": 163
},
{
"backgroundId": 164
},
{
"backgroundId": 165
},
{
"backgroundId": 166
},
{
"backgroundId": 167
},
{
"backgroundId": 168
},
{
"backgroundId": 169
},
{
"backgroundId": 170
},
{
"backgroundId": 171
},
{
"backgroundId": 172
},
{
"backgroundId": 173
},
{
"backgroundId": 174
},
{
"backgroundId": 175
},
{
"backgroundId": 176
},
{
"backgroundId": 177
},
{
"backgroundId": 178
},
{
"backgroundId": 179
},
{
"backgroundId": 180
},
{
"backgroundId": 181
},
{
"backgroundId": 182
},
{
"backgroundId": 183
},
{
"backgroundId": 184
},
{
"backgroundId": 185
},
{
"backgroundId": 186
},
{
"backgroundId": 187
}
],
"stands.data": [
{
"standId": 0
},
{
"standId": 1
},
{
"standId": 2
},
{
"standId": 3
},
{
"standId": 4
},
{
"standId": 5
},
{
"standId": 6
},
{
"standId": 7
},
{
"standId": 8
},
{
"standId": 9
},
{
"standId": 10
},
{
"standId": 11
},
{
"standId": 12
},
{
"standId": 13
},
{
"standId": 14
},
{
"standId": 15
},
{
"standId": 16
},
{
"standId": 17
},
{
"standId": 18
},
{
"standId": 19
},
{
"standId": 20
},
{
"standId": 21
}
],
"overlays.data": [
{
"overlayId": 0
},
{
"overlayId": 1
},
{
"overlayId": 2
},
{
"overlayId": 3
},
{
"overlayId": 4
},
{
"overlayId": 5
},
{
"overlayId": 6
},
{
"overlayId": 7
},
{
"overlayId": 8
}
],
"cards.data": [
{
"backgroundId": 1
},
{
"backgroundId": 2
},
{
"backgroundId": 3
},
{
"backgroundId": 4
},
{
"backgroundId": 5
},
{
"backgroundId": 6
},
{
"backgroundId": 7
},
{
"backgroundId": 8
},
{
"backgroundId": 9
},
{
"backgroundId": 10
},
{
"backgroundId": 11
},
{
"backgroundId": 12
},
{
"backgroundId": 13
},
{
"backgroundId": 14
},
{
"backgroundId": 15
}
]
}
+567 -48
View File
@@ -28,29 +28,44 @@
"generic.asset.url": "${asset.url}/generic/%libname%.nitro",
"badge.asset.url": "${image.library.url}album1584/%badgename%.gif",
"furni.rotation.bounce.steps": 20,
"furni.rotation.bounce.height": 0.0625,
"enable.avatar.arrow": false,
"system.log.debug": true,
"system.log.warn": true,
"system.log.error": true,
"system.log.events": false,
"system.log.packets": true,
"system.fps.animation": 24,
"system.fps.max": 60,
"system.pong.manually": true,
"system.pong.interval.ms": 20000,
"room.color.skip.transition": true,
"room.landscapes.enabled": true,
"room.zoom.enabled": true,
"login.screen.enabled": true,
"furni.rotation.bounce.height": 0.0625,
"enable.avatar.arrow": false,
"system.log.debug": true,
"system.log.warn": true,
"system.log.error": true,
"system.log.events": false,
"system.log.packets": false,
"system.fps.animation": 24,
"system.fps.max": 60,
"system.pong.manually": true,
"system.pong.interval.ms": 20000,
"room.color.skip.transition": true,
"room.landscapes.enabled": true,
"room.zoom.enabled": true,
"timezone.settings": "Europe/Amsterdam",
"youtube.publish.disabled": false,
"user.badges.group.slot.enabled": true,
"login.screen.enabled": true,
"login.endpoint": "${api.url}/api/auth/login",
"login.register.endpoint": "${api.url}/api/auth/register",
"login.forgot.endpoint": "${api.url}/api/auth/forgot-password",
"login.logout.endpoint": "${api.url}/api/auth/logout",
"login.remember.endpoint": "${api.url}/api/auth/remember",
"login.turnstile.enabled": false,
"login.turnstile.sitekey": "",
"avatar.mandatory.libraries": [
"login.health.endpoint": "${api.url}/api/health",
"login.check-email.endpoint": "${api.url}/api/auth/check-email",
"login.check-username.endpoint": "${api.url}/api/auth/check-username",
"login.room_templates.endpoint": "${api.url}/api/auth/room-templates",
"login.remember.endpoint": "${api.url}/api/auth/remember",
"login.server_key.endpoint": "${api.url}/api/auth/server-key",
"login.sso-token.endpoint": "${api.url}/api/auth/sso-token",
"login.refresh.endpoint": "${api.url}/api/auth/refresh",
"badges.custom.list.endpoint": "${api.url}/api/badges/custom",
"badges.custom.create.endpoint": "${api.url}/api/badges/custom",
"badges.custom.update.endpoint": "${api.url}/api/badges/custom/%badgeId%",
"badges.custom.delete.endpoint": "${api.url}/api/badges/custom/%badgeId%",
"badges.custom.texts.endpoint": "${api.url}/api/badges/custom/texts",
"login.turnstile.enabled": true,
"login.turnstile.sitekey": "1x00000000000000000000AA",
"avatar.mandatory.libraries": [
"bd:1",
"li:0"
],
@@ -60,32 +75,536 @@
"dance.3",
"dance.4"
],
"avatar.default.figuredata": {
"palettes": [],
"setTypes": []
},
"avatar.default.actions": {
"actions": []
},
"pet.types": [],
"preload.assets.urls": [
"${asset.url}/generic/avatar_additions.nitro",
"${asset.url}/generic/group_badge.nitro",
"${asset.url}/generic/floor_editor.nitro",
"${images.url}/loading_icon.png",
"${images.url}/clear_icon.png",
"${images.url}/big_arrow.png"
],
"login.health.endpoint": "${api.url}/api/health",
"login.health.method": "GET",
"login.check-email.endpoint": "${api.url}/api/auth/check-email",
"login.check-username.endpoint": "${api.url}/api/auth/check-username",
"login.register.imaging.url": "${api.url}/api/avatar/imaging",
"crypto.ws.enabled": true,
"login.news.url": "${api.url}/api/auth/news",
"badges.custom.list.endpoint": "${api.url}/api/badges/custom",
"badges.custom.create.endpoint": "${api.url}/api/badges/custom",
"badges.custom.update.endpoint": "${api.url}/api/badges/custom/%badgeId%",
"badges.custom.delete.endpoint": "${api.url}/api/badges/custom/%badgeId%",
"badges.custom.texts.endpoint": "${api.url}/api/badges/custom/texts"
}
"avatar.default.figuredata": {
"palettes": [
{
"id": 1,
"colors": [
{
"id": 99999,
"index": 1001,
"club": 0,
"selectable": false,
"hexCode": "DDDDDD"
},
{
"id": 99998,
"index": 1001,
"club": 0,
"selectable": false,
"hexCode": "FAFAFA"
}
]
},
{
"id": 3,
"colors": [
{
"id": 10001,
"index": 1001,
"club": 0,
"selectable": false,
"hexCode": "EEEEEE"
},
{
"id": 10002,
"index": 1002,
"club": 0,
"selectable": false,
"hexCode": "FA3831"
},
{
"id": 10003,
"index": 1003,
"club": 0,
"selectable": false,
"hexCode": "FD92A0"
},
{
"id": 10004,
"index": 1004,
"club": 0,
"selectable": false,
"hexCode": "2AC7D2"
},
{
"id": 10005,
"index": 1005,
"club": 0,
"selectable": false,
"hexCode": "35332C"
},
{
"id": 10006,
"index": 1006,
"club": 0,
"selectable": false,
"hexCode": "EFFF92"
},
{
"id": 10007,
"index": 1007,
"club": 0,
"selectable": false,
"hexCode": "C6FF98"
},
{
"id": 10008,
"index": 1008,
"club": 0,
"selectable": false,
"hexCode": "FF925A"
},
{
"id": 10009,
"index": 1009,
"club": 0,
"selectable": false,
"hexCode": "9D597E"
},
{
"id": 10010,
"index": 1010,
"club": 0,
"selectable": false,
"hexCode": "B6F3FF"
},
{
"id": 10011,
"index": 1011,
"club": 0,
"selectable": false,
"hexCode": "6DFF33"
},
{
"id": 10012,
"index": 1012,
"club": 0,
"selectable": false,
"hexCode": "3378C9"
},
{
"id": 10013,
"index": 1013,
"club": 0,
"selectable": false,
"hexCode": "FFB631"
},
{
"id": 10014,
"index": 1014,
"club": 0,
"selectable": false,
"hexCode": "DFA1E9"
},
{
"id": 10015,
"index": 1015,
"club": 0,
"selectable": false,
"hexCode": "F9FB32"
},
{
"id": 10016,
"index": 1016,
"club": 0,
"selectable": false,
"hexCode": "CAAF8F"
},
{
"id": 10017,
"index": 1017,
"club": 0,
"selectable": false,
"hexCode": "C5C6C5"
},
{
"id": 10018,
"index": 1018,
"club": 0,
"selectable": false,
"hexCode": "47623D"
},
{
"id": 10019,
"index": 1019,
"club": 0,
"selectable": false,
"hexCode": "8A8361"
},
{
"id": 10020,
"index": 1020,
"club": 0,
"selectable": false,
"hexCode": "FF8C33"
},
{
"id": 10021,
"index": 1021,
"club": 0,
"selectable": false,
"hexCode": "54C627"
},
{
"id": 10022,
"index": 1022,
"club": 0,
"selectable": false,
"hexCode": "1E6C99"
},
{
"id": 10023,
"index": 1023,
"club": 0,
"selectable": false,
"hexCode": "984F88"
},
{
"id": 10024,
"index": 1024,
"club": 0,
"selectable": false,
"hexCode": "77C8FF"
},
{
"id": 10025,
"index": 1025,
"club": 0,
"selectable": false,
"hexCode": "FFC08E"
},
{
"id": 10026,
"index": 1026,
"club": 0,
"selectable": false,
"hexCode": "3C4B87"
},
{
"id": 10027,
"index": 1027,
"club": 0,
"selectable": false,
"hexCode": "7C2C47"
},
{
"id": 10028,
"index": 1028,
"club": 0,
"selectable": false,
"hexCode": "D7FFE3"
},
{
"id": 10029,
"index": 1029,
"club": 0,
"selectable": false,
"hexCode": "8F3F1C"
},
{
"id": 10030,
"index": 1030,
"club": 0,
"selectable": false,
"hexCode": "FF6393"
},
{
"id": 10031,
"index": 1031,
"club": 0,
"selectable": false,
"hexCode": "1F9B79"
},
{
"id": 10032,
"index": 1032,
"club": 0,
"selectable": false,
"hexCode": "FDFF33"
}
]
}
],
"setTypes": [
{
"type": "hd",
"paletteId": 1,
"mandatory_f_0": true,
"mandatory_f_1": true,
"mandatory_m_0": true,
"mandatory_m_1": true,
"sets": [
{
"id": 99999,
"gender": "U",
"club": 0,
"colorable": true,
"selectable": false,
"preselectable": false,
"sellable": false,
"parts": [
{
"id": 1,
"type": "bd",
"colorable": true,
"index": 0,
"colorindex": 1
},
{
"id": 1,
"type": "hd",
"colorable": true,
"index": 0,
"colorindex": 1
},
{
"id": 1,
"type": "lh",
"colorable": true,
"index": 0,
"colorindex": 1
},
{
"id": 1,
"type": "rh",
"colorable": true,
"index": 0,
"colorindex": 1
}
]
}
]
},
{
"type": "bds",
"paletteId": 1,
"mandatory_f_0": false,
"mandatory_f_1": false,
"mandatory_m_0": false,
"mandatory_m_1": false,
"sets": [
{
"id": 10001,
"gender": "U",
"club": 0,
"colorable": true,
"selectable": false,
"preselectable": false,
"sellable": false,
"parts": [
{
"id": 10001,
"type": "bds",
"colorable": true,
"index": 0,
"colorindex": 1
},
{
"id": 10001,
"type": "lhs",
"colorable": true,
"index": 0,
"colorindex": 1
},
{
"id": 10001,
"type": "rhs",
"colorable": true,
"index": 0,
"colorindex": 1
}
],
"hiddenLayers": [
{
"partType": "bd"
},
{
"partType": "rh"
},
{
"partType": "lh"
}
]
}
]
},
{
"type": "ss",
"paletteId": 3,
"mandatory_f_0": false,
"mandatory_f_1": false,
"mandatory_m_0": false,
"mandatory_m_1": false,
"sets": [
{
"id": 10010,
"gender": "F",
"club": 0,
"colorable": true,
"selectable": false,
"preselectable": false,
"sellable": false,
"parts": [
{
"id": 10001,
"type": "ss",
"colorable": true,
"index": 0,
"colorindex": 1
}
],
"hiddenLayers": [
{
"partType": "ch"
},
{
"partType": "lg"
},
{
"partType": "ca"
},
{
"partType": "wa"
},
{
"partType": "sh"
},
{
"partType": "ls"
},
{
"partType": "rs"
},
{
"partType": "lc"
},
{
"partType": "rc"
},
{
"partType": "cc"
},
{
"partType": "cp"
}
]
},
{
"id": 10011,
"gender": "M",
"club": 0,
"colorable": true,
"selectable": false,
"preselectable": false,
"sellable": false,
"parts": [
{
"id": 10002,
"type": "ss",
"colorable": true,
"index": 0,
"colorindex": 1
}
],
"hiddenLayers": [
{
"partType": "ch"
},
{
"partType": "lg"
},
{
"partType": "ca"
},
{
"partType": "wa"
},
{
"partType": "sh"
},
{
"partType": "ls"
},
{
"partType": "rs"
},
{
"partType": "lc"
},
{
"partType": "rc"
},
{
"partType": "cc"
},
{
"partType": "cp"
}
]
}
]
}
]
},
"avatar.default.actions": {
"actions": [
{
"id": "Default",
"state": "std",
"precedence": 1000,
"main": true,
"isDefault": true,
"geometryType": "vertical",
"activePartSet": "figure",
"assetPartDefinition": "std"
}
]
},
"pet.types": [
"dog",
"cat",
"croco",
"terrier",
"bear",
"pig",
"lion",
"rhino",
"spider",
"turtle",
"chicken",
"frog",
"dragon",
"monster",
"monkey",
"horse",
"monsterplant",
"bunnyeaster",
"bunnyevil",
"bunnydepressed",
"bunnylove",
"pigeongood",
"pigeonevil",
"demonmonkey",
"bearbaby",
"terrierbaby",
"gnome",
"gnome",
"kittenbaby",
"puppybaby",
"pigletbaby",
"haloompa",
"fools",
"pterosaur",
"velociraptor",
"cow",
"LeetPen",
"bbwibb",
"elephants"
],
"preload.assets.urls": [
"${images.url}/loading_icon.png",
"${images.url}/clear_icon.png",
"${images.url}/big_arrow.png"
]
}