mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-30 05:59:51 +01:00
Compare commits
10
Commits
91ac93670f
...
43b7b4159f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43b7b4159f | ||
|
|
1aeae3b93b | ||
|
|
d657cd2360 | ||
|
|
222746be26 | ||
|
|
95dd73de52 | ||
|
|
c893fa0a03 | ||
|
|
c0e7f2f00a | ||
|
|
4dce8046cb | ||
|
|
7df2425f76 | ||
|
|
e3cf2d51cc |
No files matched your search
@@ -1,47 +1,87 @@
|
||||
# BlitzKit
|
||||
|
||||
Tools for everything and anything, World of Tanks Blitz.
|
||||
Apps for World of Tanks Blitz.
|
||||
|
||||
This GitHub repository contains the code for BlitzKit and all its services. If you are here, looking to use BlitzKit, go to https://blitzkit.app/.
|
||||
## Development
|
||||
|
||||
If you intend to contribute to the code, note that you may need to check out sensitive, closed-source submodules to locally develop certain aspects of BlitzKit. Communicate with `@tresabhi` on Discord to get started or discuss sensitive content, such as environment variables or submodules.
|
||||
Setting up a local developer environment for BlitzKit is more complex than most projects, but this section will guide you through all the steps. If you're ever lost or confused, feel free to ask for help in [the official Discord server](https://discord.gg/nDt7AjGJQH).
|
||||
|
||||
## Packages
|
||||
### Prerequisites
|
||||
|
||||
| Package | Title | Source | Memo |
|
||||
| ------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [packages/app](packages/app) | BlitzKit | [Play Store](https://play.google.com/store/apps/details?id=com.tresabhi.blitzkit), [App Store](https://apps.apple.com/app/4321), [Microsoft Store](https://apps.microsoft.com/detail/zyxw) | The native app of BlitzKit for all platforms. Unavailable from stores at this time. |
|
||||
| [packages/bot](packages/bot) | BlitzKit Discord Bot | [Discord Application Directory](https://discord.com/discovery/applications/1097673957865443370) | The one-in-all Discord bot for statistics. |
|
||||
| [packages/core](packages/core) | | | Mutual internal code shared between all other packages. |
|
||||
| [packages/i18n](packages/i18n) | | [Crowdin](https://crowdin.com/project/blitzkrieg) | Translations served as JSON shared across all other packages, including some utility code. |
|
||||
| [packages/scripts](packages/scripts) | | | CLI utilities for the development and deployment of BlitzKit services. |
|
||||
| [packages/varuna](packages/varuna) | Varuna | [NPM](https://www.npmjs.com/package/varuna) | React and Astro-oriented state management library, awaiting separation from BlitzKit. Unavailable from NPM at this time. |
|
||||
| [packages/website](packages/website) | BlitzKit | https://blitzkit.app | The BlitzKit website provides a wide range of tools, APIs, and interconnects all other services. |
|
||||
Before you touch any code, you'll need the following.
|
||||
|
||||
## Submodules
|
||||
#### 1. Linux
|
||||
|
||||
| Submodule | Title | Source | Memo |
|
||||
| ------------------------------------------------------ | --------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [submodules/blitzkit-closed](packages/blitzkit-closed) | ClosedKit | [blitzkit/closed](https://github.com/blitzkit/closed) | Closed source, sensitive code hidden from the public to prevent bad actors from abusing the game. Discuss with `@tresabhi` on Discord for **special permission** to access. |
|
||||
Because Node API for .NET doesn't resolve runtime-specific object files for libraries, the entirety of the BlitzKit project is "vendor-locked" to a single OS. GitHub Actions, responsible for BlitzKit CD/CI works best with Ubuntu, thus we must also use a Linux OS.
|
||||
|
||||
## Environment Variables
|
||||
#### 2. Bun
|
||||
|
||||
| Name | Source | BlitzKit Default | Memo |
|
||||
| ------------------------------------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `PUBLIC_WARGAMING_APPLICATION_ID` | [Wargaming Developer's Room](https://developers.wargaming.net/) | | Application ID needed to communicate with Wargaming APIs. |
|
||||
| `PUBLIC_PROMOTE_OPENTEST` | | `false` | Optional boolean dictating whether the website advertises [the opentest subdomain of BlitzKit](https://opentest.blitzkit.app/). |
|
||||
| `ALLOW_ROBOTS` | | `true` | Toggles between `Allow: /` and `Disallow: /` in the website's `/robots.txt`. This is usually `false` for all BlitzKit's subdomains. |
|
||||
| `WOTB_GLOSSARY`, `WOTB_GLOSSARY_DOMAIN`, `WOTB_GLOSSARY_PATH` | | | |
|
||||
| `PUBLIC_GOOGLE_TAG_MANAGER_ID` | [Google Tag Manager](https://tagmanager.google.com/) | `G-GL2JVHCGPQ` | Google Tag Manager's unique identifier for analytical purposes. |
|
||||
| `PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID` | [Google Analytics](https://analytics.google.com/) | `430711767` | Google Analytics' unique property ID for [BlitzKit's apex domain](https://blitzkit.app/) and subdomains. |
|
||||
| `GOOGLE_APPLICATION_CREDENTIALS` | [Google Cloud](https://cloud.google.com/) | | Google Cloud's API key, primarily used for ranking popular tanks and finding relevant tank review videos in Tankopedia. |
|
||||
| `PUBLIC_DISCORD_SERVER_ID` | [BlitzKit Discord Server](https://discord.gg/nDt7AjGJQH) | `734786591205359697` | BlitzKit Discord Server's id. |
|
||||
| `PUBLIC_ASSET_BASE` | [blitzkit/assets](https://github.com/blitzkit/assets) | `https://blitzkit.github.io/assets` | The root domain and path that serves the external assets like tank models and icons. |
|
||||
| `PUBLIC_BRANCH` | | `main` | The branch that the website is operating on. This is completely nominal. |
|
||||
| `PUBLIC_GUIDE_WEBSITE` | [GuidesBlitz](https://guidesblitz.com/) | `https://guidesblitz.com` | The website that serves the encyclopedia-like content in BlitzKit. |
|
||||
| `SERVICING` | | `false` | Wether the website is down for servicing. |
|
||||
| `DISCORD_CLIENT_ID` | [Discord Developer Portal](https://discord.com/developers/applications) | `1097673957865443370` | The BlitzKit Discord Bot's user ID. |
|
||||
| `GH_TOKEN` | [GitHub Fine-Grained Tokens](https://github.com/settings/personal-access-tokens) | | The GitHub API key used for checking out private submodules and pushing content to the asset repository. |
|
||||
| `DISCORD_TOKEN` | [Discord Developer Portal](https://discord.com/developers/applications) | | The Discord API Key for the BlitzKit Discord Bot. |
|
||||
| `DATABASE_URL` | [CockroachDB](https://www.cockroachlabs.com/) | | The CockroachDB-SQL database URL used to link Wargaming and Discord users. |
|
||||
| `WOTB_DLC_CDN` | | | |
|
||||
https://bun.com/
|
||||
|
||||
BlitzKit is written mostly in TypeScript, with packages fetched from NPM, managed by Bun.
|
||||
|
||||
#### 3. .NET
|
||||
|
||||
https://dotnet.microsoft.com/download/dotnet/9.0/
|
||||
|
||||
You'll need the .NET 9.0 SDK to build and run the interface layer between BlitzKit and your local installation of World of Tanks Blitz.
|
||||
|
||||
#### 4. Protocol Buffers
|
||||
|
||||
https://protobuf.dev/installation/
|
||||
|
||||
Both BlitzKit and World of Tanks Blitz encode their APIs with Protocol Buffers.
|
||||
|
||||
#### 5. World of Tanks Blitz
|
||||
|
||||
https://store.steampowered.com/app/444200/
|
||||
|
||||
You'll need an up-to-date installation of World of Tanks Blitz on your computer. You may use the same installation that you play, even on a Windows partition if you dual boot.
|
||||
|
||||
#### 6. UE4SS
|
||||
|
||||
...
|
||||
|
||||
#### 7. FModel
|
||||
|
||||
...
|
||||
|
||||
### Set Up
|
||||
|
||||
#### 1. Cloning
|
||||
|
||||
BlitzKit is a massive monorepo with a lot of submodules. You'll likely not need all the branches and commit history. So, clone with depth 1.
|
||||
|
||||
```bash
|
||||
git clone --depth 1 https://github.com/blitzkit/blitzkit.git
|
||||
cd blitzkit
|
||||
git submodule update --init --recursive --depth 1
|
||||
```
|
||||
|
||||
#### 2. Proto Files
|
||||
|
||||
...
|
||||
|
||||
#### 3. Mappings
|
||||
|
||||
...
|
||||
|
||||
#### 4. Environment Variables
|
||||
|
||||
...
|
||||
|
||||
#### 5. The Big One
|
||||
|
||||
...
|
||||
|
||||
### Development Server
|
||||
|
||||
...
|
||||
|
||||
### Building
|
||||
|
||||
...
|
||||
|
||||
### Branches & Pull Requests
|
||||
|
||||
...
|
||||
@@ -138,7 +138,10 @@ public partial class GameInterface
|
||||
{
|
||||
var path = texturePaths[name];
|
||||
var texture = provider.LoadPackageObject<UTexture2D>(path);
|
||||
return provider.Image(texture);
|
||||
var isRMAO = name.EndsWith("_RMAO");
|
||||
var postProcess = isRMAO ? ImagePostProcess.RMAO : ImagePostProcess.None;
|
||||
|
||||
return provider.Image(texture, postProcess);
|
||||
}
|
||||
|
||||
public HashSet<string> Files => files;
|
||||
|
||||
@@ -17,6 +17,12 @@ using SkiaSharp;
|
||||
|
||||
namespace BlitzKit.Game.Models;
|
||||
|
||||
public enum ImagePostProcess
|
||||
{
|
||||
None,
|
||||
RMAO,
|
||||
}
|
||||
|
||||
public partial class BlitzFileProvider : DefaultFileProvider
|
||||
{
|
||||
[GeneratedRegex(@"\(GameplayTags=\(\(TagName=""(.+)""\)\)\)")]
|
||||
@@ -87,13 +93,37 @@ public partial class BlitzFileProvider : DefaultFileProvider
|
||||
private readonly SKEncodedImageFormat imageFormat = SKEncodedImageFormat.Webp;
|
||||
private readonly int imageQuality = 80;
|
||||
|
||||
public byte[] Image(UTexture2D uTexture)
|
||||
public byte[] Image(UTexture2D uTexture, ImagePostProcess postProcess = ImagePostProcess.None)
|
||||
{
|
||||
var cTexture = uTexture.Decode(ETexturePlatform.DesktopMobile);
|
||||
|
||||
ArgumentNullException.ThrowIfNull(cTexture);
|
||||
|
||||
var bitmap = cTexture.ToSkBitmap();
|
||||
|
||||
return Image(bitmap, postProcess);
|
||||
}
|
||||
|
||||
private byte[] Image(SKBitmap bitmap, ImagePostProcess postProcess)
|
||||
{
|
||||
if (postProcess != ImagePostProcess.None)
|
||||
{
|
||||
for (int y = 0; y < bitmap.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bitmap.Width; x++)
|
||||
{
|
||||
var color = bitmap.GetPixel(x, y);
|
||||
|
||||
switch (postProcess)
|
||||
{
|
||||
case ImagePostProcess.RMAO:
|
||||
bitmap.SetPixel(x, y, new SKColor(color.Blue, color.Red, color.Green, color.Alpha));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var data = bitmap.Encode(imageFormat, imageQuality);
|
||||
|
||||
return data.ToArray();
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { times } from "lodash-es";
|
||||
import { Quicklime } from "quicklime";
|
||||
import { degToRad, lerp } from "three/src/math/MathUtils.js";
|
||||
|
||||
const ANGLE = Math.PI * 2 ** -2;
|
||||
|
||||
const LIGHTS_COUNT = 5;
|
||||
const THETA_OFFSET = degToRad(-152);
|
||||
const LIGHT_DISTANCE = 13;
|
||||
const LIGHT_HEIGHT_0 = 4;
|
||||
const LIGHT_HEIGHT_1 = 2;
|
||||
const INTENSITY_0 = 3.5;
|
||||
const INTENSITY_1 = 1.5;
|
||||
const HEMISPHERE_INTENSITY = 2;
|
||||
|
||||
export const transitionEvent = new Quicklime<number>(0);
|
||||
|
||||
export function TankopediaLighting() {
|
||||
return (
|
||||
<group>
|
||||
<hemisphereLight
|
||||
intensity={HEMISPHERE_INTENSITY}
|
||||
color="#ffffff"
|
||||
groundColor="#afafaf"
|
||||
/>
|
||||
|
||||
{times(LIGHTS_COUNT, (index) => {
|
||||
const x = index / (LIGHTS_COUNT - 1);
|
||||
const theta = 2 * Math.PI * (index / LIGHTS_COUNT) + THETA_OFFSET;
|
||||
const position = [
|
||||
LIGHT_DISTANCE * Math.sin(theta),
|
||||
lerp(LIGHT_HEIGHT_0, LIGHT_HEIGHT_1, x),
|
||||
LIGHT_DISTANCE * Math.cos(theta),
|
||||
] as const;
|
||||
const intensity = lerp(INTENSITY_0, INTENSITY_1, x);
|
||||
|
||||
return (
|
||||
<spotLight
|
||||
castShadow
|
||||
userData={{ index }}
|
||||
key={index}
|
||||
position={position}
|
||||
intensity={intensity}
|
||||
penumbra={1}
|
||||
decay={0}
|
||||
color="#ffffff"
|
||||
angle={ANGLE}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
@@ -1,14 +1,11 @@
|
||||
import {
|
||||
VisualChanges_TankPart,
|
||||
type StageParameters,
|
||||
} from "@protos/blitz_static_tank_upgrade_single_stage";
|
||||
import { type StageParameters } from "@protos/blitz_static_tank_upgrade_single_stage";
|
||||
import { OrbitControls } from "@react-three/drei";
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import { useProtagonist } from "../../hooks/useProtagonist";
|
||||
import { useTankChassisModel } from "../../hooks/useTankChassisModel";
|
||||
import { useTankGunModel } from "../../hooks/useTankGunModel";
|
||||
import { useTankHullModel } from "../../hooks/useTankHullModel";
|
||||
import { useTankTurretModel } from "../../hooks/useTankTurretModel";
|
||||
import { TankopediaLighting } from "../TankopediaLighting";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
interface TankopediaSandboxProps {
|
||||
@@ -33,17 +30,6 @@ export function TankopediaSandbox({ parameters }: TankopediaSandboxProps) {
|
||||
}
|
||||
|
||||
function Content({ parameters }: TankopediaSandboxProps) {
|
||||
const tank = useProtagonist();
|
||||
|
||||
const selectedTurret = parameters.visual_changes.find(
|
||||
({ tank_part }) => tank_part === VisualChanges_TankPart.TANK_PART_TURRET,
|
||||
)!.name;
|
||||
const selectedGun = parameters.visual_changes.find(
|
||||
({ tank_part }) => tank_part === VisualChanges_TankPart.TANK_PART_GUN,
|
||||
)!.name;
|
||||
|
||||
const collision = false;
|
||||
|
||||
const hull = useTankHullModel();
|
||||
const chassis = useTankChassisModel();
|
||||
const turret = useTankTurretModel(parameters);
|
||||
@@ -64,23 +50,11 @@ function Content({ parameters }: TankopediaSandboxProps) {
|
||||
<primitive object={gun} />
|
||||
</group>
|
||||
|
||||
<spotLight
|
||||
castShadow
|
||||
position={[3, 10, 5]}
|
||||
intensity={2 ** 10}
|
||||
penumbra={1}
|
||||
/>
|
||||
<spotLight
|
||||
castShadow
|
||||
position={[-3, -10, -5]}
|
||||
intensity={2 ** 10}
|
||||
penumbra={1}
|
||||
/>
|
||||
<ambientLight />
|
||||
<TankopediaLighting />
|
||||
|
||||
<mesh receiveShadow rotation={[-Math.PI / 2, 0, 0]}>
|
||||
<planeGeometry args={[2 ** 8, 2 ** 8]} />
|
||||
<meshStandardMaterial color="gray" />
|
||||
<planeGeometry args={[2 ** 9, 2 ** 9]} />
|
||||
<meshStandardMaterial color="black" />
|
||||
</mesh>
|
||||
|
||||
<OrbitControls />
|
||||
|
||||
@@ -1,33 +1,43 @@
|
||||
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);
|
||||
const [root] = gltf.scene.children;
|
||||
|
||||
root.traverse((child) => {
|
||||
if (
|
||||
child instanceof Mesh &&
|
||||
child.material instanceof MeshStandardMaterial
|
||||
) {
|
||||
// TODO: remove this hack and fix the gltf
|
||||
child.material.vertexColors = false;
|
||||
useEffect(() => {
|
||||
for (const material of Object.values(gltf.materials)) {
|
||||
if (material instanceof MeshStandardMaterial) {
|
||||
// TODO: remove this hack and fix the gltf
|
||||
material.vertexColors = false;
|
||||
material.needsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
child.castShadow = true;
|
||||
child.receiveShadow = true;
|
||||
});
|
||||
console.log(gltf.nodes);
|
||||
|
||||
for (const child of root.children) {
|
||||
if (child.name in map) {
|
||||
child.visible = map[child.name];
|
||||
continue;
|
||||
for (const node of Object.values(gltf.nodes)) {
|
||||
if (node instanceof Mesh) {
|
||||
node.castShadow = true;
|
||||
node.receiveShadow = true;
|
||||
}
|
||||
}
|
||||
}, [url]);
|
||||
|
||||
child.visible = false;
|
||||
console.warn(`Root child ${child.name} isn't recognized`);
|
||||
}
|
||||
useEffect(() => {
|
||||
for (const child of root.children) {
|
||||
if (child.name in map) {
|
||||
child.visible = map[child.name];
|
||||
continue;
|
||||
}
|
||||
|
||||
child.visible = false;
|
||||
console.warn(`Root child ${child.name} isn't recognized`);
|
||||
}
|
||||
}, [url, map]);
|
||||
|
||||
return root;
|
||||
}
|
||||
Reference in new issue
Block a user