The first commit, the core and all files is adding to repo
This commit is contained in:
666
shared/database.ts
Normal file
666
shared/database.ts
Normal file
@@ -0,0 +1,666 @@
|
||||
export type Json =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| { [key: string]: Json | undefined }
|
||||
| Json[];
|
||||
|
||||
export type Database = {
|
||||
// Allows to automatically instantiate createClient with right options
|
||||
// instead of createClient<Database, { PostgrestVersion: 'XX' }>(URL, KEY)
|
||||
__InternalSupabase: {
|
||||
PostgrestVersion: "14.18";
|
||||
};
|
||||
public: {
|
||||
Tables: {
|
||||
catalog_servers: {
|
||||
Row: {
|
||||
address: string | null;
|
||||
created_at: string;
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
Insert: {
|
||||
address?: string | null;
|
||||
created_at?: string;
|
||||
id?: string;
|
||||
name: string;
|
||||
};
|
||||
Update: {
|
||||
address?: string | null;
|
||||
created_at?: string;
|
||||
id?: string;
|
||||
name?: string;
|
||||
};
|
||||
Relationships: [];
|
||||
};
|
||||
contributor_tokens: {
|
||||
Row: {
|
||||
created_at: string;
|
||||
id: string;
|
||||
label: string;
|
||||
owner_id: string;
|
||||
revoked: boolean;
|
||||
token_hash: string;
|
||||
};
|
||||
Insert: {
|
||||
created_at?: string;
|
||||
id?: string;
|
||||
label: string;
|
||||
owner_id: string;
|
||||
revoked?: boolean;
|
||||
token_hash: string;
|
||||
};
|
||||
Update: {
|
||||
created_at?: string;
|
||||
id?: string;
|
||||
label?: string;
|
||||
owner_id?: string;
|
||||
revoked?: boolean;
|
||||
token_hash?: string;
|
||||
};
|
||||
Relationships: [];
|
||||
};
|
||||
dataset_inputs: {
|
||||
Row: {
|
||||
dataset_id: string;
|
||||
manifest: Json;
|
||||
validated: boolean;
|
||||
};
|
||||
Insert: {
|
||||
dataset_id: string;
|
||||
manifest: Json;
|
||||
validated?: boolean;
|
||||
};
|
||||
Update: {
|
||||
dataset_id?: string;
|
||||
manifest?: Json;
|
||||
validated?: boolean;
|
||||
};
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "dataset_inputs_dataset_id_fkey";
|
||||
columns: ["dataset_id"];
|
||||
isOneToOne: true;
|
||||
referencedRelation: "datasets";
|
||||
referencedColumns: ["id"];
|
||||
},
|
||||
];
|
||||
};
|
||||
datasets: {
|
||||
Row: {
|
||||
bounds: Json;
|
||||
cover_path: string | null;
|
||||
created_at: string;
|
||||
description: string;
|
||||
id: string;
|
||||
map_path: string | null;
|
||||
name: string;
|
||||
owner_id: string;
|
||||
panorama_count: number;
|
||||
rejection_reason: string | null;
|
||||
revision_id: string | null;
|
||||
server_id: string | null;
|
||||
source: string;
|
||||
state: string;
|
||||
updated_at: string;
|
||||
};
|
||||
Insert: {
|
||||
bounds: Json;
|
||||
cover_path?: string | null;
|
||||
created_at?: string;
|
||||
description?: string;
|
||||
id?: string;
|
||||
map_path?: string | null;
|
||||
name: string;
|
||||
owner_id: string;
|
||||
panorama_count?: number;
|
||||
rejection_reason?: string | null;
|
||||
revision_id?: string | null;
|
||||
server_id?: string | null;
|
||||
source: string;
|
||||
state?: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
Update: {
|
||||
bounds?: Json;
|
||||
cover_path?: string | null;
|
||||
created_at?: string;
|
||||
description?: string;
|
||||
id?: string;
|
||||
map_path?: string | null;
|
||||
name?: string;
|
||||
owner_id?: string;
|
||||
panorama_count?: number;
|
||||
rejection_reason?: string | null;
|
||||
revision_id?: string | null;
|
||||
server_id?: string | null;
|
||||
source?: string;
|
||||
state?: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "datasets_revision_fk";
|
||||
columns: ["revision_id"];
|
||||
isOneToOne: false;
|
||||
referencedRelation: "revisions";
|
||||
referencedColumns: ["id"];
|
||||
},
|
||||
{
|
||||
foreignKeyName: "datasets_server_id_fkey";
|
||||
columns: ["server_id"];
|
||||
isOneToOne: false;
|
||||
referencedRelation: "catalog_servers";
|
||||
referencedColumns: ["id"];
|
||||
},
|
||||
];
|
||||
};
|
||||
game_rounds: {
|
||||
Row: {
|
||||
deadline: string | null;
|
||||
result: Json | null;
|
||||
round_number: number;
|
||||
session_id: string;
|
||||
viewpoint: Json;
|
||||
};
|
||||
Insert: {
|
||||
deadline?: string | null;
|
||||
result?: Json | null;
|
||||
round_number: number;
|
||||
session_id: string;
|
||||
viewpoint: Json;
|
||||
};
|
||||
Update: {
|
||||
deadline?: string | null;
|
||||
result?: Json | null;
|
||||
round_number?: number;
|
||||
session_id?: string;
|
||||
viewpoint?: Json;
|
||||
};
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "game_rounds_session_id_fkey";
|
||||
columns: ["session_id"];
|
||||
isOneToOne: false;
|
||||
referencedRelation: "game_sessions";
|
||||
referencedColumns: ["id"];
|
||||
},
|
||||
];
|
||||
};
|
||||
game_sessions: {
|
||||
Row: {
|
||||
access_hash: string;
|
||||
created_at: string;
|
||||
current_round: number;
|
||||
difficulty: string;
|
||||
id: string;
|
||||
owner_id: string | null;
|
||||
revision_id: string;
|
||||
};
|
||||
Insert: {
|
||||
access_hash: string;
|
||||
created_at?: string;
|
||||
current_round?: number;
|
||||
difficulty: string;
|
||||
id?: string;
|
||||
owner_id?: string | null;
|
||||
revision_id: string;
|
||||
};
|
||||
Update: {
|
||||
access_hash?: string;
|
||||
created_at?: string;
|
||||
current_round?: number;
|
||||
difficulty?: string;
|
||||
id?: string;
|
||||
owner_id?: string | null;
|
||||
revision_id?: string;
|
||||
};
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "game_sessions_revision_id_fkey";
|
||||
columns: ["revision_id"];
|
||||
isOneToOne: false;
|
||||
referencedRelation: "revisions";
|
||||
referencedColumns: ["id"];
|
||||
},
|
||||
];
|
||||
};
|
||||
pairing_codes: {
|
||||
Row: {
|
||||
consumed: boolean;
|
||||
device_hash: string;
|
||||
expires_at: string;
|
||||
owner_id: string | null;
|
||||
polled_at: string | null;
|
||||
token_value: string | null;
|
||||
user_code: string;
|
||||
};
|
||||
Insert: {
|
||||
consumed?: boolean;
|
||||
device_hash: string;
|
||||
expires_at: string;
|
||||
owner_id?: string | null;
|
||||
polled_at?: string | null;
|
||||
token_value?: string | null;
|
||||
user_code: string;
|
||||
};
|
||||
Update: {
|
||||
consumed?: boolean;
|
||||
device_hash?: string;
|
||||
expires_at?: string;
|
||||
owner_id?: string | null;
|
||||
polled_at?: string | null;
|
||||
token_value?: string | null;
|
||||
user_code?: string;
|
||||
};
|
||||
Relationships: [];
|
||||
};
|
||||
profiles: {
|
||||
Row: {
|
||||
created_at: string;
|
||||
display_name: string;
|
||||
id: string;
|
||||
role: string;
|
||||
};
|
||||
Insert: {
|
||||
created_at?: string;
|
||||
display_name?: string;
|
||||
id: string;
|
||||
role?: string;
|
||||
};
|
||||
Update: {
|
||||
created_at?: string;
|
||||
display_name?: string;
|
||||
id?: string;
|
||||
role?: string;
|
||||
};
|
||||
Relationships: [];
|
||||
};
|
||||
rate_limits: {
|
||||
Row: {
|
||||
count: number;
|
||||
expires_at: string;
|
||||
key: string;
|
||||
};
|
||||
Insert: {
|
||||
count?: number;
|
||||
expires_at: string;
|
||||
key: string;
|
||||
};
|
||||
Update: {
|
||||
count?: number;
|
||||
expires_at?: string;
|
||||
key?: string;
|
||||
};
|
||||
Relationships: [];
|
||||
};
|
||||
reports: {
|
||||
Row: {
|
||||
created_at: string;
|
||||
dataset_id: string;
|
||||
id: string;
|
||||
owner_id: string | null;
|
||||
reason: string;
|
||||
state: string;
|
||||
};
|
||||
Insert: {
|
||||
created_at?: string;
|
||||
dataset_id: string;
|
||||
id?: string;
|
||||
owner_id?: string | null;
|
||||
reason: string;
|
||||
state?: string;
|
||||
};
|
||||
Update: {
|
||||
created_at?: string;
|
||||
dataset_id?: string;
|
||||
id?: string;
|
||||
owner_id?: string | null;
|
||||
reason?: string;
|
||||
state?: string;
|
||||
};
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "reports_dataset_id_fkey";
|
||||
columns: ["dataset_id"];
|
||||
isOneToOne: false;
|
||||
referencedRelation: "datasets";
|
||||
referencedColumns: ["id"];
|
||||
},
|
||||
];
|
||||
};
|
||||
review_decisions: {
|
||||
Row: {
|
||||
created_at: string;
|
||||
dataset_id: string;
|
||||
decision: string;
|
||||
id: string;
|
||||
reason: string | null;
|
||||
reviewer_id: string;
|
||||
};
|
||||
Insert: {
|
||||
created_at?: string;
|
||||
dataset_id: string;
|
||||
decision: string;
|
||||
id?: string;
|
||||
reason?: string | null;
|
||||
reviewer_id: string;
|
||||
};
|
||||
Update: {
|
||||
created_at?: string;
|
||||
dataset_id?: string;
|
||||
decision?: string;
|
||||
id?: string;
|
||||
reason?: string | null;
|
||||
reviewer_id?: string;
|
||||
};
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "review_decisions_dataset_id_fkey";
|
||||
columns: ["dataset_id"];
|
||||
isOneToOne: false;
|
||||
referencedRelation: "datasets";
|
||||
referencedColumns: ["id"];
|
||||
},
|
||||
];
|
||||
};
|
||||
revisions: {
|
||||
Row: {
|
||||
asset_root: string;
|
||||
created_at: string;
|
||||
dataset_id: string;
|
||||
id: string;
|
||||
manifest: Json;
|
||||
};
|
||||
Insert: {
|
||||
asset_root: string;
|
||||
created_at?: string;
|
||||
dataset_id: string;
|
||||
id?: string;
|
||||
manifest: Json;
|
||||
};
|
||||
Update: {
|
||||
asset_root?: string;
|
||||
created_at?: string;
|
||||
dataset_id?: string;
|
||||
id?: string;
|
||||
manifest?: Json;
|
||||
};
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "revisions_dataset_id_fkey";
|
||||
columns: ["dataset_id"];
|
||||
isOneToOne: false;
|
||||
referencedRelation: "datasets";
|
||||
referencedColumns: ["id"];
|
||||
},
|
||||
];
|
||||
};
|
||||
upload_jobs: {
|
||||
Row: {
|
||||
attempts: number;
|
||||
created_at: string;
|
||||
dataset_id: string;
|
||||
error: string | null;
|
||||
id: string;
|
||||
lease_owner: string | null;
|
||||
lease_until: string | null;
|
||||
progress: number;
|
||||
state: string;
|
||||
updated_at: string;
|
||||
};
|
||||
Insert: {
|
||||
attempts?: number;
|
||||
created_at?: string;
|
||||
dataset_id: string;
|
||||
error?: string | null;
|
||||
id?: string;
|
||||
lease_owner?: string | null;
|
||||
lease_until?: string | null;
|
||||
progress?: number;
|
||||
state?: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
Update: {
|
||||
attempts?: number;
|
||||
created_at?: string;
|
||||
dataset_id?: string;
|
||||
error?: string | null;
|
||||
id?: string;
|
||||
lease_owner?: string | null;
|
||||
lease_until?: string | null;
|
||||
progress?: number;
|
||||
state?: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "upload_jobs_dataset_id_fkey";
|
||||
columns: ["dataset_id"];
|
||||
isOneToOne: true;
|
||||
referencedRelation: "datasets";
|
||||
referencedColumns: ["id"];
|
||||
},
|
||||
];
|
||||
};
|
||||
};
|
||||
Views: {
|
||||
[_ in never]: never;
|
||||
};
|
||||
Functions: {
|
||||
finalize_upload: {
|
||||
Args: { p_dataset: string; p_owner: string };
|
||||
Returns: string;
|
||||
};
|
||||
complete_job: {
|
||||
Args: {
|
||||
p_job: string;
|
||||
p_worker: string;
|
||||
p_count: number;
|
||||
p_cover: string;
|
||||
};
|
||||
Returns: undefined;
|
||||
};
|
||||
approve_pairing: {
|
||||
Args: {
|
||||
p_code: string;
|
||||
p_owner: string;
|
||||
p_token: string;
|
||||
p_hash: string;
|
||||
};
|
||||
Returns: undefined;
|
||||
};
|
||||
activate_round: {
|
||||
Args: { p_access_hash: string; p_round: number; p_session: string };
|
||||
Returns: string;
|
||||
};
|
||||
check_rate: {
|
||||
Args: { p_key: string; p_limit: number; p_seconds: number };
|
||||
Returns: boolean;
|
||||
};
|
||||
claim_job: {
|
||||
Args: { p_worker: string };
|
||||
Returns: {
|
||||
attempts: number;
|
||||
created_at: string;
|
||||
dataset_id: string;
|
||||
error: string | null;
|
||||
id: string;
|
||||
lease_owner: string | null;
|
||||
lease_until: string | null;
|
||||
progress: number;
|
||||
state: string;
|
||||
updated_at: string;
|
||||
}[];
|
||||
SetofOptions: {
|
||||
from: "*";
|
||||
to: "upload_jobs";
|
||||
isOneToOne: false;
|
||||
isSetofReturn: true;
|
||||
};
|
||||
};
|
||||
create_game: {
|
||||
Args: {
|
||||
p_access_hash: string;
|
||||
p_dataset: string;
|
||||
p_difficulty: string;
|
||||
p_owner: string | null;
|
||||
};
|
||||
Returns: string;
|
||||
};
|
||||
publish_dataset: {
|
||||
Args: {
|
||||
p_dataset: string;
|
||||
p_decision: string;
|
||||
p_reason?: string | null;
|
||||
p_reviewer: string;
|
||||
};
|
||||
Returns: undefined;
|
||||
};
|
||||
submit_guess: {
|
||||
Args: {
|
||||
p_access_hash: string;
|
||||
p_round: number;
|
||||
p_session: string;
|
||||
p_x?: number | null;
|
||||
p_z?: number | null;
|
||||
};
|
||||
Returns: Json;
|
||||
};
|
||||
};
|
||||
Enums: {
|
||||
[_ in never]: never;
|
||||
};
|
||||
CompositeTypes: {
|
||||
[_ in never]: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">;
|
||||
|
||||
type DefaultSchema = DatabaseWithoutInternals[Extract<
|
||||
keyof Database,
|
||||
"public"
|
||||
>];
|
||||
|
||||
export type Tables<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof (DefaultSchema["Tables"] & DefaultSchema["Views"])
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals;
|
||||
}
|
||||
? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
|
||||
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])
|
||||
: never = never,
|
||||
> = DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals;
|
||||
}
|
||||
? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
|
||||
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends {
|
||||
Row: infer R;
|
||||
}
|
||||
? R
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] &
|
||||
DefaultSchema["Views"])
|
||||
? (DefaultSchema["Tables"] &
|
||||
DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends {
|
||||
Row: infer R;
|
||||
}
|
||||
? R
|
||||
: never
|
||||
: never;
|
||||
|
||||
export type TablesInsert<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof DefaultSchema["Tables"]
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals;
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
|
||||
: never = never,
|
||||
> = DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals;
|
||||
}
|
||||
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
||||
Insert: infer I;
|
||||
}
|
||||
? I
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
|
||||
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
|
||||
Insert: infer I;
|
||||
}
|
||||
? I
|
||||
: never
|
||||
: never;
|
||||
|
||||
export type TablesUpdate<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof DefaultSchema["Tables"]
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals;
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
|
||||
: never = never,
|
||||
> = DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals;
|
||||
}
|
||||
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
||||
Update: infer U;
|
||||
}
|
||||
? U
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
|
||||
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
|
||||
Update: infer U;
|
||||
}
|
||||
? U
|
||||
: never
|
||||
: never;
|
||||
|
||||
export type Enums<
|
||||
DefaultSchemaEnumNameOrOptions extends
|
||||
| keyof DefaultSchema["Enums"]
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
EnumName extends DefaultSchemaEnumNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals;
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"]
|
||||
: never = never,
|
||||
> = DefaultSchemaEnumNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals;
|
||||
}
|
||||
? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName]
|
||||
: DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"]
|
||||
? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions]
|
||||
: never;
|
||||
|
||||
export type CompositeTypes<
|
||||
PublicCompositeTypeNameOrOptions extends
|
||||
| keyof DefaultSchema["CompositeTypes"]
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals;
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
|
||||
: never = never,
|
||||
> = PublicCompositeTypeNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals;
|
||||
}
|
||||
? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
|
||||
: PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"]
|
||||
? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
|
||||
: never;
|
||||
|
||||
export const Constants = {
|
||||
public: {
|
||||
Enums: {},
|
||||
},
|
||||
} as const;
|
||||
112
shared/protocol.ts
Normal file
112
shared/protocol.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { z } from "zod";
|
||||
export const boundsSchema = z
|
||||
.object({
|
||||
minX: z.number().int(),
|
||||
minZ: z.number().int(),
|
||||
maxX: z.number().int(),
|
||||
maxZ: z.number().int(),
|
||||
})
|
||||
.refine(
|
||||
(b) =>
|
||||
b.maxX > b.minX &&
|
||||
b.maxZ > b.minZ &&
|
||||
b.maxX - b.minX <= 2048 &&
|
||||
b.maxZ - b.minZ <= 2048,
|
||||
"Bounds must be positive and at most 2048 blocks per side",
|
||||
);
|
||||
export const pointSchema = z.object({
|
||||
x: z.number().finite(),
|
||||
y: z.number().finite(),
|
||||
z: z.number().finite(),
|
||||
});
|
||||
export const faces = ["px", "nx", "py", "ny", "pz", "nz"] as const;
|
||||
export const datasetSchema = z
|
||||
.object({
|
||||
format: z.literal("minegeo-dataset/v1"),
|
||||
minecraftVersion: z.literal("1.21.11"),
|
||||
dimension: z.literal("minecraft:overworld"),
|
||||
source: z.enum(["plugin", "fabric"]),
|
||||
name: z.string().min(3).max(80),
|
||||
description: z.string().max(2000).default(""),
|
||||
serverName: z.string().min(2).max(80),
|
||||
serverAddress: z.string().max(255).optional(),
|
||||
bounds: boundsSchema,
|
||||
north: z.literal("-z"),
|
||||
viewpoints: z
|
||||
.array(
|
||||
pointSchema.extend({ id: z.string().regex(/^[a-zA-Z0-9_-]{1,64}$/) }),
|
||||
)
|
||||
.min(5)
|
||||
.max(100),
|
||||
assets: z
|
||||
.array(
|
||||
z.object({
|
||||
path: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9_./-]+$/)
|
||||
.refine((s) => !s.includes("..") && !s.startsWith("/")),
|
||||
sha256: z.string().regex(/^[a-f0-9]{64}$/),
|
||||
bytes: z
|
||||
.number()
|
||||
.int()
|
||||
.positive()
|
||||
.max(50 * 1024 * 1024),
|
||||
}),
|
||||
)
|
||||
.max(4096),
|
||||
})
|
||||
.superRefine((d, ctx) => {
|
||||
if (new Set(d.viewpoints.map((p) => p.id)).size !== d.viewpoints.length)
|
||||
ctx.addIssue({ code: "custom", message: "Viewpoint IDs must be unique" });
|
||||
if (new Set(d.assets.map((a) => a.path)).size !== d.assets.length)
|
||||
ctx.addIssue({ code: "custom", message: "Asset paths must be unique" });
|
||||
for (const p of d.viewpoints)
|
||||
if (
|
||||
p.x < d.bounds.minX ||
|
||||
p.x > d.bounds.maxX ||
|
||||
p.z < d.bounds.minZ ||
|
||||
p.z > d.bounds.maxZ
|
||||
)
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: "Viewpoint outside map bounds",
|
||||
});
|
||||
});
|
||||
export type DatasetManifest = z.infer<typeof datasetSchema>;
|
||||
export type Bounds = z.infer<typeof boundsSchema>;
|
||||
export type Point = z.infer<typeof pointSchema>;
|
||||
export const difficulties = {
|
||||
easy: { seconds: 180, zoom: true, label: "Easy" },
|
||||
normal: { seconds: 90, zoom: true, label: "Normal" },
|
||||
hard: { seconds: 30, zoom: false, label: "Hard" },
|
||||
} as const;
|
||||
export type Difficulty = keyof typeof difficulties;
|
||||
export function scoreGuess(
|
||||
a: Pick<Point, "x" | "z">,
|
||||
b: Pick<Point, "x" | "z">,
|
||||
bounds: Bounds,
|
||||
) {
|
||||
const distance = Math.hypot(a.x - b.x, a.z - b.z);
|
||||
return {
|
||||
distance,
|
||||
score:
|
||||
distance <= 1
|
||||
? 5000
|
||||
: Math.round(
|
||||
5000 *
|
||||
Math.exp(
|
||||
(-5 * distance) /
|
||||
Math.hypot(
|
||||
bounds.maxX - bounds.minX,
|
||||
bounds.maxZ - bounds.minZ,
|
||||
),
|
||||
),
|
||||
),
|
||||
};
|
||||
}
|
||||
export type TerrainBlock = [number, number, number, string, string?];
|
||||
export type TerrainSnapshot = {
|
||||
bounds: Bounds;
|
||||
blocks: TerrainBlock[];
|
||||
coverage: [number, number][];
|
||||
};
|
||||
Reference in New Issue
Block a user