Compare commits

...
4 Commits
Author SHA1 Message Date
tresabhi c6c0e02fea ao maps 2026-06-13 20:26:43 -05:00
tresabhi 028f4230e5 fix banner paths 2026-06-13 20:02:27 -05:00
tresabhi cf31ad8f7a implement buffer proxy caching 2026-06-13 15:08:00 -05:00
tresabhi 9418526897 fix r3f types 2026-06-13 14:53:56 -05:00
18 changed files with 63 additions and 44 deletions

No files matched your search

-4
View File
@@ -206,8 +206,6 @@
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.6.1",
"@types/lodash-es": "^4.17.12",
"@types/react": "^19.2.15",
"@types/react-dom": "^19.2.3",
"@types/three": "^0.184.1",
"@vite-pwa/astro": "^1.2.0",
"@wooorm/starry-night": "^3.9.0",
@@ -3109,8 +3107,6 @@
"@blitzkit/website-ue/@gltf-transform/core": ["@gltf-transform/[email protected]", "", { "dependencies": { "property-graph": "^4.1.0" } }, "sha512-cOPxOhHFFz5hwmix+li1+Nnq5qMV/QD3fTCsVlApxxFACtFdjkt2R/juseD4gvZ7D2c/yl6OilKH0pvI735YyQ=="],
"@blitzkit/website-ue/@types/react": ["@types/[email protected]", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q=="],
"@blitzkit/website-ue/astro": ["[email protected]", "", { "dependencies": { "@astrojs/compiler": "^2.13.0", "@astrojs/internal-helpers": "0.7.6", "@astrojs/markdown-remark": "6.3.11", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.3.1", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.1.1", "cssesc": "^3.0.0", "debug": "^4.4.3", "deterministic-object-hash": "^2.0.2", "devalue": "^5.6.2", "diff": "^8.0.3", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.27.3", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.4.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.1", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.1", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.3", "shiki": "^3.21.0", "smol-toml": "^1.6.0", "svgo": "^4.0.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.3", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^6.4.1", "vitefu": "^1.1.1", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", "zod-to-json-schema": "^3.25.1", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "astro.js" } }, "sha512-TnFwLnAXty5MXKPDGuKXqK4AMBXG+FH6RUdK7Oyc3gyfNoFIthT+4eRbzOK43bdRlLaZuxgciDSjgtggZ3OtGQ=="],
"@blitzkit/website-ue/astro-favicons": ["[email protected]", "", { "dependencies": { "favilib": "^1.0.5", "ultrahtml": "^1.5.3" }, "peerDependencies": { "astro": ">= 4.0.0" } }, "sha512-9YfNWaBEFz1nCHy4P1l4wfPRvuLe4qHuHovIcqD5HYfZeW82wyVG1+CZUVGhcjwbRCVkGRJ3G+CKTyX9BcoI5w=="],
+4 -2
View File
@@ -240,14 +240,16 @@ public partial class GameInterface
if (dataTable == null)
{
throw new ArgumentException($"Unknown tank part: {part}");
Console.WriteLine($"Unknown tank part (no matching data table): {part}");
return [];
}
dataTable.TryGetDataTableRow(part, StringComparison.Ordinal, out var row);
if (row == null)
{
throw new ArgumentException($"Unknown tank part: {part}");
Console.WriteLine($"Unknown tank part (no matching row): {part}");
return [];
}
var visualData = row.Get<UObject>("VisualData");
+10 -7
View File
@@ -34,11 +34,11 @@ public class MonoGltf
readonly MaterialBuilder emptyMaterial = new("empty_material");
readonly ExporterOptions options = new() { TextureFormat = ETextureFormat.Jpeg };
public static readonly Dictionary<string, KnownChannel> knownChannels = new()
public static readonly Dictionary<string, KnownChannel[]> knownChannels = new()
{
{ "BaseColor", KnownChannel.BaseColor },
{ "Normal", KnownChannel.Normal },
{ "RMAO", KnownChannel.MetallicRoughness },
{ "BaseColor", [KnownChannel.BaseColor] },
{ "Normal", [KnownChannel.Normal] },
{ "RMAO", [KnownChannel.MetallicRoughness, KnownChannel.Occlusion] },
// { "CDE", ?? },
};
readonly byte[] stubBytes = File.ReadAllBytes("../game/stub/small.png");
@@ -152,7 +152,7 @@ public class MonoGltf
foreach (var parameterTexture in materialData.Parameters.Textures)
{
if (knownChannels.TryGetValue(parameterTexture.Key, out var channel))
if (knownChannels.TryGetValue(parameterTexture.Key, out var channels))
{
var texture = parameterTexture.Value;
var name = Path.GetFileNameWithoutExtension(texture.GetPathName());
@@ -163,8 +163,11 @@ public class MonoGltf
var uniqueBytes = (byte[])stubBytes.Clone();
var stubImage = new MemoryImage(uniqueBytes);
materialBuilder.WithChannelImage(channel, stubImage);
materialBuilder.GetChannel(channel).Texture.PrimaryImage.Name = path;
foreach (var channel in channels)
{
materialBuilder.WithChannelImage(channel, stubImage);
materialBuilder.GetChannel(channel).Texture.PrimaryImage.Name = path;
}
}
}
}
-2
View File
@@ -27,8 +27,6 @@
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.6.1",
"@types/lodash-es": "^4.17.12",
"@types/react": "^19.2.15",
"@types/react-dom": "^19.2.3",
"@types/three": "^0.184.1",
"@vite-pwa/astro": "^1.2.0",
"@wooorm/starry-night": "^3.9.0",
+1
View File
@@ -0,0 +1 @@
/// <reference types="@react-three/fiber" />
@@ -0,0 +1,20 @@
import { notFoundResponse } from "./responses";
const cache = new Map<string, Uint8Array<ArrayBuffer>>();
export function bufferProxy(cook: () => number[], key: string) {
let array = cache.get(key);
if (array === undefined) {
const bytes = cook();
array = new Uint8Array(bytes);
if (import.meta.env.DEV) {
cache.set(key, array);
}
}
if (array.length === 0) return notFoundResponse;
return new Response(array);
}
@@ -1,4 +1,5 @@
import sharp from "sharp";
import { bufferProxy } from "./bufferProxy";
export async function imageProxy(url: string) {
if (import.meta.env.DEV) {
@@ -33,7 +33,7 @@ const strings = await api.strings(locale);
width: 100%;
height: calc(100% - var(--space-5) / 2);
background-image: url(/assets/home-hero.webp);
background-image: url(/media/home-hero.webp);
background-size: cover;
background-position: top;
@@ -18,6 +18,7 @@ export function TankopediaSandbox({ parameters }: TankopediaSandboxProps) {
<div className={styles.sandbox}>
<Canvas
// scene={{ fog: new FogExp2("black", 0.05) }}
camera={{ fov: 40 }}
shadows
className={styles.canvas}
frameloop="demand"
@@ -42,11 +43,11 @@ function Content({ parameters }: TankopediaSandboxProps) {
<primitive object={hull} />
</group>
<group position={[0, 0, 5]}>
<group position={[19.515463 / 100, 129.6958 / 100, 0.0039617573 / 100]}>
<primitive object={turret} />
</group>
<group position={[0, 0, -5]}>
<group position={[154.39078 / 100, 178.7401 / 100, 0.11611016 / 100]}>
<primitive object={gun} />
</group>
@@ -74,14 +74,14 @@ const toolStrings = strings.tools[tool.id as keyof typeof strings.tools];
<div
class="tool-banner"
style={{
backgroundImage: `url(/assets/tools/${tool.id}.webp)`,
backgroundImage: `url(/media/tools/banners/${tool.id}.webp)`,
backgroundPosition: tool.banner_background_position ?? "top",
}}
>
<div class="content">
<div
class="pattern"
style={{ backgroundImage: `url(/assets/patterns/${tool.id}.svg)` }}
style={{ backgroundImage: `url(/media/patterns/${tool.id}.svg)` }}
>
</div>
@@ -2,7 +2,6 @@ import { useLoader } from "@react-three/fiber";
import { useEffect } from "react";
import { Mesh, MeshStandardMaterial } from "three";
import { GLTFLoader } from "three-stdlib";
import { OnObjectUpdate } from "three/src/nodes/utils/EventNode.js";
export function useTankModel(url: string, map: Record<string, boolean>) {
const gltf = useLoader(GLTFLoader, url);
@@ -17,8 +16,6 @@ export function useTankModel(url: string, map: Record<string, boolean>) {
}
}
console.log(gltf.nodes);
for (const node of Object.values(gltf.nodes)) {
if (node instanceof Mesh) {
node.castShadow = true;
@@ -4,6 +4,7 @@ import { mixStaticPaths } from "../../../astro/mixStaticPaths";
import { game } from "../../../game/game";
import { getStaticPaths as _getStaticPaths } from "../_index";
import { assertDiscoveryTag } from "../../../game/assertDiscoveryTag";
import { bufferProxy } from "../../../api/bufferProxy";
export const getStaticPaths = mixStaticPaths(_getStaticPaths, () => {
return api.metadata
@@ -11,13 +12,10 @@ export const getStaticPaths = mixStaticPaths(_getStaticPaths, () => {
.map(({ id }) => ({ params: { id } }));
});
export function GET({ params }: APIContext<never, { id: string }>) {
const item = api.metadata.item(params.id);
const assetsDiscovery = item.BlitzStaticAssetsDiscovery();
const tag = assertDiscoveryTag(assetsDiscovery);
const icon = game!.consumableIcon(tag);
const array = new Uint8Array(icon);
return new Response(array);
return bufferProxy(() => game!.consumableIcon(tag), `consumable-icon-${tag}`);
}
@@ -5,6 +5,7 @@ import { mixStaticPaths } from "../../../astro/mixStaticPaths";
import { game } from "../../../game/game";
import { getStaticPaths as _getStaticPaths } from "../_index";
import { assertDiscoveryTag } from "../../../game/assertDiscoveryTag";
import { bufferProxy } from "../../../api/bufferProxy";
export const getStaticPaths = mixStaticPaths(_getStaticPaths, () => {
return api.metadata
@@ -20,8 +21,6 @@ export function GET({ params }: APIContext<never, { id: string }>) {
const item = api.metadata.item(params.id);
const assetsDiscovery = item.BlitzStaticAssetsDiscovery();
const tag = assertDiscoveryTag(assetsDiscovery);
const icon = game!.equipmentIcon(tag);
const array = new Uint8Array(icon);
return new Response(array);
return bufferProxy(() => game!.equipmentIcon(tag), `equipment-icon-${tag}`);
}
@@ -5,6 +5,7 @@ import { mixStaticPaths } from "../../../../astro/mixStaticPaths";
import { game } from "../../../../game/game";
import { clientUnmounted } from "../../../../game/unmounted";
import { getStaticPaths as _getStaticPaths } from "../../_index";
import { bufferProxy } from "../../../../api/bufferProxy";
export const getStaticPaths = mixStaticPaths(_getStaticPaths, async () => {
return Object.values(Nation)
@@ -20,7 +21,6 @@ export async function GET({ params }: APIContext<never, { id: string }>) {
const enumName = Nation[Number(params.id)];
const nationName = enumName.replace("NATION_", "").toLowerCase();
const flag = game!.flag(nationName);
return new Response(new Uint8Array(flag));
return bufferProxy(() => game!.flag(nationName), `flag-card-${nationName}`);
}
@@ -6,6 +6,7 @@ import { assertDiscoveryTag } from "../../../../game/assertDiscoveryTag";
import { game } from "../../../../game/game";
import { clientUnmounted } from "../../../../game/unmounted";
import { getStaticPaths as _getStaticPaths } from "../../_index";
import { bufferProxy } from "../../../../api/bufferProxy";
export const getStaticPaths = mixStaticPaths(_getStaticPaths, async () => {
const { list } = await api.tankList();
@@ -18,8 +19,6 @@ export async function GET({ params }: APIContext<never, { id: string }>) {
const item = api.metadata.item(params.id);
const assetDiscovery = item.BlitzStaticAssetsDiscovery();
const tag = assertDiscoveryTag(assetDiscovery);
const icon = game!.tankBigIcon(tag);
const array = new Uint8Array(icon);
return new Response(array);
return bufferProxy(() => game!.tankBigIcon(tag), `tank-icon-big-${tag}`);
}
@@ -5,6 +5,7 @@ import { mixStaticPaths } from "../../../../astro/mixStaticPaths";
import { getStaticPaths as _getStaticPaths } from "../../_index";
import { assertDiscoveryTag } from "../../../../game/assertDiscoveryTag";
import { game } from "../../../../game/game";
import { bufferProxy } from "../../../../api/bufferProxy";
export const getStaticPaths = mixStaticPaths(_getStaticPaths, async () => {
const tanks = await api.tanks();
@@ -36,8 +37,9 @@ export async function GET({
const item = api.metadata.item(params.id);
const assetDiscovery = item.BlitzStaticAssetsDiscovery();
const tag = assertDiscoveryTag(assetDiscovery);
const bytes = game!.tankPart(tag, params.part);
const array = new Uint8Array(bytes);
return new Response(array);
return bufferProxy(
() => game!.tankPart(tag, params.part),
`tank-part-${tag}-${params.part}`,
);
}
@@ -2,13 +2,15 @@ import type { APIContext } from "astro";
import { mixStaticPaths } from "../../astro/mixStaticPaths";
import { game } from "../../game/game";
import { getStaticPaths as _getStaticPaths } from "./_index";
import { bufferProxy } from "../../api/bufferProxy";
export const getStaticPaths = mixStaticPaths(_getStaticPaths, () => {
return game!.textures.map((name) => ({ params: { name } }));
});
export function GET({ params }: APIContext<never, { name: string }>) {
const image = game!.texture(params.name);
const array = new Uint8Array(image);
return new Response(array);
return bufferProxy(
() => game!.texture(params.name),
`texture-${params.name}`,
);
}
+3 -3
View File
@@ -2,7 +2,7 @@
"extends": ["../../tsconfig.json", "astro/tsconfigs/base"],
"compilerOptions": {
"paths": {
"@protos/*": ["../../packages/closed/protos/*"]
}
}
"@protos/*": ["../../packages/closed/protos/*"],
},
},
}