mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 12:09:51 +02:00
Compare commits
7
Commits
61abddc41b
...
d2ee528c52
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2ee528c52 | ||
|
|
0531fbf3ef | ||
|
|
415aab3824 | ||
|
|
f16e508e59 | ||
|
|
3299ce069e | ||
|
|
6395dd3f3d | ||
|
|
9536cd2d39 |
No files matched your search
@@ -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
@@ -3,7 +3,7 @@
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"csharpier": {
|
||||
"version": "1.2.6",
|
||||
"version": "1.3.0",
|
||||
"commands": [
|
||||
"csharpier"
|
||||
],
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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!}
|
||||
|
||||
@@ -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;
|
||||
@@ -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,3 +1,2 @@
|
||||
cd packages/game
|
||||
dotnet build -clp:ErrorsOnly
|
||||
find ./bin -type f | sort
|
||||
dotnet build
|
||||
Reference in new issue
Block a user