Beginning campaign UI and WS to get player state.

This commit is contained in:
Clément Pons
2025-11-12 17:53:48 +01:00
parent 3ed9ab3dce
commit dd4191bea6
32 changed files with 4225 additions and 132 deletions

View File

@@ -3,6 +3,13 @@ import { z } from "zod/v4";
export const CampaignValidation = z.object({
id: z.number(),
name: z.string().nonempty(),
description: z.string(),
joinby: z.enum([ 'link', 'invite' ]),
});
description: z.string()
});
class CampaignSheet
{
constructor()
{
}
}

View File

@@ -44,6 +44,7 @@ export const defaultCharacter: Character = {
exhaustion: 0,
sickness: [],
poisons: [],
money: 0,
},
owner: -1,
@@ -563,6 +564,8 @@ export class CharacterBuilder extends CharacterCompiler
{
document.title = `d[any] - Edition de nouveau personnage`;
this.id = 'new';
this._character.id = -1;
this.render();
this.display(0);
}
@@ -628,14 +631,21 @@ export class CharacterBuilder extends CharacterCompiler
{
if(this.id === 'new' || this.id === '-1')
{
//@ts-ignore
this.id = this._character.id = this._result.id = await useRequestFetch()(`/api/character`, {
const result = await useRequestFetch()(`/api/character`, {
method: 'post',
body: this._character,
onResponseError: (e) => {
Toaster.add({ title: 'Erreur d\'enregistrement', content: e.response.status === 401 ? "Vous n'êtes pas autorisé à effectué cette opération" : e.response.statusText, type: 'error', closeable: true, duration: 25000, timer: true });
this.id = 'new';
}
});
if(result !== undefined)
{
this._character.id = this._result.id = result as number;
this.id = result.toString();
}
Toaster.add({ content: 'Personnage créé', type: 'success', duration: 25000, timer: true });
useRouter().replace({ name: 'character-id-edit', params: { id: this.id } })
if(leave) useRouter().push({ name: 'character-id', params: { id: this.id } });

View File

@@ -139,6 +139,7 @@ export class Content
try
{
Content._overview = parse<Record<string, Omit<LocalContent, 'content'>>>(overview);
Content._reverseMapping = Object.values(Content._overview).reduce((p, v) => { p[v.path] = v.id; return p; }, {} as Record<string, string>);
await Content.pull();
}
catch(e)
@@ -245,16 +246,14 @@ export class Content
if(force || !_overview || new Date(_overview.timestamp) < new Date(file.timestamp))
{
Content._overview[file.id] = file;
Content._reverseMapping[file.path] = file.id;
Content.queue.queue(() => {
return useRequestFetch()(`/api/file/content/${file.id}`, { cache: 'no-cache' }).then(async (content: string | undefined | null) => {
if(content)
{
if(file.type !== 'folder')
{
Content.queue.queue(() => Content.write(file.id, content, { create: true }));
//Content.queue.queue(() => Content.write('storage/' + file.path + (file.type === 'canvas' ? '.canvas' : '.md'), Content.toString({ ...file, content: Content.fromString(file, content) }), { create: true }));
}
}
else
Content._overview[file.id]!.error = true;

View File

@@ -68,4 +68,36 @@ export function clamp(x: number, min: number, max: number): number
export function lerp(x: number, a: number, b: number): number
{
return (1-x)*a+x*b;
}
// The value position is randomized
// The metadata separator is randomized as a letter (to avoid collision with numbers)
// The URI is (| == picked separator) |v_length|first part of the hash + seed + second part of the hash|v_pos|seed as hex.
// Every number are converted to string as hexadecimal values
export function cryptURI(key: string, value: number, seed?: number): string
{
const _seed = seed ?? Date.now();
const hash = Bun.hash(key + value.toString(), _seed).toString(16);
const pos = Math.floor(Math.random() * hash.length);
const separator = String.fromCharCode(Math.floor(Math.random() * 26 + 96));
return Bun.zstdCompressSync(separator + value.toString(16).length + separator + hash.substring(0, pos) + value + hash.substring(pos) + separator + pos + separator + _seed.toString(16), { level: 1 }).toString('base64');
}
export function decryptURI(uri: string, key: string): number | undefined
{
const decoder = new TextDecoder();
const _uri = decoder.decode(Bun.zstdDecompressSync(Buffer.from(uri, 'base64')));
const separator = _uri.charAt(0);
const length = parseInt(_uri.substring(1, _uri.indexOf(separator, 1)), 10);
const seed = parseInt(_uri.substring(_uri.lastIndexOf(separator) + 1), 16);
const pos = parseInt(_uri.substring(_uri.lastIndexOf(separator, _uri.length - seed.toString(16).length - 2) + 1, _uri.lastIndexOf(separator)), 10);
const _hash = _uri.substring(2 + length.toString(10).length, _uri.length - (2 + seed.toString(16).length + pos.toString(10).length));
const value = _hash.substring(pos, pos + length);
const hash = _hash.substring(0, pos) + _hash.substring(pos + length);
if(Bun.hash(key + value, seed).toString(16) === hash)
return parseInt(value, 10);
else
return undefined;
}