mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
Merge branch 'Dev' into merge-duckie-main-2026-05-06
This commit is contained in:
@@ -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.";
|
||||
});
|
||||
})();
|
||||
})();
|
||||
|
||||
Vendored
+37
-9
@@ -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.";
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user