Compare commits

...
7 Commits
Author SHA1 Message Date
TrèsAbhi d2ee528c52 reduce 2026-06-10 22:58:38 -05:00
tresabhi 0531fbf3ef always cancel 2026-06-10 20:23:56 -05:00
tresabhi 415aab3824 chunk upload 2026-06-10 20:23:00 -05:00
tresabhi f16e508e59 keys 2026-06-10 20:15:10 -05:00
tresabhi 3299ce069e texture caching 2026-06-10 20:12:18 -05:00
tresabhi 6395dd3f3d disable fail fast 2026-06-10 19:57:17 -05:00
tresabhi 9536cd2d39 update avatars 2026-06-10 19:55:53 -05:00
13 changed files with 117 additions and 116 deletions

No files matched your search

+8 -1
View File
@@ -11,7 +11,7 @@ permissions:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref == 'refs/heads/main' }}
cancel-in-progress: true
env:
# blitz
@@ -39,6 +39,7 @@ jobs:
name: Build chunk ${{ matrix.chunk }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
chunk: [pages, api, media, models, textures]
@@ -86,3 +87,9 @@ jobs:
run: bun run build-website
env:
BUILD_CHUNK: ${{ matrix.chunk }}
- name: Upload artifact
uses: actions/upload-pages-artifact@v5
with:
name: chunk-${{ matrix.chunk }}
path: ${{ matrix.chunk == 'pages' && 'dist/website' || format('dist/website/{0}', matrix.chunk) }}
+1 -1
View File
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"csharpier": {
"version": "1.2.6",
"version": "1.3.0",
"commands": [
"csharpier"
],
+68 -60
View File
@@ -1,4 +1,5 @@
using System.Text.RegularExpressions;
using System.Text.Json;
using System.Text.RegularExpressions;
using BlitzKit.Game.Models;
using CUE4Parse_Conversion;
using CUE4Parse_Conversion.Materials;
@@ -28,22 +29,31 @@ public partial class GameInterface
public GameInterface(string directory, string map, string temp)
{
Console.WriteLine("GameInterface constructor called");
provider = new(directory, map, temp);
Console.WriteLine("Provider created");
files = [.. provider.Files.Keys];
Console.WriteLine("Files loaded");
}
readonly Dictionary<string, string> texturePaths = new();
readonly string textureCache = "../../temp/textures.json";
public void DiscoverTextures(string[] tankTags)
{
Console.WriteLine("Discovering textures...");
if (File.Exists(textureCache))
{
Console.WriteLine("Found textures cache");
var content = File.ReadAllText(textureCache);
var cached = JsonSerializer.Deserialize<Dictionary<string, string>>(content);
foreach (var (key, value) in cached)
{
texturePaths[key] = value;
}
return;
}
Console.WriteLine("Textures cache not found, building...");
var options = new ExporterOptions() { TextureFormat = ETextureFormat.Jpeg };
@@ -109,6 +119,13 @@ public partial class GameInterface
}
Console.WriteLine($"Discovered {texturePaths.Count} textures");
Directory.CreateDirectory(Path.GetDirectoryName(textureCache));
var json = JsonSerializer.Serialize(texturePaths);
File.WriteAllText(textureCache, json);
Console.WriteLine("Saved textures cache");
}
public List<string> Textures => [.. texturePaths.Keys];
@@ -200,62 +217,53 @@ public partial class GameInterface
public byte[] TankPart(string tag, string part)
{
try
var pda = provider.Discovered<UPrimaryDataAsset>(tag);
UDataTable? dataTable = null;
foreach (var (prefix, name) in tankPartPrefixes)
{
var pda = provider.Discovered<UPrimaryDataAsset>(tag);
UDataTable? dataTable = null;
foreach (var (prefix, name) in tankPartPrefixes)
if (!part.StartsWith(prefix))
{
if (!part.StartsWith(prefix))
{
continue;
}
dataTable = pda.Get<UDataTable>(name);
break;
continue;
}
if (dataTable == null)
{
throw new ArgumentException($"Unknown tank part: {part}");
}
dataTable.TryGetDataTableRow(part, StringComparison.Ordinal, out var row);
if (row == null)
{
throw new ArgumentException($"Unknown tank part: {part}");
}
var visualData = row.Get<UObject>("VisualData");
var meshSettings = visualData.Get<FStructFallback>("MeshSettings");
var meshes = new List<UStaticMesh>();
foreach (var property in meshSettings.Properties)
{
var name = property.Name.Text;
if (name == "CollisionMesh")
{
continue;
}
if (meshSettings.TryGet<UStaticMesh>(name, out var mesh))
{
meshes.Add(mesh);
}
}
var gltf = new MonoGltf(meshes);
return gltf.Write($"{tag}/{part}");
}
catch (Exception e)
{
Console.WriteLine(e);
dataTable = pda.Get<UDataTable>(name);
break;
}
return [];
if (dataTable == null)
{
throw new ArgumentException($"Unknown tank part: {part}");
}
dataTable.TryGetDataTableRow(part, StringComparison.Ordinal, out var row);
if (row == null)
{
throw new ArgumentException($"Unknown tank part: {part}");
}
var visualData = row.Get<UObject>("VisualData");
var meshSettings = visualData.Get<FStructFallback>("MeshSettings");
var meshes = new List<UStaticMesh>();
foreach (var property in meshSettings.Properties)
{
var name = property.Name.Text;
if (name == "CollisionMesh")
{
continue;
}
if (meshSettings.TryGet<UStaticMesh>(name, out var mesh))
{
meshes.Add(mesh);
}
}
var gltf = new MonoGltf(meshes);
return gltf.Write($"{tag}/{part}");
}
}
+7 -19
View File
@@ -1,19 +1,19 @@
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using CUE4Parse_Conversion.Textures;
using CUE4Parse.Compression;
using CUE4Parse.FileProvider;
using CUE4Parse.MappingsProvider;
using CUE4Parse.UE4.AssetRegistry;
using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.UE4.Exceptions;
using CUE4Parse.UE4.Objects.Engine;
using CUE4Parse.UE4.Objects.UObject;
using CUE4Parse.UE4.Readers;
using CUE4Parse.UE4.Versions;
using CUE4Parse.Utils;
using SkiaSharp;
using CUE4Parse.UE4.Readers;
using CUE4Parse.UE4.AssetRegistry;
using System.Text.RegularExpressions;
using CUE4Parse.UE4.Assets.Exports;
namespace BlitzKit.Game.Models;
@@ -32,28 +32,15 @@ public partial class BlitzFileProvider : DefaultFileProvider
pathComparer: StringComparer.OrdinalIgnoreCase
)
{
Console.WriteLine("BlitzFileProvider constructor called");
MappingsContainer = new FileUsmapTypeMappingsProvider(map);
Console.WriteLine("MappingsContainer initialized");
Initialize();
Console.WriteLine("Initialize called");
Mount();
Console.WriteLine("Mount called");
string oodlePath = $"{temp}/oodle.so";
// string oodlePath = $"{temp}/oodle.so";
// OodleHelper.DownloadOodleDll(ref oodlePath!);
// Console.WriteLine("DownloadOodleDll called");
OodleHelper.Initialize("../../stupid/oodle.so");
Console.WriteLine("Initialize called");
if (TryGetGameFile("Blitz/AssetRegistry.bin", out var file))
{
var bytes = file.Read();
@@ -78,7 +65,8 @@ public partial class BlitzFileProvider : DefaultFileProvider
}
}
public T Discovered<T>(string path) where T : UObject
public T Discovered<T>(string path)
where T : UObject
{
return LoadPackageObject<T>(discovered[path]);
}
+1 -1
View File
@@ -28,7 +28,7 @@ export default defineConfig({
outDir: "../../dist/website",
prefetch: { defaultStrategy: "hover", prefetchAll: true },
build: { concurrency: 4 },
build: { concurrency: 2 },
adapter: undefined,
integrations: [favicons(), react()],
@@ -77,7 +77,7 @@ function Details({ avatars }: DetailsProps) {
<div
className={styles.image}
style={{
backgroundImage: `url(/api/avatars/${avatar.name}.webp)`,
backgroundImage: `url(/media/avatars/${avatar.id}.webp)`,
}}
/>
<Badge
@@ -102,7 +102,7 @@ function Details({ avatars }: DetailsProps) {
<Link
underline="never"
href={`/api/avatars/${avatar.name}.webp`}
href={`/media/avatars/${avatar.id}.webp`}
download
>
<Button>
@@ -128,7 +128,7 @@ function Series(props: PropsWithSkeleton<SeriesProps>) {
{!props.skeleton &&
props.avatars.slice(-MAX_CARDS).map((avatar, index) => (
<div
key={avatar.name}
key={avatar.id}
className={styles.entry}
style={{
filter: `brightness(${(index + 1) / count})`,
@@ -136,7 +136,7 @@ function Series(props: PropsWithSkeleton<SeriesProps>) {
left: `calc(${index} * ${CARD_TRIM})`,
width: `calc(100% - ${CARD_TRIM} * ${count - 1})`,
height: `calc(100% - ${CARD_TRIM} * ${count - 1})`,
backgroundImage: `url(/api/avatars/${avatar.name}.webp)`,
backgroundImage: `url(/media/avatars/${avatar.id}.webp)`,
}}
/>
))}
@@ -1,10 +1,10 @@
import type { Avatar } from "@protos/avatar";
import fuzzysort from "fuzzysort";
import { useMemo } from "react";
import { api } from "../../api/dynamic";
import { useAwait } from "../../hooks/useAwait";
import { useGameStrings } from "../../hooks/useGameStrings";
import { useLocale } from "../../hooks/useLocale";
import type { Avatar } from "../../protos/avatar";
import { Avatars } from "../../stores/avatars";
import { AvatarCard } from "../AvatarCard";
import { IncrementalLoader } from "../IncrementalLoader";
@@ -12,7 +12,7 @@ import "./index.css";
const { go } = fuzzysort;
const indexingPattern = /.+_(\d+)$/;
const indexingPattern = /ProfileAvatarEntity\..+_(\d+)$/;
export function AvatarsList() {
const locale = useLocale();
@@ -38,6 +38,7 @@ export function AvatarsList() {
}, []);
const ordered = useMemo(() => {
const array = Array.from(groups.entries());
return array
.sort((a, b) => a[0].localeCompare(b[0], locale))
.map(([name, avatars]) => ({
@@ -49,8 +50,8 @@ export function AvatarsList() {
let diff = gradeA - gradeB;
if (diff === 0) {
const matchesA = a.name.match(indexingPattern);
const matchesB = b.name.match(indexingPattern);
const matchesA = a.id.match(indexingPattern);
const matchesB = b.id.match(indexingPattern);
if (matchesA && matchesB) {
const indexA = Number(matchesA[1]);
@@ -29,7 +29,13 @@ export function TankopediaCharacteristics({
if (!shouldRender) return null;
return <Group group={group} characteristics={characteristics} />;
return (
<Group
key={group.name}
group={group}
characteristics={characteristics}
/>
);
})}
</div>
</div>
@@ -65,9 +71,9 @@ function Group({ group, characteristics }: GroupProps) {
{
(
strings.tanks.characteristics.titles as Record<string, string>
)[group.group]
)[group.name]
}
<img src={`/media/groups/${group.group}.png`} />
<img src={`/media/groups/${group.name}.png`} />
</div>
</Heading>
</div>
@@ -175,8 +175,10 @@ function Consumables({ characteristics }: ConsumablesProps) {
{Object.entries(consumables.consumables).map(
([id, { compatibility, consumable, purchase }]) => {
if (!isCompatible(compatibility!)) return null;
return (
<Consumable
key={id}
id={id}
consumable={consumable!}
purchase={purchase!}
-10
View File
@@ -7,20 +7,14 @@ import { assertDiscoveryTag } from "./assertDiscoveryTag";
let _game: GameInterface | null = null;
if (!clientUnmounted && import.meta.env.SSR) {
console.log("Pre-import");
const { GameInterface } = await import("@blitzkit/game");
console.log("Pre-creation");
_game = new GameInterface(
assertSecret(import.meta.env.PUBLIC_WOTB_CLIENT),
assertSecret(import.meta.env.WOTB_USMAP),
"../../temp",
);
console.log("Pre-tags");
const tankTags: string[] = [];
for (const item of api.metadata.group("TankEntity")) {
@@ -30,11 +24,7 @@ if (!clientUnmounted && import.meta.env.SSR) {
tankTags.push(tag);
}
console.log("Pre-discovery");
_game.discoverTextures(tankTags);
console.log("Post-discovery");
}
export const game = _game;
+2 -2
View File
@@ -19,8 +19,8 @@ export enum TankopediaCompare {
const groups: Record<string, boolean> = {};
for (const { group } of characteristicsOrder) {
groups[group] = true;
for (const { name } of characteristicsOrder) {
groups[name] = true;
}
export const Tankopedia = new Varuna<Tankopedia, Tank>((tank) => ({
@@ -29,7 +29,7 @@ export interface ToyRenderConfig {
}
export type CharacteristicsGroup = {
group: string;
name: string;
render: CharacteristicsGroupRender;
order: (CharacteristicRenderConfig | ToyRenderConfig)[];
};
@@ -101,7 +101,7 @@ export const characteristicsOrder: CharacteristicsGroup[] = [
// },
{
group: "gun",
name: "gun",
render: CharacteristicsGroupRender.Statistical,
order: [
{
@@ -121,7 +121,7 @@ export const characteristicsOrder: CharacteristicsGroup[] = [
},
{
group: "clip",
name: "clip",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "clipping_potential", localize: true, units: "hp" },
@@ -139,7 +139,7 @@ export const characteristicsOrder: CharacteristicsGroup[] = [
},
{
group: "shell",
name: "shell",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "penetration", units: "mm" },
@@ -157,7 +157,7 @@ export const characteristicsOrder: CharacteristicsGroup[] = [
},
{
group: "accuracy",
name: "accuracy",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "aim_time", decimals: 1, units: "s" },
@@ -168,7 +168,7 @@ export const characteristicsOrder: CharacteristicsGroup[] = [
},
{
group: "flexibility",
name: "flexibility",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "gun_depression", decimals: 0, units: "deg" },
@@ -181,7 +181,7 @@ export const characteristicsOrder: CharacteristicsGroup[] = [
},
{
group: "maneuverability",
name: "maneuverability",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "speed_forward", units: "km_h" },
@@ -207,7 +207,7 @@ export const characteristicsOrder: CharacteristicsGroup[] = [
},
{
group: "survivability",
name: "survivability",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "health", units: "hp" },
@@ -222,7 +222,7 @@ export const characteristicsOrder: CharacteristicsGroup[] = [
},
{
group: "concealment",
name: "concealment",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "view_range", units: "m" },
+1 -2
View File
@@ -1,3 +1,2 @@
cd packages/game
dotnet build -clp:ErrorsOnly
find ./bin -type f | sort
dotnet build