diff --git a/packages/scripts/src/core/github/commitMultipleFiles.ts b/packages/scripts/src/core/github/commitMultipleFiles.ts
index c3b41765b..f8ee1ae82 100644
--- a/packages/scripts/src/core/github/commitMultipleFiles.ts
+++ b/packages/scripts/src/core/github/commitMultipleFiles.ts
@@ -32,7 +32,9 @@ export async function commitMultipleFiles(
// we discard blob if they're the same size; it's unlikely their
// contents will be different; I love playing russian roulette!
- const equal = buffer.length === change.content.length || buffer.equals(change.content);
+ const equal =
+ buffer.length === change.content.length ||
+ buffer.equals(change.content);
if (!equal) {
const diff = change.content.length - buffer.length;
diff --git a/src/BlitzKit.CLI/BlitzKit.CLI.csproj b/src/BlitzKit.CLI/BlitzKit.CLI.csproj
index 50d09a569..d50d97098 100644
--- a/src/BlitzKit.CLI/BlitzKit.CLI.csproj
+++ b/src/BlitzKit.CLI/BlitzKit.CLI.csproj
@@ -13,6 +13,8 @@
+
+
diff --git a/src/BlitzKit.CLI/Functions/Mangler.cs b/src/BlitzKit.CLI/Functions/Mangler.cs
index 801dda85a..f00821514 100644
--- a/src/BlitzKit.CLI/Functions/Mangler.cs
+++ b/src/BlitzKit.CLI/Functions/Mangler.cs
@@ -5,6 +5,7 @@
using CUE4Parse.UE4.Assets.Exports.Engine;
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
using CUE4Parse.UE4.Assets.Objects;
+using CUE4Parse.UE4.Objects.UObject;
using Google.Protobuf.Collections;
using Newtonsoft.Json.Linq;
@@ -14,8 +15,9 @@ public class Mangler
{
readonly Tanks tanks = new();
readonly BlitzProvider provider = new();
+ readonly List changes = [];
- public void Mangle()
+ public async Task Mangle()
{
var nationDirs = provider.RootDirectory.GetDirectory("Blitz/Content/Tanks").Directories;
@@ -26,6 +28,8 @@ public void Mangle()
MangleNation(nationDir.Value);
}
+
+ await BlitzKitAssets.CommitAssets("ue assets", changes);
}
void MangleNation(VFS nation)
@@ -48,6 +52,9 @@ void MangleNation(VFS nation)
{
var pdaName = $"PDA_{tankDir.Name}";
var pda = provider.LoadObject($"{tankDir.Path}/{pdaName}.{pdaName}");
+
+ var tankId = PropertyUtil.Get(pda, "TankId").Text;
+
var hulls = PropertyUtil.Get(pda, "DT_Hulls");
UDataTableUtility.TryGetDataTableRow(hulls, "hull", StringComparison.Ordinal, out var hull);
var hullVisualData = PropertyUtil.Get(hull, "VisualData");
@@ -55,9 +62,9 @@ void MangleNation(VFS nation)
var hullCollision = PropertyUtil.Get(hullMeshSettings, "CollisionMesh");
MonoGltf hullCollisionGltf = new(hullCollision);
- hullCollisionGltf.Write();
+ changes.Add(new($"tanks/{tankId}/collision/hull.glb", hullCollisionGltf.Write()));
- return new() { Id = "" };
+ return new() { Id = tankId };
}
Dictionary CrewTypes = new()
diff --git a/src/BlitzKit.CLI/Models/BlitzKitAssets.cs b/src/BlitzKit.CLI/Models/BlitzKitAssets.cs
new file mode 100644
index 000000000..522f32f3d
--- /dev/null
+++ b/src/BlitzKit.CLI/Models/BlitzKitAssets.cs
@@ -0,0 +1,162 @@
+using System.Net;
+using BlitzKit.CLI.Utils;
+using DotNetEnv;
+using Octokit;
+
+namespace BlitzKit.CLI.Models
+{
+ public static class BlitzKitAssets
+ {
+ public static int TIME_PER_BLOB = (int)Math.Pow(2, 4) * 10000;
+ public static int TIME_BETWEEN_BLOBS = (int)(60 * 60 * 1000 / 5000 / 0.9);
+
+ public static async Task CommitAssets(string message, List changes)
+ {
+ Console.WriteLine($"Committing \"{message}\"... with {changes.Count} proposed changes");
+
+ if (changes.Count == 0)
+ return;
+
+ await CommitMultipleFiles(
+ Env.GetString("PUBLIC_ASSET_REPO"),
+ Env.GetString("PUBLIC_ASSET_BRANCH"),
+ message,
+ changes
+ );
+ }
+
+ public static async Task CommitMultipleFiles(
+ string repoRaw,
+ string branch,
+ string message,
+ List changesRaw
+ )
+ {
+ using HttpClient httpClient = new();
+
+ GitHubClient octokit = new(new ProductHeaderValue("MyAmazingApp"))
+ {
+ Credentials = new(Env.GetString("GH_TOKEN")),
+ };
+ List changes = [];
+
+ await Task.WhenAll(
+ changesRaw
+ .Select(async change =>
+ {
+ var blobPath = $"{repoRaw}/{branch}/{change.Path}";
+
+ HttpResponseMessage response = await httpClient.GetAsync(
+ $"https://raw.githubusercontent.com/{blobPath}"
+ );
+
+ if (response.StatusCode == HttpStatusCode.NotFound)
+ {
+ Console.WriteLine(
+ $"🟢 (+{change.Content.Count.ToString("N0", Program.Culture)}B) {blobPath}"
+ );
+ changes.Add(change);
+ }
+ else if (response.StatusCode == HttpStatusCode.OK)
+ {
+ var bytes = await response.Content.ReadAsByteArrayAsync();
+
+ // we discard blob if they're the same size; it's unlikely their
+ // contents will be different; I love playing russian roulette!
+ var equal =
+ bytes.Length == change.Content.Count || bytes.SequenceEqual([.. change.Content]);
+ if (!equal)
+ {
+ var diff = change.Content.Count - bytes.Length;
+
+ Console.WriteLine(
+ $"🟡 ({(diff > 0 ? '+' : "")}{diff.ToString("N0", Program.Culture)}B) {blobPath}"
+ );
+
+ changes.Add(change);
+ }
+ }
+ else
+ {
+ throw new Exception($"Unexpected status code {response.StatusCode} for {blobPath}");
+ }
+ })
+ .ToList()
+ );
+
+ if (changes.Count == 0)
+ return;
+
+ var repoRawSplit = repoRaw.Split('/');
+ var owner = repoRawSplit[0];
+ var repo = repoRawSplit[1];
+ var latestCommitSha = (await octokit.Git.Reference.Get(owner, repo, $"heads/{branch}"))
+ .Object
+ .Sha;
+ var treeSha = (await octokit.Git.Commit.Get(owner, repo, latestCommitSha)).Tree.Sha;
+ NewTree newTree = new() { BaseTree = treeSha };
+
+ foreach (var change in changes)
+ {
+ while (true)
+ {
+ try
+ {
+ var blobSha = octokit
+ .Git.Blob.Create(
+ owner,
+ repo,
+ new()
+ {
+ Encoding = EncodingType.Base64,
+ Content = Convert.ToBase64String(change.Content),
+ }
+ )
+ .Result.Sha;
+
+ await Task.Delay(TIME_BETWEEN_BLOBS);
+
+ newTree.Tree.Add(
+ new()
+ {
+ Sha = blobSha,
+ Path = change.Path,
+ Mode = "100644",
+ Type = TreeType.Blob,
+ }
+ );
+
+ break;
+ }
+ catch
+ {
+ PrettyLog.Warn($"Failed blob {change.Path}; retrying in {TIME_PER_BLOB}ms...");
+ await Task.Delay(TIME_PER_BLOB);
+ }
+ }
+ }
+
+ var treeData = await octokit.Git.Tree.Create(owner, repo, newTree);
+ var newCommit = await octokit.Git.Commit.Create(
+ owner,
+ repo,
+ new(message, treeData.Sha, latestCommitSha)
+ );
+ await octokit.Git.Reference.Update(owner, repo, $"heads/{branch}", new(newCommit.Sha, true));
+ }
+ }
+
+ public class GithubChangeBlob
+ {
+ public required string Sha;
+ public required string Path;
+ public required string Mode;
+ public required string Type;
+ }
+
+ public class FileChange(string path, ArraySegment content)
+ {
+ public string Path = path;
+ public ArraySegment Content = content;
+ }
+}
diff --git a/src/BlitzKit.CLI/Program.cs b/src/BlitzKit.CLI/Program.cs
index 940572cf1..d0c86fc3c 100644
--- a/src/BlitzKit.CLI/Program.cs
+++ b/src/BlitzKit.CLI/Program.cs
@@ -1,12 +1,17 @@
-using BlitzKit.CLI.Functions;
+using System.Globalization;
+using BlitzKit.CLI.Functions;
using BlitzKit.CLI.Utils;
+using DotNetEnv;
namespace BlitzKit.CLI
{
class Program
{
+ public static CultureInfo Culture = new("en-US");
+
static async Task Main(string[] args)
{
+ Env.Load();
await AgnosticHelpers.Initialize();
switch (args[0])
@@ -21,7 +26,7 @@ static async Task Main(string[] args)
{
Mangler mangler = new();
- mangler.Mangle();
+ await mangler.Mangle();
break;
}