diff --git a/.gitignore b/.gitignore
index 249e7db..90a9bfe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,10 @@ Thumbs.db
*.zip
.env
.claude/
+
+# Local runtime config copies
+/public/configuration/renderer-config.json
+/public/configuration/ui-config.json
+/public/configuration/client-mode.json
+/public/configuration/adsense.json
+/public/configuration/hotlooks.json
diff --git a/README.md b/README.md
index a733fba..bc91d90 100644
--- a/README.md
+++ b/README.md
@@ -20,12 +20,13 @@
- `yarn install`
- `yarn link "@nitrots/nitro-renderer"` <== This will link the renderer in the project
- Rename a few files
- - Rename `public/renderer-config.json.example` to `public/renderer-config.json`
- - Rename `public/ui-config.json.example` to `public/ui-config.json`
-- Set your links
- - Open `public/renderer-config.json`
+ - Copy `public/configuration/renderer-config.example` to `public/configuration/renderer-config.json`
+ - Copy `public/configuration/ui-config.example` to `public/configuration/ui-config.json`
+ - Copy `public/configuration/client-mode.example` to `public/configuration/client-mode.json`
+ - Set your links
+ - Open `public/configuration/renderer-config.json`
- Update `socket.url, asset.url, image.library.url, & hof.furni.url`
- - Open `public/ui-config.json`
+ - Open `public/configuration/ui-config.json`
- Update `camera.url, thumbnails.url, url.prefix, habbopages.url`
- `yarn build` <== the final step to build the DIST folder this is where your browser needs to point / or upload this to your /client if you do the compile on a other machine (preferd)
- You can override any variable by passing it to `NitroConfig` in the index.html
diff --git a/docs/secure-runtime-modes.en.html b/docs/secure-runtime-modes.en.html
new file mode 100644
index 0000000..491608d
--- /dev/null
+++ b/docs/secure-runtime-modes.en.html
@@ -0,0 +1,236 @@
+
+
+
+
+
+ Nitro Secure Runtime Modes
+
+
+
+
+
+
+ Nitro V3
+ Secure Runtime
+
+
Runtime configuration guide
+
+ This page gives you a cleaner, readable overview of runtime toggles, example files and the values that belong in config files
+ rather than hardcoded inside src.
+
+
+
+
+
+
+
+
+ Overview
+
+
+
Dist Obfuscation
+
Chooses whether the client loads app.js/app.css or the obfuscated .dat versions.
+
+
+
Secure Assets
+
Controls whether renderer-config, ui-config and gamedata go through /nitro-sec/file.
+
+
+
Secure API
+
Enables or disables runtime encryption for /api/* requests.
+
+
+
+
+
+ Files to use
+
+
+
+
+ | File |
+ Purpose |
+ Note |
+
+
+
+
+ public/configuration/client-mode.example |
+ Template for runtime toggles |
+ Copy it into a real configuration/client-mode.json in deployment; that real file stays ignored by Git |
+
+
+ public/configuration/renderer-config.example |
+ Clean renderer config template |
+ Does not touch your local configuration/renderer-config.json |
+
+
+ public/configuration/ui-config.example |
+ UI config reference template |
+ Use it as the source of truth for UI URLs and widgets |
+
+
+ Latest_Compiled_Version/config.ini.example |
+ Backend secure flags |
+ Defines the emulator-side runtime settings |
+
+
+
+
+
+
+
+ client-mode.example
+ This is the main runtime switchboard. You can enable or disable behavior without editing client source code.
+{
+ "distObfuscationEnabled": true,
+ "secureAssetsEnabled": true,
+ "secureApiEnabled": true,
+ "apiBaseUrl": "https://nitro.example.com:2096",
+ "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
+ "plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
+}
+
+
+
Fields
+
+ distObfuscationEnabled: use .dat or plain assets
+ secureAssetsEnabled: enables /nitro-sec/file
+ secureApiEnabled: encrypts /api/* requests
+ apiBaseUrl: emulator/API base URL
+
+
+
+
Recommendation
+
Always set apiBaseUrl explicitly so you do not rely on fallback logic.
+
+
+
+
+
+ renderer-config.example
+ Socket, API, asset and gamedata URLs should live here, not inside React components.
+
+
+
Main keys
+
+ socket.url
+ api.url
+ asset.url
+ image.library.url
+ images.url
+ gamedata.url
+
+
+
+
Translations
+
+ external.texts.translation.url
+ furnidata.translation.url
+ - Uses
%locale% and %timestamp%
+
+
+
+
+
+
+ ui-config.example
+ UI image and login view sources should come from config values here or from renderer config, never from hardcoded URLs in components.
+
+
Login view
+
+ loginview.images.background
+ loginview.images.drape
+ loginview.images.left
+ loginview.images.right
+ loginview.widgets for promotional blocks
+
+
+
+
+
+ Runtime code involved
+
+
+
src/bootstrap.ts
+
Reads client-mode, builds NitroConfig['config.urls'] and prepares client bootstrap.
+
+
+
src/secure-assets.ts
+
Handles ECDH, decrypt/encrypt, plain fallback and secure API runtime behavior.
+
+
+
scripts/write-asset-loader.mjs
+
Generates public/configuration/asset-loader.js and decides between plain assets and .dat.
+
+
+
scripts/minify-dist.mjs
+
Generates .dat files while keeping plain files available for runtime switching.
+
+
+
+
+
+ Emulator
+nitro.secure.assets.enabled=true
+nitro.secure.api.enabled=true
+nitro.secure.config.root=C:/path/to/Nitro-V3/public
+nitro.secure.gamedata.root=C:/path/to/gamedata
+nitro.secure.master_key=change-me-to-a-long-random-secret
+
+ nitro.secure.assets.enabled: enables /nitro-sec/bootstrap and /nitro-sec/file
+ nitro.secure.api.enabled: enables secure handling for /api/*
+ nitro.secure.config.root: path to live config files
+ nitro.secure.gamedata.root: path to live gamedata
+ nitro.secure.master_key: persistent server-side secret
+
+
+
+
+ Quick scenarios
+
+
+
Everything enabled
+
Secure assets, secure API and dist obfuscation all enabled.
+
+
+
Only .dat
+
Uses obfuscated assets but leaves config/API in plain mode.
+
+
+
Everything plain
+
Complete fallback mode for local testing or debugging.
+
+
+
+
+
+ Final checklist
+
+
You created real files from client-mode.example, renderer-config.example and ui-config.example
+
Public URLs live in config files, not in React components
+
Both plain files and .dat files are deployed
+
Your server exposes a proper MIME type for .dat
+
You set nitro.secure.master_key on the emulator side
+
+
+
+
+
+
+
+
+
diff --git a/docs/secure-runtime-modes.en.md b/docs/secure-runtime-modes.en.md
index 5ff441e..3bac47f 100644
--- a/docs/secure-runtime-modes.en.md
+++ b/docs/secure-runtime-modes.en.md
@@ -3,11 +3,11 @@
This document summarizes all values you may need to configure for:
- `dist` bundle obfuscation (`app.js` / `app.css` → `.dat`)
-- secure runtime assets (`renderer-config.json`, `ui-config.json`, `gamedata`)
+- secure runtime assets (`configuration/renderer-config.json`, `configuration/ui-config.json`, `gamedata`)
- secure runtime API (`/api/*`)
- plain fallbacks when you want to disable the secure layer without removing the code
-## 1. `Nitro-V3/public/client-mode.json`
+## 1. `Nitro-V3/public/configuration/client-mode.json`
This file controls everything at runtime.
@@ -17,7 +17,7 @@ This file controls everything at runtime.
"secureAssetsEnabled": true,
"secureApiEnabled": true,
"apiBaseUrl": "https://nitro.example.com:2096",
- "plainConfigBaseUrl": "https://hotel.example.com/",
+ "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
}
```
@@ -30,7 +30,7 @@ This file controls everything at runtime.
- `secureAssetsEnabled`
- `true`: `bootstrap.ts` and `secure-assets.ts` use `/nitro-sec/file`
- - `false`: `renderer-config.json`, `ui-config.json`, and gamedata are loaded in plain mode
+ - `false`: `configuration/renderer-config.json`, `configuration/ui-config.json`, and gamedata are loaded in plain mode
- `secureApiEnabled`
- `true`: the `fetch` wrapper encrypts `/api/*` requests
@@ -43,7 +43,7 @@ This file controls everything at runtime.
- `plainConfigBaseUrl`
- base URL for plain config files
- - usually: `https://hotel.example.com/`
+ - usually: `https://hotel.example.com/configuration/`
- `plainGamedataBaseUrl`
- base URL for plain gamedata files
@@ -74,7 +74,7 @@ The current fallback is:
(window as any).NitroSecureApiUrl = clientMode.apiBaseUrl || 'https://nitro.example.com:2096/';
```
-So in production it is better to always set `apiBaseUrl` inside `client-mode.json`.
+So in production it is better to always set `apiBaseUrl` inside `configuration/client-mode.json`.
## 3. `Nitro-V3/src/secure-assets.ts`
@@ -95,7 +95,7 @@ This file contains the runtime logic for:
Normally you should not need to touch it unless you want to change the secure protocol itself.
-## 4. `Nitro-V3/public/renderer-config.json`
+## 4. `Nitro-V3/public/configuration/renderer-config.json`
This file still defines the paths used by the renderer.
@@ -129,7 +129,7 @@ You can use plain classic paths, for example:
or you can keep the renderer config as-is and let `secure-assets.ts` handle the fallback conversion.
-## 5. `Nitro-V3/public/ui-config.json`
+## 5. `Nitro-V3/public/configuration/ui-config.json`
There is no secure logic here, but it is one of the files loaded through `config.urls`.
@@ -140,12 +140,12 @@ So you only need to maintain the content itself correctly.
## 6. `Nitro-V3/scripts/write-asset-loader.mjs`
-This script generates `public/asset-loader.js`.
+This script generates `public/configuration/asset-loader.js`.
### What it does now
- renders the initial shell
-- reads `client-mode.json`
+- reads `configuration/client-mode.json`
- decides whether to load:
- `app.css.dat` / `app.js.dat`
- or `assets/app.css` / `assets/app.js`
@@ -194,7 +194,7 @@ nitro.secure.master_key=change-me-to-a-long-random-secret
- enables the secure layer for `/api/*`
- `nitro.secure.config.root`
- - folder used to read `renderer-config.json` and `ui-config.json`
+ - folder used to read `configuration/renderer-config.json` and `configuration/ui-config.json`
- `nitro.secure.gamedata.root`
- folder used to read live gamedata
@@ -207,7 +207,7 @@ nitro.secure.master_key=change-me-to-a-long-random-secret
### Everything enabled
-`client-mode.json`
+`configuration/client-mode.json`
```json
{
@@ -215,7 +215,7 @@ nitro.secure.master_key=change-me-to-a-long-random-secret
"secureAssetsEnabled": true,
"secureApiEnabled": true,
"apiBaseUrl": "https://nitro.example.com:2096",
- "plainConfigBaseUrl": "https://hotel.example.com/",
+ "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
}
```
@@ -232,7 +232,7 @@ nitro.secure.master_key=a-long-random-secret
### `.dat` only, no secure assets/API
-`client-mode.json`
+`configuration/client-mode.json`
```json
{
@@ -240,7 +240,7 @@ nitro.secure.master_key=a-long-random-secret
"secureAssetsEnabled": false,
"secureApiEnabled": false,
"apiBaseUrl": "https://nitro.example.com:2096",
- "plainConfigBaseUrl": "https://hotel.example.com/",
+ "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
}
```
@@ -254,7 +254,7 @@ nitro.secure.api.enabled=false
### Everything plain
-`client-mode.json`
+`configuration/client-mode.json`
```json
{
@@ -262,7 +262,7 @@ nitro.secure.api.enabled=false
"secureAssetsEnabled": false,
"secureApiEnabled": false,
"apiBaseUrl": "https://nitro.example.com:2096",
- "plainConfigBaseUrl": "https://hotel.example.com/",
+ "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
}
```
@@ -273,9 +273,9 @@ nitro.secure.api.enabled=false
For changes to:
-- `client-mode.json`
-- `renderer-config.json`
-- `ui-config.json`
+- `configuration/client-mode.json`
+- `configuration/renderer-config.json`
+- `configuration/ui-config.json`
- live gamedata
- `config.ini`
@@ -298,10 +298,12 @@ To make the toggles work properly:
## 12. Quick checklist
-- `client-mode.json` configured
+- `configuration/client-mode.json` configured
- `apiBaseUrl` correct
- `nitro.secure.master_key` set
- `nitro.secure.config.root` correct
- `nitro.secure.gamedata.root` correct
- both `.dat` and plain files deployed
- `.dat` MIME type configured on the web server
+
+
diff --git a/docs/secure-runtime-modes.html b/docs/secure-runtime-modes.html
new file mode 100644
index 0000000..d54c635
--- /dev/null
+++ b/docs/secure-runtime-modes.html
@@ -0,0 +1,236 @@
+
+
+
+
+
+ Nitro Secure Runtime Modes
+
+
+
+
+
+
+ Nitro V3
+ Secure Runtime
+
+
Documentazione configurazione runtime
+
+ Questa pagina riassume in modo ordinato come configurare i toggle runtime, i file example e i parametri lato client / emulatore
+ senza sporcare i componenti in src.
+
+
+
+
+
+
+
+
+ Overview
+
+
+
Dist Obfuscation
+
Sceglie se caricare app.js/app.css oppure .dat.
+
+
+
Secure Assets
+
Controlla se renderer-config, ui-config e gamedata passano da /nitro-sec/file.
+
+
+
Secure API
+
Attiva o disattiva la cifratura runtime automatica su /api/*.
+
+
+
+
+
+ File da usare
+
+
+
+
+ | File |
+ Scopo |
+ Nota |
+
+
+
+
+ public/configuration/client-mode.example |
+ Template per i toggle runtime |
+ Da copiare in configuration/client-mode.json nel deploy reale, che resta ignorato da Git |
+
+
+ public/configuration/renderer-config.example |
+ Template sicuro del renderer config |
+ Non tocca il tuo configuration/renderer-config.json locale |
+
+
+ public/configuration/ui-config.example |
+ Template UI config |
+ Da mantenere come riferimento pulito |
+
+
+ Latest_Compiled_Version/config.ini.example |
+ Flag backend secure |
+ Specifica la parte lato emulatore |
+
+
+
+
+
+
+
+ client-mode.example
+ È il punto centrale per attivare o disattivare il comportamento runtime senza dover modificare il codice.
+{
+ "distObfuscationEnabled": true,
+ "secureAssetsEnabled": true,
+ "secureApiEnabled": true,
+ "apiBaseUrl": "https://nitro.example.com:2096",
+ "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
+ "plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
+}
+
+
+
Campi
+
+ distObfuscationEnabled: usa .dat oppure file plain
+ secureAssetsEnabled: attiva /nitro-sec/file
+ secureApiEnabled: cifra le richieste /api/*
+ apiBaseUrl: base URL emulatore/API
+
+
+
+
Suggerimento
+
Conviene impostare sempre apiBaseUrl in modo esplicito, così non dipendi da fallback impliciti del runtime.
+
+
+
+
+
+ renderer-config.example
+ Qui definisci URL di socket, API, asset library e gamedata. Tutti i link pubblici dovrebbero vivere qui, non nei componenti React.
+
+
+
Chiavi principali
+
+ socket.url
+ api.url
+ asset.url
+ image.library.url
+ images.url
+ gamedata.url
+
+
+
+
Traduzioni
+
+ external.texts.translation.url
+ furnidata.translation.url
+ - Usano
%locale% e %timestamp%
+
+
+
+
+
+
+ ui-config.example
+ Per la login view e altre immagini UI, la sorgente deve stare qui o in renderer config, non hardcoded nei componenti.
+
+
Login view
+
+ loginview.images.background
+ loginview.images.drape
+ loginview.images.left
+ loginview.images.right
+ loginview.widgets per i blocchi promozionali
+
+
+
+
+
+ Codice runtime coinvolto
+
+
+
src/bootstrap.ts
+
Legge client-mode, costruisce NitroConfig['config.urls'] e prepara il bootstrap del client.
+
+
+
src/secure-assets.ts
+
Gestisce ECDH, decrypt/encrypt, fallback plain e secure API runtime.
+
+
+
scripts/write-asset-loader.mjs
+
Genera public/configuration/asset-loader.js e decide se usare file plain o .dat.
+
+
+
scripts/minify-dist.mjs
+
Genera i .dat ma mantiene anche i file plain per il toggle runtime.
+
+
+
+
+
+ Emulatore
+nitro.secure.assets.enabled=true
+nitro.secure.api.enabled=true
+nitro.secure.config.root=C:/path/to/Nitro-V3/public
+nitro.secure.gamedata.root=C:/path/to/gamedata
+nitro.secure.master_key=change-me-to-a-long-random-secret
+
+ nitro.secure.assets.enabled: abilita /nitro-sec/bootstrap e /nitro-sec/file
+ nitro.secure.api.enabled: abilita la cifratura su /api/*
+ nitro.secure.config.root: cartella dei config live
+ nitro.secure.gamedata.root: cartella del gamedata live
+ nitro.secure.master_key: chiave persistente server-side
+
+
+
+
+ Scenari rapidi
+
+
+
Tutto attivo
+
Secure assets, secure API e dist obfuscation tutti attivi.
+
+
+
Solo .dat
+
Usi i .dat, ma lasci config/API in plain.
+
+
+
Tutto plain
+
Modalità fallback completa per debug o test locali.
+
+
+
+
+
+ Checklist finale
+
+
Hai creato i file reali partendo da client-mode.example, renderer-config.example e ui-config.example
+
Gli URL pubblici stanno nei file config, non nei componenti React
+
Hai deployato sia i file plain sia i .dat
+
Il server espone correttamente il MIME type per .dat
+
Hai impostato nitro.secure.master_key lato emulatore
+
+
+
+
+
+
+
+
+
diff --git a/docs/secure-runtime-modes.md b/docs/secure-runtime-modes.md
index 3b59b39..4a9d311 100644
--- a/docs/secure-runtime-modes.md
+++ b/docs/secure-runtime-modes.md
@@ -3,11 +3,11 @@
Questa doc riassume tutti i dati da impostare per:
- offuscamento bundle `dist` (`app.js` / `app.css` → `.dat`)
-- secure assets runtime (`renderer-config.json`, `ui-config.json`, `gamedata`)
+- secure assets runtime (`configuration/renderer-config.json`, `configuration/ui-config.json`, `gamedata`)
- secure API runtime (`/api/*`)
- fallback plain quando vuoi spegnere tutto senza togliere il codice
-## 1. `Nitro-V3/public/client-mode.json`
+## 1. `Nitro-V3/public/configuration/client-mode.json`
Questo file controlla tutto a runtime.
@@ -17,7 +17,7 @@ Questo file controlla tutto a runtime.
"secureAssetsEnabled": true,
"secureApiEnabled": true,
"apiBaseUrl": "https://nitro.example.com:2096",
- "plainConfigBaseUrl": "https://hotel.example.com/",
+ "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
}
```
@@ -30,7 +30,7 @@ Questo file controlla tutto a runtime.
- `secureAssetsEnabled`
- `true`: `bootstrap.ts` e `secure-assets.ts` usano `/nitro-sec/file`
- - `false`: `renderer-config.json`, `ui-config.json` e gamedata vengono letti in plain
+ - `false`: `configuration/renderer-config.json`, `configuration/ui-config.json` e gamedata vengono letti in plain
- `secureApiEnabled`
- `true`: il wrapper `fetch` cifra le chiamate `/api/*`
@@ -43,7 +43,7 @@ Questo file controlla tutto a runtime.
- `plainConfigBaseUrl`
- base URL dei file config plain
- - normalmente: `https://hotel.example.com/`
+ - normalmente: `https://hotel.example.com/configuration/`
- `plainGamedataBaseUrl`
- base URL del gamedata plain
@@ -74,7 +74,7 @@ Il fallback attuale è:
(window as any).NitroSecureApiUrl = clientMode.apiBaseUrl || 'https://nitro.example.com:2096/';
```
-Quindi in produzione conviene sempre valorizzare `apiBaseUrl` dentro `client-mode.json`.
+Quindi in produzione conviene sempre valorizzare `apiBaseUrl` dentro `configuration/client-mode.json`.
## 3. `Nitro-V3/src/secure-assets.ts`
@@ -95,7 +95,7 @@ Qui vive tutta la logica runtime:
Normalmente non serve toccarlo, a meno che tu non voglia cambiare il protocollo secure.
-## 4. `Nitro-V3/public/renderer-config.json`
+## 4. `Nitro-V3/public/configuration/renderer-config.json`
Questo file continua a definire i path usati dal renderer.
@@ -129,7 +129,7 @@ Conviene usare i path plain classici, per esempio:
oppure lasciare il renderer configurato com’è e demandare il fallback a `secure-assets.ts`.
-## 5. `Nitro-V3/public/ui-config.json`
+## 5. `Nitro-V3/public/configuration/ui-config.json`
Qui non c’è logica secure, ma è uno dei file caricati da `config.urls`.
@@ -140,12 +140,12 @@ Quindi basta mantenerlo corretto come contenuto, non serve altro.
## 6. `Nitro-V3/scripts/write-asset-loader.mjs`
-Questo script genera `public/asset-loader.js`.
+Questo script genera `public/configuration/asset-loader.js`.
### Cosa fa ora
- mostra la shell iniziale
-- legge `client-mode.json`
+- legge `configuration/client-mode.json`
- decide se caricare:
- `app.css.dat` / `app.js.dat`
- oppure `assets/app.css` / `assets/app.js`
@@ -194,7 +194,7 @@ nitro.secure.master_key=change-me-to-a-long-random-secret
- abilita il layer secure per `/api/*`
- `nitro.secure.config.root`
- - cartella dove leggere `renderer-config.json` e `ui-config.json`
+ - cartella dove leggere `configuration/renderer-config.json` e `configuration/ui-config.json`
- `nitro.secure.gamedata.root`
- cartella dove leggere il gamedata live
@@ -207,7 +207,7 @@ nitro.secure.master_key=change-me-to-a-long-random-secret
### Tutto attivo
-`client-mode.json`
+`configuration/client-mode.json`
```json
{
@@ -215,7 +215,7 @@ nitro.secure.master_key=change-me-to-a-long-random-secret
"secureAssetsEnabled": true,
"secureApiEnabled": true,
"apiBaseUrl": "https://nitro.example.com:2096",
- "plainConfigBaseUrl": "https://hotel.example.com/",
+ "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
}
```
@@ -232,7 +232,7 @@ nitro.secure.master_key=una-chiave-lunga-random
### Solo `.dat`, senza secure assets/api
-`client-mode.json`
+`configuration/client-mode.json`
```json
{
@@ -240,7 +240,7 @@ nitro.secure.master_key=una-chiave-lunga-random
"secureAssetsEnabled": false,
"secureApiEnabled": false,
"apiBaseUrl": "https://nitro.example.com:2096",
- "plainConfigBaseUrl": "https://hotel.example.com/",
+ "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
}
```
@@ -254,7 +254,7 @@ nitro.secure.api.enabled=false
### Tutto plain
-`client-mode.json`
+`configuration/client-mode.json`
```json
{
@@ -262,7 +262,7 @@ nitro.secure.api.enabled=false
"secureAssetsEnabled": false,
"secureApiEnabled": false,
"apiBaseUrl": "https://nitro.example.com:2096",
- "plainConfigBaseUrl": "https://hotel.example.com/",
+ "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
}
```
@@ -273,9 +273,9 @@ nitro.secure.api.enabled=false
Per cambiare:
-- `client-mode.json`
-- `renderer-config.json`
-- `ui-config.json`
+- `configuration/client-mode.json`
+- `configuration/renderer-config.json`
+- `configuration/ui-config.json`
- gamedata live
- `config.ini`
@@ -298,10 +298,12 @@ Per usare bene i toggle:
## 12. Checklist veloce
-- `client-mode.json` configurato
+- `configuration/client-mode.json` configurato
- `apiBaseUrl` corretto
- `nitro.secure.master_key` valorizzata
- `nitro.secure.config.root` corretto
- `nitro.secure.gamedata.root` corretto
- `.dat` e file plain entrambi deployati
- MIME `.dat` presente sul web server
+
+
diff --git a/localization/badge-texts-en.json b/localization/badge-texts-en.json
deleted file mode 100644
index a8ab19a..0000000
--- a/localization/badge-texts-en.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "notification.badge.received": "New Badge!"
-}
diff --git a/localization/badge-texts-it.json b/localization/badge-texts-it.json
deleted file mode 100644
index 10c1271..0000000
--- a/localization/badge-texts-it.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "notification.badge.received": "Nuovo Distintivo!"
-}
diff --git a/public/UITexts.example b/public/UITexts.example
deleted file mode 100644
index bb8779b..0000000
--- a/public/UITexts.example
+++ /dev/null
@@ -1,113 +0,0 @@
-{
- "friendlist.search": "Search friends",
- "purse.seasonal.currency.101": "cash",
- "widget.chooser.checkall": "Select furniture",
- "widget.chooser.btn.pickall": "pick up selected items!",
- "wiredfurni.params.requireall.2": "If one of the selected furni has an avatar",
- "wiredfurni.params.requireall.3": "If all selected furni have avatars on them",
- "widget.settings.general": "General",
- "widget.settings.general.title": "Adjust the default Nitro settings",
- "widget.settings.volume": "Volume",
- "widget.settings.interface": "Interface",
- "widget.settings.interface.title": "Adjust the interface settings",
- "widget.settings.interface.fps.automatic": "Set FPS to unlimited",
- "widget.settings.interface.fps.warning": "Setting FPS to unlimited may cause performance issues!",
- "widget.settings.interface.secondary": "Change the window header color",
- "widget.settings.interface.reset": "Reset header color to default",
- "widget.room.chat.hide_pets": "Hide pets",
- "widget.room.chat.hide_avatars": "Hide avatars",
- "widget.room.chat.hide_balloon": "Hide speech bubble",
- "widget.room.chat.show_balloon": "Speech bubble",
- "widget.room.chat.clear_history": "clear history",
- "widget.room.youtube.shared": "YouTube is being shared",
- "widget.room.youtube.open_video": "Open the video",
- "wiredfurni.tooltip.select.tile": "Select tile",
- "wiredfurni.tooltip.remove.tile": "Deselect tile",
- "wiredfurni.tooltip.remove.5x5_tile": "select 5x5 tiles",
- "wiredfurni.tooltip.remove.clear_tile": "Clear all selections",
- "wiredfurni.params.furni_neighborhood.group.user": "Players",
- "wiredfurni.params.furni_neighborhood.group.furni": "Furniture",
- "wiredfurni.params.selector_option.bot": "No bots",
- "wiredfurni.params.selector_option.pet": "No pets",
- "catalog.title": "Catalog",
- "catalog.favorites": "Favorites",
- "catalog.favorites.pages": "Pages",
- "catalog.favorites.furni": "Furni",
- "catalog.favorites.empty": "No favorites",
- "catalog.favorites.empty.hint": "Click the heart on furni or the star on pages to add them.",
- "catalog.admin": "Admin",
- "catalog.admin.new": "New",
- "catalog.admin.root": "Root",
- "catalog.admin.new.root.category": "New root category",
- "catalog.admin.edit.root": "Edit Root",
- "catalog.admin.edit": "Edit:",
- "catalog.admin.edit.page": "Edit Page",
- "catalog.admin.hidden": "hidden",
- "catalog.admin.edit.title": "Edit \"%name%\"",
- "catalog.admin.show": "Show",
- "catalog.admin.hide": "Hide",
- "catalog.admin.delete": "Delete",
- "catalog.admin.delete.title": "Delete \"%name%\"",
- "catalog.admin.delete.category.confirm": "Delete category \"%name%\" and all its content?",
- "catalog.admin.delete.page": "Delete page",
- "catalog.admin.delete.page.confirm": "Delete page \"%name%\"?",
- "catalog.admin.delete.offer.confirm": "Are you sure you want to delete this offer?",
- "catalog.admin.create": "Create",
- "catalog.admin.save": "Save",
- "catalog.admin.create.subpage": "Create sub-page",
- "catalog.admin.order": "Order",
- "catalog.admin.visible": "Visible",
- "catalog.admin.enabled": "Enabled",
- "catalog.admin.offer.new": "New Offer",
- "catalog.admin.offer.edit": "Edit Offer",
- "catalog.admin.offer.name": "Catalog Name",
- "catalog.admin.offer.general": "General",
- "catalog.admin.offer.quantity": "Quantity",
- "catalog.admin.offer.prices": "Prices",
- "catalog.admin.offer.credits": "Credits",
- "catalog.admin.offer.points": "Points",
- "catalog.admin.offer.points.type": "Points Type",
- "catalog.admin.offer.options": "Options",
- "catalog.admin.offer.club.only": "Club Only",
- "catalog.admin.offer.extradata": "Extra Data (optional)....",
- "catalog.admin.offer.have.offer": "Multi-discount (have_offer)",
- "catalog.trophies.title": "Trophies",
- "catalog.trophies.write.hint": "Write a text for the trophy before purchasing",
- "catalog.trophies.inscription": "Trophy Inscription",
- "catalog.trophies.inscription.placeholder": "Write the text that will appear on the trophy...",
- "catalog.pets.show.colors": "Show colors",
- "catalog.pets.choose.color": "Choose color",
- "catalog.pets.choose.breed": "Choose breed",
- "catalog.pets.back.breeds": "← Breeds",
- "catalog.prefix.text": "Text",
- "catalog.prefix.text.placeholder": "Enter text...",
- "catalog.prefix.icon": "Icon",
- "catalog.prefix.icon.remove": "Remove icon",
- "catalog.prefix.effect": "Effect",
- "catalog.prefix.color": "Color",
- "catalog.prefix.color.single": "🎨 Single",
- "catalog.prefix.color.per.letter": "🌈 Per Letter",
- "catalog.prefix.color.hint": "Select a letter, then choose the color. Auto-advances.",
- "catalog.prefix.color.apply.all.title": "Apply current color to all letters",
- "catalog.prefix.color.apply.all": "Apply to all",
- "catalog.prefix.color.selected": "Selected letter:",
- "catalog.prefix.price": "Price:",
- "catalog.prefix.price.amount": "5 Credits",
- "catalog.prefix.purchased": "✓ Purchased!",
- "catalog.prefix.purchase": "Purchase",
- "groupforum.list.tab.most_active": "Most active threads",
- "groupforum.list.tab.my_forums": "My group forums",
- "groupforum.list.no_forums": "There are no forums",
- "groupforum.view.threads": "Number of threads",
- "groupforum.thread.pin": "Pin thread",
- "groupforum.thread.unpin": "Unpin thread",
- "groupforum.thread.lock": "Lock thread",
- "groupforum.thread.unlock": "Unlock thread",
- "groupforum.thread.hide": "Hide thread",
- "groupforum.thread.restore": "Restore thread",
- "groupforum.thread.delete": "Delete thread + posts",
- "groupforum.message.hide": "Hide message",
- "group.forum.enable.caption": "Enable / Disable group forum",
- "group.forum.enable.help": "If you disable the group forum, all posts will also be deleted!",
- "groupforum.view.no_threads": "There are currently no active threads"
-}
\ No newline at end of file
diff --git a/public/client-mode.json b/public/client-mode.json
deleted file mode 100644
index a738f14..0000000
--- a/public/client-mode.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "distObfuscationEnabled": true,
- "secureAssetsEnabled": true,
- "secureApiEnabled": true,
- "apiBaseUrl": "",
- "plainConfigBaseUrl": "",
- "plainGamedataBaseUrl": ""
-}
diff --git a/public/configuration/UITexts.example b/public/configuration/UITexts.example
new file mode 100644
index 0000000..acf246e
--- /dev/null
+++ b/public/configuration/UITexts.example
@@ -0,0 +1,116 @@
+{
+ "notification.badge.received": "Nuovo Distintivo!",
+ "wiredfurni.badgereceived.title": "Distintivo ricevuto!",
+ "wiredfurni.badgereceived.body": "Hai appena ricevuto un nuovo Distintivo! Controlla nel tuo Inventario!",
+ "friendlist.search": "Search friends",
+ "purse.seasonal.currency.101": "cash",
+ "widget.chooser.checkall": "Select furniture",
+ "widget.chooser.btn.pickall": "pick up selected items!",
+ "wiredfurni.params.requireall.2": "If one of the selected furni has an avatar",
+ "wiredfurni.params.requireall.3": "If all selected furni have avatars on them",
+ "widget.settings.general": "General",
+ "widget.settings.general.title": "Adjust the default Nitro settings",
+ "widget.settings.volume": "Volume",
+ "widget.settings.interface": "Interface",
+ "widget.settings.interface.title": "Adjust the interface settings",
+ "widget.settings.interface.fps.automatic": "Set FPS to unlimited",
+ "widget.settings.interface.fps.warning": "Setting FPS to unlimited may cause performance issues!",
+ "widget.settings.interface.secondary": "Change the window header color",
+ "widget.settings.interface.reset": "Reset header color to default",
+ "widget.room.chat.hide_pets": "Hide pets",
+ "widget.room.chat.hide_avatars": "Hide avatars",
+ "widget.room.chat.hide_balloon": "Hide speech bubble",
+ "widget.room.chat.show_balloon": "Speech bubble",
+ "widget.room.chat.clear_history": "clear history",
+ "widget.room.youtube.shared": "YouTube is being shared",
+ "widget.room.youtube.open_video": "Open the video",
+ "wiredfurni.tooltip.select.tile": "Select tile",
+ "wiredfurni.tooltip.remove.tile": "Deselect tile",
+ "wiredfurni.tooltip.remove.5x5_tile": "select 5x5 tiles",
+ "wiredfurni.tooltip.remove.clear_tile": "Clear all selections",
+ "wiredfurni.params.furni_neighborhood.group.user": "Players",
+ "wiredfurni.params.furni_neighborhood.group.furni": "Furniture",
+ "wiredfurni.params.selector_option.bot": "No bots",
+ "wiredfurni.params.selector_option.pet": "No pets",
+ "catalog.title": "Catalog",
+ "catalog.favorites": "Favorites",
+ "catalog.favorites.pages": "Pages",
+ "catalog.favorites.furni": "Furni",
+ "catalog.favorites.empty": "No favorites",
+ "catalog.favorites.empty.hint": "Click the heart on furni or the star on pages to add them.",
+ "catalog.admin": "Admin",
+ "catalog.admin.new": "New",
+ "catalog.admin.root": "Root",
+ "catalog.admin.new.root.category": "New root category",
+ "catalog.admin.edit.root": "Edit Root",
+ "catalog.admin.edit": "Edit:",
+ "catalog.admin.edit.page": "Edit Page",
+ "catalog.admin.hidden": "hidden",
+ "catalog.admin.edit.title": "Edit \"%name%\"",
+ "catalog.admin.show": "Show",
+ "catalog.admin.hide": "Hide",
+ "catalog.admin.delete": "Delete",
+ "catalog.admin.delete.title": "Delete \"%name%\"",
+ "catalog.admin.delete.category.confirm": "Delete category \"%name%\" and all its content?",
+ "catalog.admin.delete.page": "Delete page",
+ "catalog.admin.delete.page.confirm": "Delete page \"%name%\"?",
+ "catalog.admin.delete.offer.confirm": "Are you sure you want to delete this offer?",
+ "catalog.admin.create": "Create",
+ "catalog.admin.save": "Save",
+ "catalog.admin.create.subpage": "Create sub-page",
+ "catalog.admin.order": "Order",
+ "catalog.admin.visible": "Visible",
+ "catalog.admin.enabled": "Enabled",
+ "catalog.admin.offer.new": "New Offer",
+ "catalog.admin.offer.edit": "Edit Offer",
+ "catalog.admin.offer.name": "Catalog Name",
+ "catalog.admin.offer.general": "General",
+ "catalog.admin.offer.quantity": "Quantity",
+ "catalog.admin.offer.prices": "Prices",
+ "catalog.admin.offer.credits": "Credits",
+ "catalog.admin.offer.points": "Points",
+ "catalog.admin.offer.points.type": "Points Type",
+ "catalog.admin.offer.options": "Options",
+ "catalog.admin.offer.club.only": "Club Only",
+ "catalog.admin.offer.extradata": "Extra Data (optional)....",
+ "catalog.admin.offer.have.offer": "Multi-discount (have_offer)",
+ "catalog.trophies.title": "Trophies",
+ "catalog.trophies.write.hint": "Write a text for the trophy before purchasing",
+ "catalog.trophies.inscription": "Trophy Inscription",
+ "catalog.trophies.inscription.placeholder": "Write the text that will appear on the trophy...",
+ "catalog.pets.show.colors": "Show colors",
+ "catalog.pets.choose.color": "Choose color",
+ "catalog.pets.choose.breed": "Choose breed",
+ "catalog.pets.back.breeds": "? Breeds",
+ "catalog.prefix.text": "Text",
+ "catalog.prefix.text.placeholder": "Enter text...",
+ "catalog.prefix.icon": "Icon",
+ "catalog.prefix.icon.remove": "Remove icon",
+ "catalog.prefix.effect": "Effect",
+ "catalog.prefix.color": "Color",
+ "catalog.prefix.color.single": "?? Single",
+ "catalog.prefix.color.per.letter": "?? Per Letter",
+ "catalog.prefix.color.hint": "Select a letter, then choose the color. Auto-advances.",
+ "catalog.prefix.color.apply.all.title": "Apply current color to all letters",
+ "catalog.prefix.color.apply.all": "Apply to all",
+ "catalog.prefix.color.selected": "Selected letter:",
+ "catalog.prefix.price": "Price:",
+ "catalog.prefix.price.amount": "5 Credits",
+ "catalog.prefix.purchased": "? Purchased!",
+ "catalog.prefix.purchase": "Purchase",
+ "groupforum.list.tab.most_active": "Most active threads",
+ "groupforum.list.tab.my_forums": "My group forums",
+ "groupforum.list.no_forums": "There are no forums",
+ "groupforum.view.threads": "Number of threads",
+ "groupforum.thread.pin": "Pin thread",
+ "groupforum.thread.unpin": "Unpin thread",
+ "groupforum.thread.lock": "Lock thread",
+ "groupforum.thread.unlock": "Unlock thread",
+ "groupforum.thread.hide": "Hide thread",
+ "groupforum.thread.restore": "Restore thread",
+ "groupforum.thread.delete": "Delete thread + posts",
+ "groupforum.message.hide": "Hide message",
+ "group.forum.enable.caption": "Enable / Disable group forum",
+ "group.forum.enable.help": "If you disable the group forum, all posts will also be deleted!",
+ "groupforum.view.no_threads": "There are currently no active threads"
+}
diff --git a/public/adsense.json b/public/configuration/adsense.example
similarity index 100%
rename from public/adsense.json
rename to public/configuration/adsense.example
diff --git a/public/asset-loader.js b/public/configuration/asset-loader.js
similarity index 97%
rename from public/asset-loader.js
rename to public/configuration/asset-loader.js
index f64cbe6..c1cfde3 100644
--- a/public/asset-loader.js
+++ b/public/configuration/asset-loader.js
@@ -145,6 +145,10 @@
const readClientMode = async () => {
try {
+ if(window.__nitroClientMode && typeof window.__nitroClientMode === "object") {
+ debug("loader: client-mode preset");
+ return window.__nitroClientMode;
+ }
const url = withCacheBust(new URL("./client-mode.json", getBase()));
const response = await fetch(url, { cache: "no-store" });
if(!response.ok) throw new Error("client-mode " + response.status);
diff --git a/public/configuration/bootstrap.js b/public/configuration/bootstrap.js
new file mode 100644
index 0000000..f2a9d7e
--- /dev/null
+++ b/public/configuration/bootstrap.js
@@ -0,0 +1,133 @@
+(() => {
+ const API_BASE = "https://nitro.slogga.it:2096";
+
+ const getBase = () => {
+ const source = document.currentScript?.src || location.href;
+ return new URL(".", source);
+ };
+
+ const withCacheBust = (url) => {
+ url.searchParams.set("v", Date.now().toString(36));
+ return url;
+ };
+
+ const bytesToBase64 = (buffer) => {
+ let binary = "";
+ const bytes = new Uint8Array(buffer);
+ for(let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
+ return btoa(binary);
+ };
+
+ const hexValue = (code) => {
+ if(code >= 48 && code <= 57) return code - 48;
+ if(code >= 65 && code <= 70) return code - 55;
+ if(code >= 97 && code <= 102) return code - 87;
+ return -1;
+ };
+
+ const hexToBytes = (hex) => {
+ const normalized = hex.trim();
+ if((normalized.length % 2) !== 0) throw new Error("Invalid encrypted hex payload.");
+ const bytes = new Uint8Array(normalized.length / 2);
+ for(let i = 0; i < bytes.length; i++) {
+ const high = hexValue(normalized.charCodeAt(i * 2));
+ const low = hexValue(normalized.charCodeAt((i * 2) + 1));
+ if(high < 0 || low < 0) throw new Error("Invalid encrypted hex payload.");
+ bytes[i] = (high << 4) | low;
+ }
+ return bytes;
+ };
+
+ const deriveAesKey = async (privateKey, serverKeyBase64) => {
+ const serverBytes = Uint8Array.from(atob(serverKeyBase64), char => char.charCodeAt(0));
+ const serverKey = await crypto.subtle.importKey("spki", serverBytes, { name: "ECDH", namedCurve: "P-256" }, false, []);
+ const secret = await crypto.subtle.deriveBits({ name: "ECDH", public: serverKey }, privateKey, 256);
+ const salt = new TextEncoder().encode("nitro-secure-assets-v1");
+ const material = new Uint8Array(secret.byteLength + salt.length);
+ material.set(new Uint8Array(secret), 0);
+ material.set(salt, secret.byteLength);
+ const hash = await crypto.subtle.digest("SHA-256", material);
+ return crypto.subtle.importKey("raw", hash, "AES-GCM", false, ["decrypt"]);
+ };
+
+ const decryptPayload = async (key, response) => {
+ if(response.headers.get("X-Nitro-Sec") !== "1") return response.text();
+ const bytes = hexToBytes(await response.text());
+ if(bytes.length < 13) throw new Error("Encrypted response is too short.");
+ const iv = bytes.slice(0, 12);
+ const payload = bytes.slice(12);
+ const clear = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, payload);
+ return new TextDecoder().decode(clear);
+ };
+
+ const importTextModule = async (sourceText) => {
+ const blobUrl = URL.createObjectURL(new Blob([sourceText], { type: "text/javascript" }));
+ try {
+ await import(blobUrl);
+ } finally {
+ URL.revokeObjectURL(blobUrl);
+ }
+ };
+
+ const loadPlainBootstrap = async () => {
+ const url = withCacheBust(new URL("./asset-loader.js", getBase()));
+ await import(url.href);
+ };
+
+ const loadSecureBootstrap = async () => {
+ if(!API_BASE) 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 bootstrapResponse = await fetch(base + "/nitro-sec/bootstrap", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ key: publicKey })
+ });
+
+ if(!bootstrapResponse.ok) throw new Error("Secure bootstrap failed: HTTP " + bootstrapResponse.status);
+
+ const bootstrapPayload = await bootstrapResponse.json();
+ if(!bootstrapPayload || typeof bootstrapPayload.key !== "string" || !bootstrapPayload.key.length) {
+ throw new Error("Secure bootstrap returned an invalid server key.");
+ }
+
+ const sessionKey = await deriveAesKey(pair.privateKey, bootstrapPayload.key);
+
+ const fetchSecureConfig = async (file) => {
+ const url = new URL(base + "/nitro-sec/file");
+ url.searchParams.set("kind", "config");
+ url.searchParams.set("file", file);
+ url.searchParams.set("v", Date.now().toString(36));
+
+ const response = await fetch(url.toString(), {
+ headers: { "X-Nitro-Key": publicKey },
+ cache: "no-store"
+ });
+
+ if(!response.ok) throw new Error("Failed to load secure config " + file + ": HTTP " + response.status);
+
+ return decryptPayload(sessionKey, response);
+ };
+
+ const modeText = await fetchSecureConfig("client-mode.json");
+ window.__nitroClientMode = JSON.parse(modeText);
+
+ const loaderText = await fetchSecureConfig("asset-loader.js");
+ await importTextModule(loaderText);
+ };
+
+ (async () => {
+ try {
+ await loadSecureBootstrap();
+ } 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.";
+ });
+})();
\ No newline at end of file
diff --git a/public/client-mode.example.json b/public/configuration/client-mode.example
similarity index 76%
rename from public/client-mode.example.json
rename to public/configuration/client-mode.example
index 4582313..a6e11ed 100644
--- a/public/client-mode.example.json
+++ b/public/configuration/client-mode.example
@@ -3,6 +3,6 @@
"secureAssetsEnabled": true,
"secureApiEnabled": true,
"apiBaseUrl": "https://nitro.example.com:2096",
- "plainConfigBaseUrl": "https://hotel.example.com/",
+ "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
"plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
}
diff --git a/public/hotlooks.json b/public/configuration/hotlooks.example
similarity index 100%
rename from public/hotlooks.json
rename to public/configuration/hotlooks.example
diff --git a/public/renderer-config.example b/public/configuration/renderer-config.example
similarity index 100%
rename from public/renderer-config.example
rename to public/configuration/renderer-config.example
diff --git a/public/ui-config.example b/public/configuration/ui-config.example
similarity index 100%
rename from public/ui-config.example
rename to public/configuration/ui-config.example
diff --git a/public/renderer-config.json b/public/renderer-config.json
deleted file mode 100644
index 6b769d7..0000000
--- a/public/renderer-config.json
+++ /dev/null
@@ -1,598 +0,0 @@
-{
- "socket.url": "wss://nitro.example.com:2096",
- "api.url": "https://nitro.example.com:2096",
- "asset.url": "https://hotel.example.com/client/nitro/bundled",
- "image.library.url": "https://hotel.example.com/client/c_images/",
- "hof.furni.url": "https://hotel.example.com/client/c_images/dcr/hof_furni",
- "images.url": "https://hotel.example.com/client/nitro/images",
- "gamedata.url": "https://nitro.example.com:2096/nitro-sec/file?kind=gamedata&file=",
- "sounds.url": "${asset.url}/sounds/%sample%.mp3",
- "external.texts.url": [
- "${gamedata.url}/ExternalTexts.json",
- "${gamedata.url}/UITexts.json"
- ],
- "external.texts.translation.url": "${gamedata.url}/text_translate/ExternalTexts_%locale%.json?t=%timestamp%",
- "external.samples.url": "${hof.furni.url}/mp3/sound_machine_sample_%sample%.mp3",
- "furnidata.url": "${gamedata.url}/FurnitureData.json?t=%timestamp%",
- "furnidata.translation.url": "${gamedata.url}/furniture_translate/FurnitureData_%locale%.json?t=%timestamp%",
- "productdata.url": "${gamedata.url}/ProductData.json?t=%timestamp%",
- "avatar.actions.url": "${gamedata.url}/HabboAvatarActions.json?t=%timestamp%",
- "avatar.figuredata.url": "${gamedata.url}/FigureData.json?t=%timestamp%",
- "avatar.figuremap.url": "${gamedata.url}/FigureMap.json?t=%timestamp%",
- "avatar.effectmap.url": "${gamedata.url}/EffectMap.json?t=%timestamp%",
- "avatar.asset.url": "${asset.url}/figure/%libname%.nitro",
- "avatar.asset.effect.url": "${asset.url}/effect/%libname%.nitro",
- "furni.asset.url": "${asset.url}/furniture/%libname%.nitro",
- "furni.asset.icon.url": "${hof.furni.url}/icons/%libname%%param%_icon.png",
- "pet.asset.url": "${asset.url}/pets/%libname%.nitro",
- "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,
- "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": [
- "bd:1",
- "li:0"
- ],
- "avatar.mandatory.effect.libraries": [
- "dance.1",
- "dance.2",
- "dance.3",
- "dance.4"
- ],
- "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": [
- "${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"
- ]
-}
diff --git a/public/ui-config.json b/public/ui-config.json
deleted file mode 100644
index f625755..0000000
--- a/public/ui-config.json
+++ /dev/null
@@ -1,2816 +0,0 @@
-{
- "image.library.notifications.url": "${image.library.url}notifications/%image%.png",
- "achievements.images.url": "${image.library.url}Quests/%image%.png",
- "camera.url": "https://hotel.example.com/client/camera/",
- "thumbnails.url": "https://hotel.example.com/client/camera/thumbnail/%thumbnail%.png",
- "url.prefix": "",
- "habbopages.url": "/gamedata/habbopages/",
- "group.homepage.url": "${url.prefix}/groups/%groupid%/id",
- "guide.help.alpha.groupid": 0,
- "chat.viewer.height.percentage": 0.4,
- "widget.dimmer.colorwheel": false,
- "avatar.wardrobe.max.slots": 10,
- "user.badges.max.slots": 5,
- "user.tags.enabled": false,
- "camera.publish.disabled": false,
- "hc.disabled": false,
- "badge.descriptions.enabled": true,
- "motto.max.length": 38,
- "bot.name.max.length": 15,
- "pet.package.name.max.length": 15,
- "wired.action.bot.talk.to.avatar.max.length": 64,
- "wired.action.bot.talk.max.length": 64,
- "wired.action.chat.max.length": 100,
- "wired.action.kick.from.room.max.length": 100,
- "wired.action.mute.user.max.length": 100,
- "game.center.enabled": false,
- "guides.enabled": true,
- "toolbar.hide.quests": true,
- "catalog.style.new": true,
- "show.google.ads": false,
- "loginview": {
- "images": {
- "background": "https://hotel.example.com/client/nitro/images/reception/background_gradient_apr25.png", "drape": "https://hotel.example.com/client/nitro/images/reception/drape.png",
- "left": "https://hotel.example.com/client/nitro/images/reception/mute_reception_backdrop_left.png",
- "right": "https://hotel.example.com/client/nitro/images/reception/background_right.png"
- }
- },
- "navigator.room.models": [
- {
- "clubLevel": 0,
- "tileSize": 104,
- "name": "a"
- },
- {
- "clubLevel": 0,
- "tileSize": 94,
- "name": "b"
- },
- {
- "clubLevel": 0,
- "tileSize": 36,
- "name": "c"
- },
- {
- "clubLevel": 0,
- "tileSize": 84,
- "name": "d"
- },
- {
- "clubLevel": 0,
- "tileSize": 80,
- "name": "e"
- },
- {
- "clubLevel": 0,
- "tileSize": 80,
- "name": "f"
- },
- {
- "clubLevel": 0,
- "tileSize": 416,
- "name": "i"
- },
- {
- "clubLevel": 0,
- "tileSize": 320,
- "name": "j"
- },
- {
- "clubLevel": 0,
- "tileSize": 448,
- "name": "k"
- },
- {
- "clubLevel": 0,
- "tileSize": 352,
- "name": "l"
- },
- {
- "clubLevel": 0,
- "tileSize": 384,
- "name": "m"
- },
- {
- "clubLevel": 0,
- "tileSize": 372,
- "name": "n"
- },
- {
- "clubLevel": 1,
- "tileSize": 80,
- "name": "g"
- },
- {
- "clubLevel": 1,
- "tileSize": 74,
- "name": "h"
- },
- {
- "clubLevel": 1,
- "tileSize": 416,
- "name": "o"
- },
- {
- "clubLevel": 1,
- "tileSize": 352,
- "name": "p"
- },
- {
- "clubLevel": 1,
- "tileSize": 304,
- "name": "q"
- },
- {
- "clubLevel": 1,
- "tileSize": 336,
- "name": "r"
- },
- {
- "clubLevel": 1,
- "tileSize": 748,
- "name": "u"
- },
- {
- "clubLevel": 1,
- "tileSize": 438,
- "name": "v"
- },
- {
- "clubLevel": 2,
- "tileSize": 540,
- "name": "t"
- },
- {
- "clubLevel": 2,
- "tileSize": 512,
- "name": "w"
- },
- {
- "clubLevel": 2,
- "tileSize": 396,
- "name": "x"
- },
- {
- "clubLevel": 2,
- "tileSize": 440,
- "name": "y"
- },
- {
- "clubLevel": 2,
- "tileSize": 456,
- "name": "z"
- },
- {
- "clubLevel": 2,
- "tileSize": 208,
- "name": "0"
- },
- {
- "clubLevel": 2,
- "tileSize": 1009,
- "name": "1"
- },
- {
- "clubLevel": 2,
- "tileSize": 1044,
- "name": "2"
- },
- {
- "clubLevel": 2,
- "tileSize": 183,
- "name": "3"
- },
- {
- "clubLevel": 2,
- "tileSize": 254,
- "name": "4"
- },
- {
- "clubLevel": 2,
- "tileSize": 1024,
- "name": "5"
- },
- {
- "clubLevel": 2,
- "tileSize": 801,
- "name": "6"
- },
- {
- "clubLevel": 2,
- "tileSize": 354,
- "name": "7"
- },
- {
- "clubLevel": 2,
- "tileSize": 888,
- "name": "8"
- },
- {
- "clubLevel": 2,
- "tileSize": 926,
- "name": "9"
- }
- ],
- "backgrounds.data": [
- {
- "backgroundId": 0,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 1,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 2,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 3,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 4,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 5,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 6,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 7,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 8,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 9,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 10,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 11,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 12,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 13,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 14,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 15,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 16,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 17,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 18,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 19,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 20,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 21,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 22,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 23,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 24,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 25,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 26,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 27,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 28,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 29,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 30,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 31,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 32,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 33,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 34,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 35,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 36,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 37,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 38,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 39,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 40,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 41,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 42,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 43,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 44,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 45,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 46,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 47,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 48,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 49,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 50,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 51,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 52,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 53,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 54,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 55,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 56,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 57,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 58,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 59,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 60,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 61,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 62,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 63,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 64,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 65,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 66,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 67,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 68,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 69,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 70,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 71,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 72,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 73,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 74,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 75,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 76,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 77,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 78,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 79,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 80,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 81,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 82,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 83,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 84,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 85,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 86,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 87,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 88,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 89,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 90,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 91,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 92,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 93,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 94,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 95,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 96,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 97,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 98,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 99,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 100,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 101,
- "minRank": 2,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 102,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 103,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 104,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 105,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 106,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 107,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 108,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 109,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 110,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 111,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 112,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 113,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 114,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 115,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 116,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 117,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 118,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 119,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 120,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 121,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 122,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 123,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 124,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 125,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 126,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 127,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 128,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 129,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 130,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 131,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 132,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 133,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 134,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 135,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 136,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 137,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 138,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 139,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 140,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 141,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 142,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 143,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 144,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 145,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 146,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 147,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 148,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 149,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 150,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 151,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 152,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 153,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 154,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 155,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 156,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 157,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 158,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 159,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 160,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 161,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 162,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 163,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 164,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 165,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 166,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 167,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 168,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 169,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 170,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 171,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 172,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 173,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 174,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 175,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 176,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 177,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 178,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 179,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 180,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 181,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 182,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 183,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 184,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 185,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 186,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "backgroundId": 187,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- }
- ],
- "stands.data": [
- {
- "standId": 0,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 1,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 2,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 3,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 4,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 5,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 6,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 7,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 8,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 9,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 10,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 11,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 12,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 13,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 14,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 15,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "standId": 16,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "standId": 17,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "standId": 18,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "standId": 19,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "standId": 20,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "standId": 21,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- }
- ],
- "overlays.data": [
- {
- "overlayId": 0,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "overlayId": 1,
- "minRank": 0,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "overlayId": 2,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "overlayId": 3,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "overlayId": 4,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "overlayId": 5,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "overlayId": 6,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "overlayId": 7,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "overlayId": 8,
- "minRank": 0,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- }
- ],
- "hotelview": {
- "room.pool": "791",
- "room.picnic": "2193",
- "room.rooftop": "",
- "room.rooftop.pool": "",
- "room.peaceful": "",
- "room.infobus": "5956",
- "room.lobby": "1450",
- "show.avatar": true,
- "widgets": {
- "slot.1.widget": "promoarticle",
- "slot.1.conf": {},
- "slot.2.widget": "widgetcontainer",
- "slot.2.conf": {
- "image": "${image.library.url}web_promo_small/spromo_Canal_Bundle.png",
- "texts": "2021NitroPromo",
- "btnLink": ""
- },
- "slot.3.widget": "",
- "slot.3.conf": {},
- "slot.4.widget": "",
- "slot.4.conf": {},
- "slot.5.widget": "",
- "slot.5.conf": {},
- "slot.6.widget": "",
- "slot.6.conf": {
- "campaign": ""
- },
- "slot.7.widget": "",
- "slot.7.conf": {}
- },
- "images": {
- "background": "${asset.url}/images/reception/stretch_blue.png",
- "background.colour": "#8ee0f0",
- "sun": "${asset.url}/images/reception/sun.png",
- "drape": "${asset.url}/images/reception/drape.png",
- "left": "",
- "right": "",
- "right.repeat": ""
- }
- },
- "loginview": {
- "images": {
- "background": "https://hotel.example.com/client/nitro/images/reception/background_gradient_apr25.png",
- "background.colour": "#6eadc8",
- "drape": "https://hotel.example.com/client/nitro/images/reception/drape.png",
- "left": "https://hotel.example.com/client/nitro/images/reception/mute_reception_backdrop_left.png",
- "right": "https://hotel.example.com/client/nitro/images/reception/background_right.png"
- },
- "widgets": {
- "slot.1.widget": "promoarticle",
- "slot.1.conf": {},
- "slot.2.widget": "widgetcontainer",
- "slot.2.conf": {
- "image": "${image.library.url}web_promo_small/spromo_Canal_Bundle.png",
- "texts": "2021NitroPromo",
- "btnLink": ""
- },
- "slot.3.widget": "",
- "slot.3.conf": {},
- "slot.4.widget": "",
- "slot.4.conf": {},
- "slot.5.widget": "",
- "slot.5.conf": {},
- "slot.6.widget": "",
- "slot.6.conf": {
- "campaign": ""
- },
- "slot.7.widget": "",
- "slot.7.conf": {}
- }
- },
- "achievements.unseen.ignored": [
- "ACH_AllTimeHotelPresence"
- ],
- "avatareditor.show.clubitems.dimmed": true,
- "avatareditor.show.clubitems.first": true,
- "chat.history.max.items": 100,
- "system.currency.types": [
- -1,
- 0,
- 5,
- 105
- ],
- "catalog.links": {
- "hc.buy_hc": "habbo_club",
- "hc.hc_gifts": "club_gifts",
- "pets.buy_food": "pet_food",
- "pets.buy_saddle": "saddles"
- },
- "hc.center": {
- "benefits.info": true,
- "payday.info": true,
- "gift.info": true,
- "benefits.habbopage": "habboclub",
- "payday.habbopage": "hcpayday"
- },
- "respect.options": {
- "enabled": false,
- "sound": "sound_respect_received"
- },
- "currency.display.number.short": false,
- "currency.asset.icon.url": "${images.url}/wallet/%type%.png",
- "catalog.asset.url": "${image.library.url}catalogue",
- "catalog.asset.image.url": "${catalog.asset.url}/%name%.gif",
- "catalog.asset.icon.url": "${catalog.asset.url}/icon_%name%.png",
- "catalog.tab.icons": false,
- "catalog.headers": false,
- "chat.input.maxlength": 100,
- "chat.styles.disabled": [],
- "chat.styles": [
- {
- "styleId": 0,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 1,
- "minRank": 5,
- "isSystemStyle": true,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 2,
- "minRank": 5,
- "isSystemStyle": true,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 3,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 4,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 5,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 6,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 7,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 8,
- "minRank": 5,
- "isSystemStyle": true,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 9,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 10,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 11,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 12,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 13,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 14,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 15,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 16,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 17,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 18,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 19,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 20,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 21,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 22,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 23,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 24,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 25,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 26,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 27,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 28,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 29,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 30,
- "minRank": 5,
- "isSystemStyle": true,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 31,
- "minRank": 5,
- "isSystemStyle": true,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 32,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 33,
- "minRank": 5,
- "isSystemStyle": true,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 34,
- "minRank": 5,
- "isSystemStyle": true,
- "isHcOnly": false,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 35,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 36,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 37,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 38,
- "minRank": 0,
- "isSystemStyle": false,
- "isHcOnly": true,
- "isAmbassadorOnly": false
- },
- {
- "styleId": 39,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 40,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 41,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 42,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 43,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 44,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 45,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 46,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 47,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 48,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 49,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 50,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 51,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 52,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- },
- {
- "styleId": 53,
- "minRank": 5,
- "isSystemStyle": false,
- "isHcOnly": false,
- "isAmbassadorOnly": true
- }
- ],
- "camera.available.effects": [
- {
- "name": "dark_sepia",
- "colorMatrix": [
- 0.4,
- 0.4,
- 0.1,
- 0,
- 110,
- 0.3,
- 0.4,
- 0.1,
- 0,
- 30,
- 0.3,
- 0.2,
- 0.1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 0,
- "enabled": true
- },
- {
- "name": "increase_saturation",
- "colorMatrix": [
- 2,
- -0.5,
- -0.5,
- 0,
- 0,
- -0.5,
- 2,
- -0.5,
- 0,
- 0,
- -0.5,
- -0.5,
- 2,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 0,
- "enabled": true
- },
- {
- "name": "increase_contrast",
- "colorMatrix": [
- 1.5,
- 0,
- 0,
- 0,
- -50,
- 0,
- 1.5,
- 0,
- 0,
- -50,
- 0,
- 0,
- 1.5,
- 0,
- -50,
- 0,
- 0,
- 0,
- 1.5,
- 0
- ],
- "minLevel": 0,
- "enabled": true
- },
- {
- "name": "shadow_multiply_02",
- "colorMatrix": [],
- "minLevel": 0,
- "blendMode": 2,
- "enabled": true
- },
- {
- "name": "color_1",
- "colorMatrix": [
- 0.393,
- 0.769,
- 0.189,
- 0,
- 0,
- 0.349,
- 0.686,
- 0.168,
- 0,
- 0,
- 0.272,
- 0.534,
- 0.131,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 1,
- "enabled": true
- },
- {
- "name": "hue_bright_sat",
- "colorMatrix": [
- 1,
- 0.6,
- 0.2,
- 0,
- -50,
- 0.2,
- 1,
- 0.6,
- 0,
- -50,
- 0.6,
- 0.2,
- 1,
- 0,
- -50,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 1,
- "enabled": true
- },
- {
- "name": "hearts_hardlight_02",
- "colorMatrix": [],
- "minLevel": 1,
- "blendMode": 9,
- "enabled": true
- },
- {
- "name": "texture_overlay",
- "colorMatrix": [],
- "minLevel": 1,
- "blendMode": 4,
- "enabled": true
- },
- {
- "name": "pinky_nrm",
- "colorMatrix": [],
- "minLevel": 1,
- "blendMode": 0,
- "enabled": true
- },
- {
- "name": "color_2",
- "colorMatrix": [
- 0.333,
- 0.333,
- 0.333,
- 0,
- 0,
- 0.333,
- 0.333,
- 0.333,
- 0,
- 0,
- 0.333,
- 0.333,
- 0.333,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 2,
- "enabled": true
- },
- {
- "name": "night_vision",
- "colorMatrix": [
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1.1,
- 0,
- 0,
- -50,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 2,
- "enabled": true
- },
- {
- "name": "stars_hardlight_02",
- "colorMatrix": [],
- "minLevel": 2,
- "blendMode": 9,
- "enabled": true
- },
- {
- "name": "coffee_mpl",
- "colorMatrix": [],
- "minLevel": 2,
- "blendMode": 2,
- "enabled": true
- },
- {
- "name": "security_hardlight",
- "colorMatrix": [],
- "minLevel": 3,
- "blendMode": 9,
- "enabled": true
- },
- {
- "name": "bluemood_mpl",
- "colorMatrix": [],
- "minLevel": 3,
- "blendMode": 2,
- "enabled": true
- },
- {
- "name": "rusty_mpl",
- "colorMatrix": [],
- "minLevel": 3,
- "blendMode": 2,
- "enabled": true
- },
- {
- "name": "decr_conrast",
- "colorMatrix": [
- 0.5,
- 0,
- 0,
- 0,
- 50,
- 0,
- 0.5,
- 0,
- 0,
- 50,
- 0,
- 0,
- 0.5,
- 0,
- 50,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 4,
- "enabled": true
- },
- {
- "name": "green_2",
- "colorMatrix": [
- 0.5,
- 0.5,
- 0.5,
- 0,
- 0,
- 0.5,
- 0.5,
- 0.5,
- 0,
- 90,
- 0.5,
- 0.5,
- 0.5,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 4,
- "enabled": true
- },
- {
- "name": "alien_hrd",
- "colorMatrix": [],
- "minLevel": 4,
- "blendMode": 9,
- "enabled": true
- },
- {
- "name": "color_3",
- "colorMatrix": [
- 0.609,
- 0.609,
- 0.082,
- 0,
- 0,
- 0.309,
- 0.609,
- 0.082,
- 0,
- 0,
- 0.309,
- 0.609,
- 0.082,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 5,
- "enabled": true
- },
- {
- "name": "color_4",
- "colorMatrix": [
- 0.8,
- -0.8,
- 1,
- 0,
- 70,
- 0.8,
- -0.8,
- 1,
- 0,
- 70,
- 0.8,
- -0.8,
- 1,
- 0,
- 70,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 5,
- "enabled": true
- },
- {
- "name": "toxic_hrd",
- "colorMatrix": [],
- "minLevel": 5,
- "blendMode": 9,
- "enabled": true
- },
- {
- "name": "hypersaturated",
- "colorMatrix": [
- 2,
- -1,
- 0,
- 0,
- 0,
- -1,
- 2,
- 0,
- 0,
- 0,
- 0,
- -1,
- 2,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 6,
- "enabled": true
- },
- {
- "name": "Yellow",
- "colorMatrix": [
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 6,
- "enabled": true
- },
- {
- "name": "misty_hrd",
- "colorMatrix": [],
- "minLevel": 6,
- "blendMode": 9,
- "enabled": true
- },
- {
- "name": "x_ray",
- "colorMatrix": [
- 0,
- 1.2,
- 0,
- 0,
- -100,
- 0,
- 2,
- 0,
- 0,
- -120,
- 0,
- 2,
- 0,
- 0,
- -120,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 7,
- "enabled": true
- },
- {
- "name": "decrease_saturation",
- "colorMatrix": [
- 0.7,
- 0.2,
- 0.2,
- 0,
- 0,
- 0.2,
- 0.7,
- 0.2,
- 0,
- 0,
- 0.2,
- 0.2,
- 0.7,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 7,
- "enabled": true
- },
- {
- "name": "drops_mpl",
- "colorMatrix": [],
- "minLevel": 8,
- "blendMode": 2,
- "enabled": true
- },
- {
- "name": "shiny_hrd",
- "colorMatrix": [],
- "minLevel": 9,
- "blendMode": 9,
- "enabled": true
- },
- {
- "name": "glitter_hrd",
- "colorMatrix": [],
- "minLevel": 10,
- "blendMode": 9,
- "enabled": true
- },
- {
- "name": "frame_gold",
- "colorMatrix": [],
- "minLevel": 10,
- "blendMode": 0,
- "enabled": true
- },
- {
- "name": "frame_gray_4",
- "colorMatrix": [],
- "minLevel": 10,
- "blendMode": 0,
- "enabled": true
- },
- {
- "name": "frame_black_2",
- "colorMatrix": [],
- "minLevel": 10,
- "blendMode": 0,
- "enabled": true
- },
- {
- "name": "frame_wood_2",
- "colorMatrix": [],
- "minLevel": 10,
- "blendMode": 0,
- "enabled": true
- },
- {
- "name": "finger_nrm",
- "colorMatrix": [],
- "minLevel": 10,
- "blendMode": 0,
- "enabled": true
- },
- {
- "name": "color_5",
- "colorMatrix": [
- 3.309,
- 0.609,
- 1.082,
- 0.2,
- 0,
- 0.309,
- 0.609,
- 0.082,
- 0,
- 0,
- 1.309,
- 0.609,
- 0.082,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 10,
- "enabled": true
- },
- {
- "name": "black_white_negative",
- "colorMatrix": [
- -0.5,
- -0.5,
- -0.5,
- 0,
- 0,
- -0.5,
- -0.5,
- -0.5,
- 0,
- 0,
- -0.5,
- -0.5,
- -0.5,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 10,
- "enabled": true
- },
- {
- "name": "blue",
- "colorMatrix": [
- 0.5,
- 0.5,
- 0.5,
- 0,
- -255,
- 0.5,
- 0.5,
- 0.5,
- 0,
- -170,
- 0.5,
- 0.5,
- 0.5,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 10,
- "enabled": true
- },
- {
- "name": "red",
- "colorMatrix": [
- 0.5,
- 0.5,
- 0.5,
- 0,
- 0,
- 0.5,
- 0.5,
- 0.5,
- 0,
- -170,
- 0.5,
- 0.5,
- 0.5,
- 0,
- -170,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 10,
- "enabled": true
- },
- {
- "name": "green",
- "colorMatrix": [
- 0.5,
- 0.5,
- 0.5,
- 0,
- -170,
- 0.5,
- 0.5,
- 0.5,
- 0,
- 0,
- 0.5,
- 0.5,
- 0.5,
- 0,
- -170,
- 0,
- 0,
- 0,
- 1,
- 0
- ],
- "minLevel": 10,
- "enabled": true
- }
- ],
- "notification": {
- "notification.admin.transient": {
- "display": "POP_UP",
- "image": "${image.library.url}/album1358/frank_wave_001.gif"
- },
- "notification.builders_club.membership_expired": {
- "display": "POP_UP"
- },
- "notification.builders_club.membership_expires": {
- "display": "POP_UP",
- "image": "${image.library.url}/notifications/builders_club_room_locked_small.png"
- },
- "notification.builders_club.membership_extended": {
- "delivery": "PERSISTENT",
- "display": "POP_UP"
- },
- "notification.builders_club.membership_made": {
- "delivery": "PERSISTENT",
- "display": "POP_UP",
- "image": "${image.library.url}/notifications/builders_club_membership_extended.png"
- },
- "notification.builders_club.membership_renewed": {
- "delivery": "PERSISTENT",
- "display": "POP_UP",
- "image": "${image.library.url}/notifications/builders_club_membership_extended.png"
- },
- "notification.builders_club.room_locked": {
- "display": "BUBBLE",
- "image": "${image.library.url}/notifications/builders_club_room_locked_small.png"
- },
- "notification.builders_club.room_unlocked": {
- "display": "BUBBLE"
- },
- "notification.builders_club.visit_denied_for_owner": {
- "display": "BUBBLE",
- "image": "${image.library.url}/notifications/builders_club_room_locked_small.png"
- },
- "notification.builders_club.visit_denied_for_visitor": {
- "display": "POP_UP",
- "image": "${image.library.url}/notifications/builders_club_room_locked.png"
- },
- "notification.campaign.credit.donation": {
- "display": "BUBBLE"
- },
- "notification.campaign.product.donation": {
- "display": "BUBBLE"
- },
- "notification.casino.too_many_dice.placement": {
- "display": "POP_UP"
- },
- "notification.casino.too_many_dice": {
- "display": "POP_UP"
- },
- "notification.cfh.created": {
- "display": "POP_UP",
- "title": ""
- },
- "notification.feed.enabled": false,
- "notification.floorplan_editor.error": {
- "display": "POP_UP"
- },
- "notification.forums.delivered": {
- "delivery": "PERSISTENT",
- "display": "POP_UP"
- },
- "notification.forums.forum_settings_updated": {
- "display": "BUBBLE"
- },
- "notification.forums.message.hidden": {
- "display": "BUBBLE"
- },
- "notification.forums.message.restored": {
- "display": "BUBBLE"
- },
- "notification.forums.thread.hidden": {
- "display": "BUBBLE"
- },
- "notification.forums.thread.locked": {
- "display": "BUBBLE"
- },
- "notification.forums.thread.pinned": {
- "display": "BUBBLE"
- },
- "notification.forums.thread.restored": {
- "display": "BUBBLE"
- },
- "notification.forums.thread.unlocked": {
- "display": "BUBBLE"
- },
- "notification.forums.thread.unpinned": {
- "display": "BUBBLE"
- },
- "notification.furni_placement_error": {
- "display": "BUBBLE"
- },
- "notification.gifting.valentine": {
- "delivery": "PERSISTENT",
- "display": "BUBBLE",
- "image": "${image.library.url}/notifications/polaroid_photo.png"
- },
- "notification.items.enabled": true,
- "notification.mute.forbidden.time": {
- "display": "BUBBLE"
- },
- "notification.npc.gift.received": {
- "display": "BUBBLE",
- "image": "${image.library.url}/album1584/X1517.gif"
- }
- }
-}
\ No newline at end of file
diff --git a/scripts/minify-dist.mjs b/scripts/minify-dist.mjs
index a9d3d18..c61ff54 100644
--- a/scripts/minify-dist.mjs
+++ b/scripts/minify-dist.mjs
@@ -44,12 +44,6 @@ for(const file of walk(dist))
if(file.endsWith('.json')) minifyJson(file);
}
-for(const file of [ 'renderer-config.json', 'ui-config.json' ])
-{
- const target = join(dist, file);
- if(existsSync(target)) rmSync(target);
-}
-
for(const file of walk(dist))
{
if(file.endsWith('.js') && !file.endsWith('asset-loader.js')) encryptFile(file);
@@ -84,4 +78,4 @@ for(const [ source, file ] of publicLoaderAssets)
}
}
-writeFileSync(join(dist, 'index.html'), ``);
+writeFileSync(join(dist, 'index.html'), ``);
diff --git a/scripts/write-asset-loader.mjs b/scripts/write-asset-loader.mjs
index a94d388..59e403c 100644
--- a/scripts/write-asset-loader.mjs
+++ b/scripts/write-asset-loader.mjs
@@ -1,4 +1,4 @@
-import { mkdirSync, writeFileSync } from 'fs';
+import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import { dirname, resolve } from 'path';
const loader = `(() => {
@@ -148,6 +148,10 @@ const loader = `(() => {
const readClientMode = async () => {
try {
+ if(window.__nitroClientMode && typeof window.__nitroClientMode === "object") {
+ debug("loader: client-mode preset");
+ return window.__nitroClientMode;
+ }
const url = withCacheBust(new URL("./client-mode.json", getBase()));
const response = await fetch(url, { cache: "no-store" });
if(!response.ok) throw new Error("client-mode " + response.status);
@@ -185,7 +189,157 @@ const loader = `(() => {
});
})();`;
-const target = resolve('public', 'asset-loader.js');
+const clientModePath = resolve('public', 'configuration', 'client-mode.json');
+let bootstrapApiBase = '';
+
+if(existsSync(clientModePath))
+{
+ try
+ {
+ const clientMode = JSON.parse(readFileSync(clientModePath, 'utf8'));
+
+ if(typeof clientMode.apiBaseUrl === 'string') bootstrapApiBase = clientMode.apiBaseUrl;
+ }
+ catch {}
+}
+
+const bootstrap = `(() => {
+ const API_BASE = ${ JSON.stringify(bootstrapApiBase) };
+
+ const getBase = () => {
+ const source = document.currentScript?.src || location.href;
+ return new URL(".", source);
+ };
+
+ const withCacheBust = (url) => {
+ url.searchParams.set("v", Date.now().toString(36));
+ return url;
+ };
+
+ const bytesToBase64 = (buffer) => {
+ let binary = "";
+ const bytes = new Uint8Array(buffer);
+ for(let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
+ return btoa(binary);
+ };
+
+ const hexValue = (code) => {
+ if(code >= 48 && code <= 57) return code - 48;
+ if(code >= 65 && code <= 70) return code - 55;
+ if(code >= 97 && code <= 102) return code - 87;
+ return -1;
+ };
+
+ const hexToBytes = (hex) => {
+ const normalized = hex.trim();
+ if((normalized.length % 2) !== 0) throw new Error("Invalid encrypted hex payload.");
+ const bytes = new Uint8Array(normalized.length / 2);
+ for(let i = 0; i < bytes.length; i++) {
+ const high = hexValue(normalized.charCodeAt(i * 2));
+ const low = hexValue(normalized.charCodeAt((i * 2) + 1));
+ if(high < 0 || low < 0) throw new Error("Invalid encrypted hex payload.");
+ bytes[i] = (high << 4) | low;
+ }
+ return bytes;
+ };
+
+ const deriveAesKey = async (privateKey, serverKeyBase64) => {
+ const serverBytes = Uint8Array.from(atob(serverKeyBase64), char => char.charCodeAt(0));
+ const serverKey = await crypto.subtle.importKey("spki", serverBytes, { name: "ECDH", namedCurve: "P-256" }, false, []);
+ const secret = await crypto.subtle.deriveBits({ name: "ECDH", public: serverKey }, privateKey, 256);
+ const salt = new TextEncoder().encode("nitro-secure-assets-v1");
+ const material = new Uint8Array(secret.byteLength + salt.length);
+ material.set(new Uint8Array(secret), 0);
+ material.set(salt, secret.byteLength);
+ const hash = await crypto.subtle.digest("SHA-256", material);
+ return crypto.subtle.importKey("raw", hash, "AES-GCM", false, ["decrypt"]);
+ };
+
+ const decryptPayload = async (key, response) => {
+ if(response.headers.get("X-Nitro-Sec") !== "1") return response.text();
+ const bytes = hexToBytes(await response.text());
+ if(bytes.length < 13) throw new Error("Encrypted response is too short.");
+ const iv = bytes.slice(0, 12);
+ const payload = bytes.slice(12);
+ const clear = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, payload);
+ return new TextDecoder().decode(clear);
+ };
+
+ const importTextModule = async (sourceText) => {
+ const blobUrl = URL.createObjectURL(new Blob([sourceText], { type: "text/javascript" }));
+ try {
+ await import(blobUrl);
+ } finally {
+ URL.revokeObjectURL(blobUrl);
+ }
+ };
+
+ const loadPlainBootstrap = async () => {
+ const url = withCacheBust(new URL("./asset-loader.js", getBase()));
+ await import(url.href);
+ };
+
+ const loadSecureBootstrap = async () => {
+ if(!API_BASE) 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 bootstrapResponse = await fetch(base + "/nitro-sec/bootstrap", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ key: publicKey })
+ });
+
+ if(!bootstrapResponse.ok) throw new Error("Secure bootstrap failed: HTTP " + bootstrapResponse.status);
+
+ const bootstrapPayload = await bootstrapResponse.json();
+ if(!bootstrapPayload || typeof bootstrapPayload.key !== "string" || !bootstrapPayload.key.length) {
+ throw new Error("Secure bootstrap returned an invalid server key.");
+ }
+
+ const sessionKey = await deriveAesKey(pair.privateKey, bootstrapPayload.key);
+
+ const fetchSecureConfig = async (file) => {
+ const url = new URL(base + "/nitro-sec/file");
+ url.searchParams.set("kind", "config");
+ url.searchParams.set("file", file);
+ url.searchParams.set("v", Date.now().toString(36));
+
+ const response = await fetch(url.toString(), {
+ headers: { "X-Nitro-Key": publicKey },
+ cache: "no-store"
+ });
+
+ if(!response.ok) throw new Error("Failed to load secure config " + file + ": HTTP " + response.status);
+
+ return decryptPayload(sessionKey, response);
+ };
+
+ const modeText = await fetchSecureConfig("client-mode.json");
+ window.__nitroClientMode = JSON.parse(modeText);
+
+ const loaderText = await fetchSecureConfig("asset-loader.js");
+ await importTextModule(loaderText);
+ };
+
+ (async () => {
+ try {
+ await loadSecureBootstrap();
+ } 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.";
+ });
+})();`;
+
+const target = resolve('public', 'configuration', 'asset-loader.js');
+const bootstrapTarget = resolve('public', 'configuration', 'bootstrap.js');
mkdirSync(dirname(target), { recursive: true });
writeFileSync(target, loader);
+writeFileSync(bootstrapTarget, bootstrap);
diff --git a/src/bootstrap.ts b/src/bootstrap.ts
index 2df054b..395f3b2 100644
--- a/src/bootstrap.ts
+++ b/src/bootstrap.ts
@@ -31,8 +31,8 @@ const cacheBustUrl = (path: string): string =>
(window as any).NitroClientMode = clientMode;
(window as any).NitroConfig = {
'config.urls': [
- clientMode.secureAssetsEnabled ? secureUrl('config', 'renderer-config.json', true) : cacheBustUrl('renderer-config.json'),
- clientMode.secureAssetsEnabled ? secureUrl('config', 'ui-config.json', true) : cacheBustUrl('ui-config.json')
+ clientMode.secureAssetsEnabled ? secureUrl('config', 'renderer-config.json', true) : cacheBustUrl('configuration/renderer-config.json'),
+ clientMode.secureAssetsEnabled ? secureUrl('config', 'ui-config.json', true) : cacheBustUrl('configuration/ui-config.json')
],
'sso.ticket': search.get('sso') || null,
'forward.type': search.get('room') ? 2 : -1,
diff --git a/src/components/ads/GoogleAdsView.tsx b/src/components/ads/GoogleAdsView.tsx
index b31574e..4b65295 100644
--- a/src/components/ads/GoogleAdsView.tsx
+++ b/src/components/ads/GoogleAdsView.tsx
@@ -1,6 +1,7 @@
import { FC, useEffect, useRef, useState } from 'react';
import { GetConfigurationValue } from '../../api';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../common';
+import { configFileUrl } from '../../secure-assets';
interface AdsenseConfig {
slot: string;
@@ -70,7 +71,7 @@ export const GoogleAdsView: FC<{}> = () => {
try {
const [ adsTxtRes, configRes ] = await Promise.all([
fetch('/ads.txt', { cache: 'no-cache' }),
- fetch('/adsense.json', { cache: 'no-cache' })
+ fetch(configFileUrl('adsense.json', true), { cache: 'no-cache' })
]);
if (!adsTxtRes.ok) throw new Error(`ads.txt ${ adsTxtRes.status }`);
@@ -156,7 +157,7 @@ export const GoogleAdsView: FC<{}> = () => {
data-full-width-responsive={ (config.fullWidthResponsive ?? true) ? 'true' : 'false' }
/> }
{ !loadError && publisherId && config && !config.slot &&
- Ad slot not configured in adsense.json
}
+ Ad slot not configured in configuration/adsense.json
}
diff --git a/src/components/login/LoginView.tsx b/src/components/login/LoginView.tsx
index ceba536..dd08aed 100644
--- a/src/components/login/LoginView.tsx
+++ b/src/components/login/LoginView.tsx
@@ -1,6 +1,7 @@
import { GetConfiguration } from '@nitrots/nitro-renderer';
import { FC, FormEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ClearRememberLogin, GetConfigurationValue, GetRememberLogin, StoreRememberLoginFromPayload } from '../../api';
+import { configFileUrl } from '../../secure-assets';
import flagBr from '../../assets/images/flag_icon/flag_icon_br.png';
import flagDe from '../../assets/images/flag_icon/flag_icon_de.png';
import flagEn from '../../assets/images/flag_icon/flag_icon_en.png';
@@ -1054,7 +1055,7 @@ const RegisterDialog: FC = props =>
{
if(step !== 'avatar' || hotLooks.length) return;
let cancelled = false;
- fetch('hotlooks.json', { credentials: 'omit' })
+ fetch(configFileUrl('hotlooks.json', true), { credentials: 'omit' })
.then(r => r.ok ? r.json() : null)
.then((json: unknown) =>
{
diff --git a/src/secure-assets.ts b/src/secure-assets.ts
index 31018ac..5cdfdc3 100644
--- a/src/secure-assets.ts
+++ b/src/secure-assets.ts
@@ -204,7 +204,7 @@ const getPlainAssetBase = (kind: 'config' | 'gamedata'): string =>
if(typeof configured === 'string' && configured.length) return configured.endsWith('/') ? configured : `${ configured }/`;
- if(kind === 'config') return `${ window.location.origin }/`;
+ if(kind === 'config') return `${ window.location.origin }/configuration/`;
return `${ window.location.origin }/nitro/gamedata/`;
};
@@ -239,6 +239,17 @@ export const secureUrl = (kind: 'config' | 'gamedata', file: string, cacheBust =
return `${ base }/nitro-sec/file?kind=${ encodeURIComponent(kind) }&file=${ encodeURIComponent(file) }${ version }`;
};
+export const configFileUrl = (file: string, cacheBust = false): string =>
+{
+ if(getClientMode().secureAssetsEnabled) return secureUrl('config', file, cacheBust);
+
+ const plainUrl = new URL(`configuration/${ file.replace(/^\/+/, '') }`, `${ window.location.origin }/`);
+
+ if(cacheBust) plainUrl.searchParams.set('v', Date.now().toString(36));
+
+ return plainUrl.toString();
+};
+
const createSecureSession = async (): Promise =>
{
setDebugState('secure: generating ECDH session');
diff --git a/vite.config.mjs b/vite.config.mjs
index 5922a33..7bf9554 100644
--- a/vite.config.mjs
+++ b/vite.config.mjs
@@ -16,18 +16,7 @@ export default defineConfig({
rendererRoot,
]
},
- proxy: {
- '/api': {
- target: process.env.AUTH_PROXY_TARGET || 'https://nitro.example.com:2096/',
- changeOrigin: true,
- ws: true,
- },
- '/nitro-sec': {
- target: process.env.NITRO_PROXY_TARGET || 'https://nitro.example.com:2096/',
- changeOrigin: true,
- ws: true,
- }
- }
+
},
resolve: {
tsconfigPaths: true,