User accounts, login before the title screen, own savegames per user, standalone download

- Service: accounts with Argon2 password hashes and session tokens; the first login creates
  the admin (shared savegames of before move to the admin); the admin creates and deletes
  users; everybody can change the password; savegames are stored and listed per user
- Game: login page before the title (only when the service is reachable), with licence and
  repository link; account page (password, logout; users for the admin); text fields with
  Tab/Enter, masked passwords, browser dialog on touch screens; long status messages wrap
- Download Standalone: DungeonCraft.html with the game embedded (built in the Docker image,
  served to logged-in users via nginx auth_request); works from file:// without server and
  opens downloaded savegames
- Without the service (standalone, local files) there is no login, savegame files as before

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 13:33:22 +01:00
1 parent 8853d84564
commit c9848c8948
15 files changed
+1232 -83

No files matched your search

+23
View File
@@ -16,6 +16,9 @@ mod imp {
fn dc_storage_take(dst: *mut u8);
fn dc_coarse_pointer() -> i32;
fn dc_open_url(u: *const u8, ul: u32);
fn dc_set_token(t: *const u8, tl: u32);
fn dc_fetch_download(u: *const u8, ul: u32, n: *const u8, nl: u32);
fn dc_fetch_download_state() -> i32;
fn dc_prompt(t: *const u8, tl: u32, d: *const u8, dl: u32) -> i32;
fn dc_storage_set(key: *const u8, key_len: u32, val: *const u8, val_len: u32);
fn dc_cache_set(key: *const u8, key_len: u32, val: *const u8, val_len: u32);
@@ -67,6 +70,21 @@ mod imp {
unsafe { dc_open_url(url.as_ptr(), url.len() as u32) }
}
// login session sent with every request to the service
pub fn set_token(token: &str) {
unsafe { dc_set_token(token.as_ptr(), token.len() as u32) }
}
// downloads a file of the server (with the login) straight to the user's disk
pub fn fetch_download(url: &str, name: &str) {
unsafe { dc_fetch_download(url.as_ptr(), url.len() as u32, name.as_ptr(), name.len() as u32) }
}
// 0 idle, 1 running, 2 done, 3 failed (2 and 3 are reported once)
pub fn fetch_download_state() -> i32 {
unsafe { dc_fetch_download_state() }
}
// asks for a text with the browser's dialog; None when cancelled
pub fn prompt(title: &str, default: &str) -> Option<String> {
unsafe {
@@ -128,6 +146,11 @@ mod imp {
false
}
pub fn open_url(_url: &str) {}
pub fn set_token(_token: &str) {}
pub fn fetch_download(_url: &str, _name: &str) {}
pub fn fetch_download_state() -> i32 {
0
}
pub fn storage_set(key: &str, val: &str) {
STORE.with(|s| s.borrow_mut().insert(key.to_string(), val.to_string()));
}