diff --git a/bun.lockb b/bun.lockb index 5ef51acc4..bfd8bd34e 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/docs/technical/cdon.md b/docs/technical/cdon.md deleted file mode 100644 index ddb36bed5..000000000 --- a/docs/technical/cdon.md +++ /dev/null @@ -1,99 +0,0 @@ -# Cedilla Object Notation - -1. JSON compressed as CDON, written directly to disk (a direct 50% reduction). -2. CDON further compressed with LZ4 (a clean 90% reduction). -3. The raw JSON file as fetched from a random data source. -4. CDON decompressed once again to JSON. Note that this file is a little larger than the original JSON file as it contains a lot more decimal places due to float precision (this shouldn't affect the size in memory). - -![an image of file sizes](https://i.imgur.com/AHJiFpq.png) - -FS stands for "Fast String". - -```cpp -primary CedillaObjectNotation { - utf8"CDON" magic; - Header header; - primary Body body; -} - -Header { - uint16 version; // always 1 - FSFormat fsFormat; - StringTable stringTable; -} - -FSFormat enum { - Format8, - Format16, - Format32, -} - -FSPrimitive match(fsFormat) { - Format8: uint8; - Format16: uint16; - Format32: uint32; -} - -StringTable { - FSPrimitive count; - String[count] strings; -} - -Body { - primary Value object; -} - -ValueType enum { - Null, - Boolean, - Uint8, - Uint16, - Uint32, - Uint64, - Int8, - Int16, - Int32, - Int64, - Float32, - Float64, - String, - FS, - Array, - Object, -} - -String { - uint32 length; - primary utf8 value; -} - -StringValue Value & { (ValueType.String | ValueType.FS) type } - -Value { - ValueType type; - match { - Boolean: { uint8 value; } - Uint8: { uint8 value; } - Uint16: { uint16 value; } - Uint32: { uint32 value; } - Uint64: { uint64 value; } - Int8: { int8 value; } - Int16: { int16 value; } - Int32: { int32 value; } - Int64: { int64 value; } - Float32: { float32 value; } - Float64: { float64 value; } - String: { String value; } - FS: { FSPrimitive index; } - Array: { - uint32 count; - Value[count] values; - } - Object: { - uint32 count; - StringValue[count] keys; - Value[count] values; - } - } -} -``` diff --git a/package.json b/package.json index eb7837365..956a18c56 100644 --- a/package.json +++ b/package.json @@ -28,19 +28,21 @@ "format": "prettier --write \"**/*.@(json|js|jsx|css|md|ts|tsx|html|xml|yml|gltf)\"", "update": "yarn upgrade --latest", "tunnel": "cloudflared tunnel --url http://localhost:3000", - "discoverids": "scripts/discoverIds.ts" + "discoverids": "scripts/discoverIds.ts", + "build:protos": "bun packages/scripts/src/protos.ts" }, "license": "MIT", "dependencies": { "lodash-es": "^4.17.21" }, "devDependencies": { - "@types/lodash-es": "^4.17.12", "@types/bun": "latest", + "@types/lodash-es": "^4.17.12", "@types/node": "^22.5.4", "esbuild": "^0.19.12", "prettier": "^3.3.3", "prisma": "^5.19.1", + "ts-protoc-gen": "^0.15.0", "typescript": "^5.5.4" } } diff --git a/packages/core/protos/BattleResultsClient.proto b/packages/core/protos/BattleResultsClient.proto deleted file mode 100644 index 53c53f1fa..000000000 --- a/packages/core/protos/BattleResultsClient.proto +++ /dev/null @@ -1,298 +0,0 @@ -syntax = "proto3"; - -package BattleResultsGenerated; - -import "PlayerAvatarImage.proto"; - -message Int32List { - repeated int32 values = 1; -} - -message PlayerInfo { - message AvatarEntry { - int32 key = 1; - .PlayerAvatarInfo value = 2; - } - - string name = 1; - int32 prebattleID = 2; - int32 team = 3; - int64 clanID = 4; - string clanTag = 5; - repeated int32 chatMessageCount = 6; - repeated .BattleResultsGenerated.PlayerInfo.AvatarEntry avatar = 7; - int32 profileBackground = 8; - int32 vehicleRank = 9; -} - -message TeamInfo { - string title = 1; - string icon = 2; -} - -message VehiclePublicResults { - message ExtVehAchievementsEntry { - int32 key = 1; - int32 value = 2; - } - - message ExtAccAchievementsEntry { - int32 key = 1; - int32 value = 2; - } - - int32 health = 1; - int32 credits = 2; - int32 xp = 3; - int32 shots = 4; - int32 directHits = 5; - int32 explosionHits = 6; - int32 piercings = 7; - int32 damageDealt = 8; - int32 damageAssistedRadio = 9; - int32 damageAssistedTrack = 10; - int32 damageReceived = 11; - int32 directHitsReceived = 12; - int32 noDamageDirectHitsReceived = 13; - int32 explosionHitsReceived = 14; - int32 piercingsReceived = 15; - int32 spotted = 16; - int32 damaged = 17; - int32 kills = 18; - int32 tdamageDealt = 19; - int32 tkills = 20; - int32 capturePoints = 21; - int32 droppedCapturePoints = 22; - int32 mileage = 23; - int32 lifeTime = 24; - int32 killerID = 25; - repeated int32 achievements = 26; - repeated .BattleResultsGenerated.VehiclePublicResults.ExtVehAchievementsEntry extVehAchievements = 27; - repeated .BattleResultsGenerated.VehiclePublicResults.ExtAccAchievementsEntry extAccAchievements = 28; - int32 xpDamage = 29; - int32 xpAssist = 30; - int32 xpTeamBonus = 31; - int32 wpEarned = 32; - int32 wpStolen = 33; - int32 heroCreditsBonus = 34; - int32 heroXpBonus = 35; - int64 accountDBID = 101; - int32 team = 102; - int32 serializedVehType = 103; - int32 gold = 104; - int32 deathReason = 105; - int32 camouflage = 106; - float mmr = 107; - float mmrDiff = 108; - int32 calibrationBattlesLeft = 109; - int32 healedHP = 114; - int32 wasHealedFor = 115; - int32 xpActivity = 36; - int32 customization = 116; - int32 potentialEnemyDamage = 117; - int32 rammingDamage = 118; - int32 helpedToKill = 119; -} - -message BattleResultsFinalXp { - int32 xpDamagePure = 1; - int32 xpDamagePrem = 2; - int32 xpAssistPure = 3; - int32 xpAssistPrem = 4; - int32 xpActivityPure = 5; - int32 xpActivityPrem = 6; - int32 xpTeamBonusPure = 7; - int32 xpTeamBonusPrem = 8; - int32 healingXpPure = 9; - int32 healingXpPrem = 10; - int32 heroXpBonusPure = 11; - int32 heroXpBonusPrem = 12; - int32 earnedFreeXpPure = 13; - int32 earnedFreeXpPrem = 14; - int32 earnedAvatarXpPure = 15; - int32 earnedAvatarXpPrem = 16; - int32 xpNoModifiers = 17; - int32 xpPure = 18; - int32 xpPrem = 19; - int32 totalFreeXpPure = 20; - int32 totalFreeXpPrem = 21; - int32 totalAvatarXpPure = 22; - int32 totalAvatarXpPrem = 23; -} - -message Ribbons { - int32 id = 1; - int32 grade = 2; - int32 amount = 3; - int32 value = 4; -} - -message BattleResultsUpdate { - message ExcellentBattleDataEntry { - string key = 1; - int32 value = 2; - } - - message StuffEntry { - int32 key = 1; - int32 value = 2; - } - - message RatingBattleStuffBonusEntry { - int32 key = 1; - int32 value = 2; - } - - int32 avatarXP = 1; - int32 eventCredits = 2; - int32 eventGold = 3; - int32 eventXP = 4; - int32 eventFreeXP = 5; - int32 eventAvatarXP = 6; - int32 autoRepairCost = 7; - repeated int32 autoLoadCost = 8; - repeated int32 autoEquipCost = 9; - repeated int32 autoProvCost = 10; - bool isPremium = 12; - int32 premiumXPFactor10 = 13; - int32 premiumCreditsFactor10 = 14; - int32 xpFactor10 = 15; - int32 creditsFactor10 = 16; - int32 aogasFactor10 = 17; - int32 markOfMastery = 18; - repeated .BattleResultsGenerated.BattleResultsUpdate.ExcellentBattleDataEntry excellentBattleData = 19; - bytes vehDossier = 20; - bytes accDossier = 21; - bytes vehDossierPrev = 22; - bytes accDossierPrev = 23; - int32 autoRepairDiscount = 24; - repeated int32 autoEquipDiscount = 25; - repeated int32 autoProvDiscount = 26; - repeated .BattleResultsGenerated.BattleResultsUpdate.StuffEntry stuff = 27; - int32 boosterCredits = 29; - int32 boosterXp = 30; - int32 boosterFreeXp = 31; - int32 boosterAvatarXp = 32; - int32 complimentsLimit = 33; - repeated int32 autoAbilsCost = 34; - repeated int32 autoAbilsDiscount = 35; - bool hasHealingReward = 36; - int32 healingCredits = 37; - int32 healingXp = 38; - int32 healingFreeXp = 39; - int32 healingAvatarXp = 40; - float mmr = 107; - float mmrDiff = 108; - int32 calibrationBattlesLeft = 109; - int32 ratingBattleXp = 110; - int32 ratingBattleFreeXp = 111; - int32 ratingBattleAvatarXp = 112; - repeated .BattleResultsGenerated.BattleResultsUpdate.RatingBattleStuffBonusEntry ratingBattleStuffBonus = 113; - repeated .BattleResultsGenerated.Ribbons ribbons = 114; -} - -message DropData { - message StuffEntry { - int32 key = 1; - int32 value = 2; - } - - int32 credits = 1; - int32 gold = 2; - int32 xp = 3; - int32 freeXP = 4; - int32 avatarXP = 5; - repeated .BattleResultsGenerated.DropData.StuffEntry stuff = 27; -} - -message VehicleInteractionDetails { - message DamagedModulesEntry { - int32 key = 1; - int32 value = 2; - } - - message DestroyedModulesEntry { - int32 key = 1; - int32 value = 2; - } - - bool spotted = 1; - int32 deathReason = 2; - int32 directHits = 3; - int32 directHitWithoutDamage = 4; - int32 explosionHits = 5; - int32 piercings = 6; - int32 damageDealt = 7; - int32 damageAssistedTrack = 8; - int32 damageAssistedRadio = 9; - int32 fire = 10; - repeated .BattleResultsGenerated.VehicleInteractionDetails.DamagedModulesEntry damagedModules = 11; - repeated .BattleResultsGenerated.VehicleInteractionDetails.DestroyedModulesEntry destroyedModules = 12; - int32 ricochetHitWithoutDamage = 13; -} - -message InteractionDetails { - message VehiclesEntry { - int32 key = 1; - .BattleResultsGenerated.VehicleInteractionDetails value = 2; - } - - repeated .BattleResultsGenerated.InteractionDetails.VehiclesEntry vehicles = 1; -} - -message PrebattleStats { - int32 freeXp = 1; - int32 vehicleXp = 2; -} - -message BattleResultsClient { - message QuestsProgressEntry { - string key = 1; - .BattleResultsGenerated.Int32List value = 2; - } - - message DropCategoriesEntry { - string key = 1; - .BattleResultsGenerated.DropData value = 2; - } - - message PlayersEntry { - int64 key = 1; - .BattleResultsGenerated.PlayerInfo value = 2; - } - - message VehiclesEntry { - int32 key = 1; - .BattleResultsGenerated.VehiclePublicResults value = 2; - } - - int32 arenaTypeID = 1; - int32 arenaCreateTime = 2; - int32 winnerTeam = 3; - int32 finishReason = 4; - int32 duration = 5; - int32 bonusType = 6; - int32 vehLockMode = 7; - .BattleResultsGenerated.VehiclePublicResults public = 8; - int32 gameModeID = 9; - int32 observerMode = 10; - .BattleResultsGenerated.BattleResultsFinalXp finalXp = 11; - int32 repair = 136; - int32 freeXP = 137; - int32 xpPenalty = 145; - int32 creditsPenalty = 146; - int32 creditsContributionIn = 147; - int32 creditsContributionOut = 148; - .BattleResultsGenerated.BattleResultsUpdate update = 150; - int32 originalXP = 181; - int32 originalFreeXP = 182; - int32 originalCredits = 183; - repeated .BattleResultsGenerated.BattleResultsClient.QuestsProgressEntry questsProgress = 184; - repeated .BattleResultsGenerated.BattleResultsClient.DropCategoriesEntry dropCategories = 185; - .BattleResultsGenerated.TeamInfo teamInfo = 186; - repeated .BattleResultsGenerated.BattleResultsClient.PlayersEntry players = 201; - repeated .BattleResultsGenerated.BattleResultsClient.VehiclesEntry vehicles = 301; - .BattleResultsGenerated.InteractionDetails interactionDetails = 302; - .BattleResultsGenerated.PrebattleStats prebattleStats = 303; -} - diff --git a/packages/core/protos/PlayerAvatarImage.proto b/packages/core/protos/PlayerAvatarImage.proto deleted file mode 100644 index 5778ac821..000000000 --- a/packages/core/protos/PlayerAvatarImage.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto3"; - -message PlayerAvatarInfo { - uint32 id = 1; - string image = 2; - string image_retina = 3; - string grade = 4; -} diff --git a/packages/core/protos/average_definitions.proto b/packages/core/protos/raw/average_definitions.proto similarity index 100% rename from packages/core/protos/average_definitions.proto rename to packages/core/protos/raw/average_definitions.proto diff --git a/packages/core/protos/reviews.proto b/packages/core/protos/raw/reviews.proto similarity index 100% rename from packages/core/protos/reviews.proto rename to packages/core/protos/raw/reviews.proto diff --git a/packages/core/protos/raw/tank_definitions.proto b/packages/core/protos/raw/tank_definitions.proto new file mode 100644 index 000000000..dfedaa730 --- /dev/null +++ b/packages/core/protos/raw/tank_definitions.proto @@ -0,0 +1,223 @@ +syntax = "proto3"; + +package blitzkit; + +message TankDefinitions { + map tanks = 1; +} + +message TankDefinition { + uint32 id = 1; + map roles = 2; + optional string description = 3; + bool fixed_camouflage = 4; + repeated int32 camouflages = 5; + repeated int32 ancestors = 6; + repeated int32 successors = 7; + repeated Crew crew = 8; + uint32 health = 9; + string nation = 10; + string name = 11; + optional string name_full = 12; + TankType type = 13; + uint32 max_consumables = 14; + uint32 max_provisions = 15; + Tier tier = 16; + TankClass class = 17; + bool testing = 18; + bool deprecated = 19; + repeated TurretDefinition turrets = 20; + repeated EngineDefinition engines = 21; + repeated TrackDefinition tracks = 22; + TankPrice price = 23; + optional uint32 xp = 24; + float speed_forwards = 25; + float speed_backwards = 26; + float camouflage_still = 27; + float camouflage_moving = 28; + float camouflage_onFire = 29; + string equipment_preset = 30; + uint32 weight = 31; +} + +enum TankPriceType { + TYPE_UNSPECIFIED = 0; + TYPE_CREDITS = 1; + TYPE_GOLD = 2; +} + +message TankPrice { + TankPriceType type = 1; + uint32 value = 2; +} + +message TrackDefinition { + uint32 id = 1; + Tier tier = 2; + string name = 3; + uint32 weight = 4; + float traverse_speed = 5; + optional uint32 xp = 6; + float dispersion_move = 7; + float dispersion_traverse = 8; + float resistance_hard = 9; + float resistance_medium = 10; + float resistance_soft = 11; + repeated Unlock unlocks = 12; +} + +message EngineDefinition { + uint32 id = 1; + string name = 2; + optional uint32 xp = 3; + Tier tier = 4; + optional float fire_chance = 5; + uint32 power = 6; + uint32 weight = 7; + repeated Unlock unlocks = 8; +} + +message TurretDefinition { + uint32 id = 1; + uint32 health = 2; + uint32 view_range = 3; + float traverse_speed = 4; + optional uint32 xp = 5; + string name = 6; + uint32 tier = 7; + uint32 weight = 8; + repeated GunDefinition guns = 9; + repeated Unlock unlocks = 10; +} + +message GunDefinition { + oneof gun_type { + GunDefinitionRegular regular = 1; + GunDefinitionAutoLoader auto_loader = 2; + GunDefinitionAutoReloader auto_reloader = 3; + } +} + +message GunDefinitionRegular { + GunDefinitionBase gun = 1; + GunDefinitionRegularProperties regular = 2; +} + +message GunDefinitionAutoLoader { + GunDefinitionBase gun = 1; + GunDefinitionAutoLoaderProperties auto_loader = 2; +} + +message GunDefinitionAutoReloader { + GunDefinitionBase gun = 1; + GunDefinitionAutoReloaderProperties auto_reloader = 2; +} + +message GunDefinitionBase { + uint32 id = 1; + float rotation_speed = 2; + optional uint32 xp = 3; + uint32 weight = 4; + string name = 5; + uint32 tier = 6; + repeated ShellDefinition shells = 7; + float camouflage_loss = 8; + float aim_time = 9; + float dispersion_base = 10; + float dispersion_traverse = 11; + float dispersion_shot = 12; + float dispersion_damaged = 13; + repeated Unlock unlocks = 14; +} + +message ShellDefinition { + uint32 id = 1; + string name = 2; + uint32 velocity = 3; + uint32 armor_damage = 4; + uint32 module_damage = 5; + uint32 caliber = 6; + string icon = 7; + ShellPenetration penetration = 8; + ShellType type = 9; + uint32 normalization = 10; + uint32 ricochet = 11; + uint32 explosion_radius = 12; +} + +enum ShellType { + SHELL_UNSPECIFIED = 0; + SHELL_AP = 1; + SHELL_APCR = 2; + SHELL_HEAT = 3; + SHELL_HE = 4; +} + +message ShellPenetration { + uint32 near = 1; + uint32 far = 2; +} + +message Unlock { + int32 id = 1; + ModuleType type = 2; + UnlockCost cost = 3; +} + +message UnlockCost { + string type = 1; + int32 value = 2; +} + +enum ModuleType { + MODULE_UNSPECIFIED = 0; + MODULE_VEHICLE = 1; + MODULE_ENGINE = 2; + MODULE_TRACKS = 3; + MODULE_TURRET = 4; + MODULE_GUN = 5; +} + +enum TankClass { + CLASS_UNSPECIFIED = 0; + CLASS_LIGHT = 1; + CLASS_MEDIUM = 2; + CLASS_HEAVY = 3; + CLASS_TANK_DESTROYER = 4; +} + +enum Tier { + TIER_UNSPECIFIED = 0; + TIER_I = 1; + TIER_II = 2; + TIER_III = 3; + TIER_IV = 4; + TIER_V = 5; + TIER_VI = 6; + TIER_VII = 7; + TIER_VIII = 8; + TIER_IX = 9; + TIER_X = 10; +} + +enum TankType { + TYPE_UNSPECIFIED = 0; + TYPE_RESEARCHABLE = 1; + TYPE_PREMIUM = 2; + TYPE_COLLECTOR = 3; +} + +message Crew { + CrewType type = 1; + uint32 count = 2; + repeated CrewType substitute = 3; +} + +enum CrewType { + CREW_UNSPECIFIED = 0; + CREW_COMMANDER = 1; + CREW_RADIOMAN = 2; + CREW_GUNNER = 3; + CREW_DRIVER = 4; + CREW_LOADER = 5; +} \ No newline at end of file diff --git a/packages/core/src/blitzkit/fetchCdonLz4.ts b/packages/core/src/blitzkit/fetchCdonLz4.ts deleted file mode 100644 index 762631533..000000000 --- a/packages/core/src/blitzkit/fetchCdonLz4.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { superDecompress } from './superDecompress'; - -export async function fetchCdonLz4(url: string) { - return await fetch(url, { cache: 'no-store' }) - .then((response) => response.arrayBuffer()) - .then((buffer) => superDecompress(buffer)); -} diff --git a/packages/core/src/blitzkit/index.ts b/packages/core/src/blitzkit/index.ts index 964421f42..97a291acb 100644 --- a/packages/core/src/blitzkit/index.ts +++ b/packages/core/src/blitzkit/index.ts @@ -13,7 +13,6 @@ export * from './degressiveStat'; // export * from './emblemURL'; export * from './equipmentDefinitions'; export * from './eventManager'; -// export * from './fetchCdonLz4'; // export * from './flagUserActivity'; export * from './gameDefinitions'; // export * from './getArchivedLatestSeasonNumber'; @@ -36,8 +35,6 @@ export * from './resolvePenetrationCoefficient'; export * from './reviews'; export * from './skillDefinitions'; export * from './sum'; -export * from './superCompress'; -export * from './superDecompress'; export * from './tankDefinitions'; export * from './tankIcon'; export * from './tankNames'; diff --git a/packages/core/src/blitzkit/superCompress.ts b/packages/core/src/blitzkit/superCompress.ts deleted file mode 100644 index a3524a18c..000000000 --- a/packages/core/src/blitzkit/superCompress.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { compress } from 'lz4js'; -import { CdonWriteStream } from '../streams'; - -export function superCompress(object: any) { - const write = new CdonWriteStream().cdon(object); - const compressed = compress(write.uint8Array); - return Buffer.from(compressed).toString('base64'); -} diff --git a/packages/core/src/blitzkit/superDecompress.ts b/packages/core/src/blitzkit/superDecompress.ts deleted file mode 100644 index 595d75ae0..000000000 --- a/packages/core/src/blitzkit/superDecompress.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { decompress } from 'lz4js'; -import { CdonReadStream } from '../streams'; - -export function superDecompress(buffer: ArrayBuffer) { - const decompressed = decompress(new Uint8Array(buffer)).buffer as ArrayBuffer; - const read = new CdonReadStream(decompressed); - return read.cdon() as Type; -} diff --git a/packages/core/src/blitzkit/tankDefinitions/constants.ts b/packages/core/src/blitzkit/tankDefinitions/constants.ts index bbf7777c0..6c155af3a 100644 --- a/packages/core/src/blitzkit/tankDefinitions/constants.ts +++ b/packages/core/src/blitzkit/tankDefinitions/constants.ts @@ -1,10 +1,10 @@ import { - CrewMember, + CrewType, GunDefinition, ShellType, TankClass, + TankType, Tier, - TreeType, } from '.'; export const SHELL_NAMES: Record = { @@ -13,7 +13,7 @@ export const SHELL_NAMES: Record = { hc: 'HEAT', he: 'HE', }; -export const CREW_MEMBER_NAMES: Record = { +export const CREW_MEMBER_NAMES: Record = { commander: 'Commander', radioman: 'Radioman', gunner: 'Gunner', @@ -77,7 +77,7 @@ export const flags: Record = { european: '🇪🇺', other: '<:other:1218421572243558482>', }; -export const TREE_TYPE_ICONS: Record> = { +export const TREE_TYPE_ICONS: Record> = { researchable: TANK_ICONS, premium: TANK_ICONS_PREMIUM, collector: TANK_ICONS_COLLECTOR, diff --git a/packages/core/src/blitzkit/tankDefinitions/index.ts b/packages/core/src/blitzkit/tankDefinitions/index.ts index 8cfcc6c5e..970f129d0 100644 --- a/packages/core/src/blitzkit/tankDefinitions/index.ts +++ b/packages/core/src/blitzkit/tankDefinitions/index.ts @@ -1,27 +1,48 @@ import { Vector2Tuple } from 'three'; -import { TANK_CLASSES, TREE_TYPES } from '../../blitz/constants'; import { asset } from '../asset'; import { fetchCdonLz4 } from '../fetchCdonLz4'; -import { TIERS } from './constants'; -export type TankClass = (typeof TANK_CLASSES)[number]; -export type TreeType = (typeof TREE_TYPES)[number]; +export enum TankClass { + Unspecified, + Light, + Medium, + Heavy, + TankDestroyer, +} +export enum TankType { + Unspecified, + Researchable, + Premium, + Collector, +} export enum ShellType { - AP = 'ap', - APCR = 'ap_cr', - HEAT = 'hc', - HE = 'he', + Unspecified, + AP, + APCR, + HEAT, + HE, +} + +export enum CrewType { + Unspecified, + Commander, + Radioman, + Gunner, + Driver, + Loader, } -export type CrewMember = - | 'commander' - | 'radioman' - | 'gunner' - | 'driver' - | 'loader'; export type TankDefinitions = Record; -export type ModuleType = 'vehicle' | 'engine' | 'chassis' | 'turret' | 'gun'; +export enum ModuleType { + Unspecified, + Vehicle, + Engine, + Tracks, + Turret, + Gun, +} + export interface Unlock { type: ModuleType; id: number; @@ -32,9 +53,9 @@ export interface Unlock { }; } export interface Crew { - type: CrewMember; + type: CrewType; count?: number; - substitute?: CrewMember[]; + substitute?: CrewType[]; } export type ModuleDefinition = | TrackDefinition @@ -45,18 +66,18 @@ export interface TankDefinition { id: number; roles: Record; description?: string; - fixedCamouflage?: boolean; - camouflages?: number[]; - ancestors?: number[]; - successors?: number[]; + fixed_camouflage: boolean; + camouflages: number[]; + ancestors: number[]; + successors: number[]; crew: Crew[]; health: number; nation: string; name: string; nameFull?: string; - treeType: TreeType; - consumables: number; - provisions: number; + type: TankType; + max_consumables: number; + max_provisions: number; tier: Tier; class: TankClass; testing: boolean; @@ -66,16 +87,12 @@ export interface TankDefinition { tracks: TrackDefinition[]; price: TankPrice; xp?: number; - speed: { - forwards: number; - backwards: number; - }; - camouflage: { - still: number; - moving: number; - onFire: number; - }; - equipment: string; + speed_forwards: number; + speed_backwards: number; + camouflage_still: number; + camouflage_moving: number; + camouflage_onFire: number; + equipment_preset: string; weight: number; } export interface TrackDefinition { @@ -83,17 +100,13 @@ export interface TrackDefinition { tier: Tier; name: string; weight: number; - traverseSpeed: number; + traverse_speed: number; xp?: number; - dispersion: { - move: number; - traverse: number; - }; - resistance: { - hard: number; - medium: number; - soft: number; - }; + dispersion_move: number; + dispersion_traverse: number; + resistance_hard: number; + resistance_medium: number; + resistance_soft: number; unlocks?: Unlock[]; } export interface EngineDefinition { @@ -101,18 +114,24 @@ export interface EngineDefinition { name: string; xp?: number; tier: Tier; - fireChance: number; + fire_chance: number; power: number; weight: number; unlocks?: Unlock[]; } -export type TankPrice = - | { type: 'credits'; value: number } - | { type: 'gold'; value: number }; +export type TankPrice = { + type: TankPriceType; + value: number; +}; +enum TankPriceType { + Unspecified, + Credits, + Gold, +} export interface TurretDefinition { health: number; - viewRange: number; - traverseSpeed: number; + view_range: number; + traverse_speed: number; xp?: number; id: number; name: string; @@ -126,52 +145,60 @@ export type GunDefinition = | GunDefinitionAutoLoader | GunDefinitionAutoReloader; interface GunDefinitionBase { - rotationSpeed: number; + rotation_speed: number; xp?: number; weight: number; id: number; name: string; tier: Tier; shells: ShellDefinition[]; - camouflageLoss: number; - aimTime: number; - dispersion: { - base: number; - traverse: number; - shot: number; - damaged: number; - }; + camouflage_loss: number; + aim_time: number; + dispersion_base: number; + dispersion_traverse: number; + dispersion_shot: number; + dispersion_damaged: number; unlocks?: Unlock[]; } -interface GunDefinitionRegular extends GunDefinitionBase { - type: 'regular'; +interface GunDefinitionRegular { + gun: GunDefinitionBase; + regular: GunDefinitionRegularProperties; +} +interface GunDefinitionRegularProperties { reload: number; } -interface GunDefinitionAutoLoader extends GunDefinitionBase { - type: 'autoLoader'; +interface GunDefinitionAutoLoader { + gun: GunDefinitionBase; + auto_loader: GunDefinitionAutoLoaderProperties; +} +interface GunDefinitionAutoLoaderProperties { reload: number; - intraClip: number; + intra_clip: number; count: number; } -interface GunDefinitionAutoReloader extends GunDefinitionBase { - type: 'autoReloader'; +interface GunDefinitionAutoReloader { + gun: GunDefinitionBase; + auto_reloader: GunDefinitionAutoReloaderProperties; +} +interface GunDefinitionAutoReloaderProperties { reload: number[]; intraClip: number; count: number; } -export type ShellDefinition = { +export interface ShellDefinition { id: number; name: string; - speed: number; - damage: { armor: number; module: number }; + velocity: number; + armor_damage: number; + module_damage: number; caliber: number; icon: string; penetration: number | Vector2Tuple; type: ShellType; normalization?: number; ricochet?: number; - explosionRadius?: number; -}; + explosion_radius?: number; +} export const tankDefinitions = fetchCdonLz4( asset('definitions/tanks.cdon.lz4'), @@ -180,6 +207,18 @@ export const tankDefinitions = fetchCdonLz4( export const tankDefinitionsArray = tankDefinitions.then((tanks) => Object.values(tanks), ); -export type Tier = (typeof TIERS)[number]; +export enum Tier { + Unspecified, + I, + II, + III, + IV, + V, + VI, + VII, + VIII, + IX, + X, +} export * from './constants'; diff --git a/packages/core/src/protobuf/lookupProtobufType.ts b/packages/core/src/protobuf/lookupProtobufType.ts index 11008572e..880f13713 100644 --- a/packages/core/src/protobuf/lookupProtobufType.ts +++ b/packages/core/src/protobuf/lookupProtobufType.ts @@ -1,10 +1,11 @@ import protobuf from 'protobufjs'; -import average_definitions from '../../protos/average_definitions.proto?raw'; -import reviews from '../../protos/reviews.proto?raw'; +import average_definitions from '../../protos/average_definitions.proto'; +import reviews from '../../protos/reviews.proto'; +import tank_definitions from '../../protos/tank_definitions.proto'; export type ProtoSource = keyof typeof protos; -const protos = { reviews, average_definitions }; +const protos = { reviews, average_definitions, tank_definitions }; export function lookupProtobufType(source: ProtoSource, type: string) { return protobuf.parse(protos[source]).root.lookupType(type); diff --git a/packages/core/src/streams/cdon.ts b/packages/core/src/streams/cdon.ts deleted file mode 100644 index ecf66521f..000000000 --- a/packages/core/src/streams/cdon.ts +++ /dev/null @@ -1,375 +0,0 @@ -import { times } from 'lodash-es'; -import { ReadStream, WriteStream } from './buffer'; - -enum ValueType { - Null, - Boolean, - Uint8, - Uint16, - Uint32, - Uint64, - Int8, - Int16, - Int32, - Int64, - Float32, - Float64, - String, - FastString, - Array, - Object, -} - -enum FastStringFormat { - Format8, - Format16, - Format32, -} - -export type CdonValue = - | undefined - | null - | boolean - | number - | bigint - | string - | CdonValue[] - | { [key: string]: CdonValue }; - -export class CdonReadStream extends ReadStream { - cdon() { - this.magic(); - const header = this.header(); - const body = this.body(header.fastStringFormat, header.stringTable); - return body; - } - - magic() { - const magic = this.utf8(4); - - if (magic !== 'CDON') { - throw new Error(`Invalid CDON magic number: "${magic}"`); - } - } - - header() { - const version = this.uint16(); - const fastStringFormat = this.uint8() as FastStringFormat; - - return { - version, - fastStringFormat, - stringTable: this.stringTable(fastStringFormat), - }; - } - - body( - fastStringFormat: FastStringFormat, - stringTable: Record, - ) { - return this.value(fastStringFormat, stringTable); - } - - stringTable(fastStringFormat: FastStringFormat) { - let count: number; - - switch (fastStringFormat) { - case FastStringFormat.Format8: - count = this.uint8(); - break; - - case FastStringFormat.Format16: - count = this.uint16(); - break; - - case FastStringFormat.Format32: - count = this.uint32(); - break; - } - - const strings: string[] = []; - - for (let index = 0; index < count; index++) { - strings.push(this.string()); - } - - const table: Record = {}; - times(count, (index) => (table[index] = strings[index])); - - return table; - } - - string() { - const length = this.uint32(); - const string = this.utf8(length); - return string; - } - - value( - fastStringFormat: FastStringFormat, - stringTable: Record, - ): CdonValue { - const type = this.uint8() as ValueType; - - switch (type) { - case ValueType.Null: - return null; - - case ValueType.Boolean: - return this.boolean(); - - case ValueType.Uint8: - return this.uint8(); - - case ValueType.Uint16: - return this.uint16(); - - case ValueType.Uint32: - return this.uint32(); - - case ValueType.Uint64: - return this.uint64(); - - case ValueType.Int8: - return this.int8(); - - case ValueType.Int16: - return this.int16(); - - case ValueType.Int32: - return this.int32(); - - case ValueType.Int64: - return this.int64(); - - case ValueType.Float32: - return this.float32(); - - case ValueType.Float64: - return this.float64(); - - case ValueType.String: - return this.string(); - - case ValueType.FastString: { - switch (fastStringFormat) { - case FastStringFormat.Format8: - return stringTable[this.uint8()]; - - case FastStringFormat.Format16: - return stringTable[this.uint16()]; - - case FastStringFormat.Format32: - return stringTable[this.uint32()]; - } - } - - case ValueType.Array: - return times(this.uint32(), () => - this.value(fastStringFormat, stringTable), - ); - - case ValueType.Object: { - const count = this.uint32(); - const keys = times( - count, - () => this.value(fastStringFormat, stringTable) as string, - ); - const values = times(count, () => - this.value(fastStringFormat, stringTable), - ); - const object: Record = {}; - - keys.forEach((string, index) => (object[string] = values[index])); - - return object; - } - - default: - throw new Error(`Invalid value type: ${type}`); - } - } -} - -export class CdonWriteStream extends WriteStream { - cdon(object: CdonValue) { - this.magic(); - const header = this.header(object); - this.body(header.fastStringFormat, object, header.stringTable); - return this; - } - - body( - fastStringFormat: FastStringFormat, - object: CdonValue, - stringTable: Map, - ) { - this.value(fastStringFormat, object, stringTable); - return this; - } - - value( - fastStringFormat: FastStringFormat, - object: CdonValue, - stringTable: Map, - ) { - if (object === null || object === undefined) { - this.uint8(ValueType.Null); - } else if (typeof object === 'boolean') { - this.uint8(ValueType.Boolean); - this.uint8(object ? 1 : 0); - } else if (typeof object === 'number') { - if (object % 1 === 0) { - if (object < 0) { - if (object < -0x80000000) { - this.uint8(ValueType.Int64); - this.int64(BigInt(object)); - } else if (object < -0x8000) { - this.uint8(ValueType.Int32); - this.int32(object); - } else if (object < -0x80) { - this.uint8(ValueType.Int16); - this.int16(object); - } else { - this.uint8(ValueType.Int8); - this.int8(object); - } - } else { - if (object > 0x7fffffff) { - this.uint8(ValueType.Uint64); - this.uint64(BigInt(object)); - } else if (object > 0x7fff) { - this.uint8(ValueType.Uint32); - this.uint32(object); - } else if (object > 0x7f) { - this.uint8(ValueType.Uint16); - this.uint16(object); - } else { - this.uint8(ValueType.Uint8); - this.uint8(object); - } - } - } else { - if (object < -0x80000000 || object > 0x7fffffff) { - this.uint8(ValueType.Float64); - this.float64(object); - } else { - this.uint8(ValueType.Float32); - this.float32(object); - } - } - } else if (typeof object === 'bigint') { - if (object < 0) { - this.int64(object); - } else { - this.uint64(object); - } - } else if (typeof object === 'string') { - if (stringTable.has(object)) { - this.uint8(ValueType.FastString); - const id = stringTable.get(object)!; - switch (fastStringFormat) { - case FastStringFormat.Format8: - this.uint8(id); - break; - - case FastStringFormat.Format16: - this.uint16(id); - break; - - case FastStringFormat.Format32: - this.uint32(id); - } - } else { - this.uint8(ValueType.String); - this.string(object); - } - } else if (Array.isArray(object)) { - this.uint8(ValueType.Array); - this.uint32(object.length); - object.forEach((value) => - this.value(fastStringFormat, value, stringTable), - ); - } else { - const entries = Object.entries(object).filter( - ([, value]) => value !== undefined, - ); - - this.uint8(ValueType.Object); - this.uint32(entries.length); - entries.forEach(([key]) => - this.value(fastStringFormat, key, stringTable), - ); - entries.forEach(([, value]) => - this.value(fastStringFormat, value, stringTable), - ); - } - } - - header(object: CdonValue) { - this.uint16(1); - return this.stringTable(object); - } - - stringTable(object: CdonValue) { - const stringTable = this.buildStringTable( - this.collectStringCounts(object, new Map()), - ); - let fastStringFormat: FastStringFormat; - - if (stringTable.size <= 0xff) { - fastStringFormat = FastStringFormat.Format8; - this.uint8(FastStringFormat.Format8); - this.uint8(stringTable.size); - } else if (stringTable.size <= 0xffff) { - fastStringFormat = FastStringFormat.Format16; - this.uint8(FastStringFormat.Format16); - this.uint16(stringTable.size); - } else { - fastStringFormat = FastStringFormat.Format32; - this.uint8(FastStringFormat.Format32); - this.uint32(stringTable.size); - } - - stringTable.forEach((_index, string) => this.string(string)); - - return { fastStringFormat, stringTable }; - } - - buildStringTable(stringCounts: Map) { - const stringTable = new Map(); - - stringCounts.forEach((count, string) => { - if (count > 1) stringTable.set(string, stringTable.size); - }); - - return stringTable; - } - - collectStringCounts(object: CdonValue, stringCounts: Map) { - if (typeof object === 'string') { - if (stringCounts.has(object)) { - stringCounts.set(object, stringCounts.get(object)! + 1); - } else { - stringCounts.set(object, 1); - } - } else if (object instanceof Array) { - object.forEach((value) => this.collectStringCounts(value, stringCounts)); - } else if (object instanceof Object) { - Object.entries(object).forEach((strings) => - this.collectStringCounts(strings, stringCounts), - ); - } - - return stringCounts; - } - - string(value: string) { - this.uint32(Buffer.byteLength(value)); - this.utf8(value); - } - - magic() { - this.utf8('CDON'); - } -} diff --git a/packages/core/src/streams/index.ts b/packages/core/src/streams/index.ts index a16292c7c..965bed6a8 100644 --- a/packages/core/src/streams/index.ts +++ b/packages/core/src/streams/index.ts @@ -1,6 +1,5 @@ export * from './bkrl'; export * from './buffer'; -export * from './cdon'; export * from './dds'; export * from './dids'; export * from './pvr'; diff --git a/packages/scripts/src/buildAssets/definitions.ts b/packages/scripts/src/buildAssets/definitions.ts index 852a19f3e..ee16f22d6 100644 --- a/packages/scripts/src/buildAssets/definitions.ts +++ b/packages/scripts/src/buildAssets/definitions.ts @@ -807,7 +807,7 @@ export async function definitions() { forwards: tankDefinition.root.speedLimits.forward, backwards: tankDefinition.root.speedLimits.backward, }, - equipment: + equipment_preset: typeof equipment === 'string' ? equipment : equipment.at(-1)!, consumables: tankDefinition.root.consumableSlots, provisions: tankDefinition.root.provisionSlots, @@ -888,7 +888,7 @@ export async function definitions() { id: trackId, weight: track.weight, name: strings[track.userString], - traverseSpeed: track.rotationSpeed, + traverse_speed: track.rotationSpeed, dispersion: { move: track.shotDispersionFactors.vehicleMovement, traverse: track.shotDispersionFactors.vehicleRotation, @@ -920,7 +920,7 @@ export async function definitions() { tankDefinitions[tankId].engines.push({ id: engineId, name: strings[engineListEntry.userString], - fireChance: engineListEntry.fireStartingChance, + fire_chance: engineListEntry.fireStartingChance, tier: engineListEntry.level as Tier, weight: engineListEntry.weight, power: engineListEntry.power, diff --git a/packages/scripts/src/protos.ts b/packages/scripts/src/protos.ts new file mode 100644 index 000000000..df08b44d6 --- /dev/null +++ b/packages/scripts/src/protos.ts @@ -0,0 +1,16 @@ +import { $ } from 'bun'; +import { readdir } from 'fs/promises'; + +const ROOT = 'packages/core/protos'; + +const files = await readdir(`${ROOT}/raw`); +const args = [ + '--plugin="./node_modules/.bin/protoc-gen-ts"', + '--js_out="import_style=commonjs,binary:${OUT_DIR}"', + `--ts_out="${ROOT}/generated"`, + ...files.map((file) => `${ROOT}/raw/${file}`), +]; + +console.log(`protoc ${args.join(' ')}`); + +console.log(await $`protoc ${args.join(' ')}`.text());