-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProgram.cs
331 lines (286 loc) · 17.9 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
using CobbleBuild.BedrockClasses;
using CobbleBuild.CobblemonClasses;
using CobbleBuild.ConversionTechnology;
using CobbleBuild.JavaClasses;
using CobbleBuild.Kotlin;
using Newtonsoft.Json;
using System.Diagnostics;
using static CobbleBuild.Config;
using static CobbleBuild.Misc;
using static CobbleBuild.Wrappers;
namespace CobbleBuild {
public class Program {
public static List<string> EntityTextures = new List<string>();
public static AtlasJson itemAtlasJson;
public static AtlasJson blockAtlasJson;
//public static LanguageFileOld lang = new LanguageFileOld();
//public static Dictionary<string, SoundDefinition> sounds = new Dictionary<string, SoundDefinition>();
/// <summary>
/// List of folders to delete when the build finishes
/// </summary>
public static List<string> temporaryFolders = new List<string>();
/// <summary>
/// Global repository of animations.
/// </summary>
public static Dictionary<string, Animation> animations = [];
//All Pokemon Identified as Implimented
public static List<Pokemon> targetPokemon = new List<Pokemon>();
public static int sucessCounter = 0;
static async Task Main(string[] args) {
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("CobbleBuild - Cobblemon Port Tool for Minecraft Bedrock Edition");
init(args);
//Tests if the required Directories exist.
if (!Directory.Exists(config.projectPath) || !Directory.Exists(config.behaviorPath) || !Directory.Exists(config.resourcePath)) {
error($"Be sure to run CobbleBuild in the root of the cobblemon-bedrock source code.");
}
if (!Directory.Exists(config.resourcesPath)) {
var resoursesPath = Path.Combine(config.projectPath, "extractedResources", "cobblemon-src");
Console.WriteLine("Downloading Cobblemon Source...");
Directory.CreateDirectory(resoursesPath);
RunCmd("git", "clone https://gitlab.com/cable-mc/cobblemon.git --branch v1.4.1 --single-branch ./", resoursesPath);
config.cobblemonPath = resoursesPath;
if (!config.temporairlyExtract)
config.overwriteConfig();
else
temporaryFolders.Add(resoursesPath);
}
if (!Directory.Exists(config.minecraftJavaPath)) {
//Default folder path
string mcJarPath = handleEnvVariables(ref new Config().minecraftJavaPath);
if (File.Exists(config.minecraftJavaPath) && config.minecraftJavaPath.EndsWith(".jar"))
mcJarPath = config.minecraftJavaPath;
if (File.Exists(mcJarPath) && mcJarPath.EndsWith(".jar")) {
Console.WriteLine("Extracting Minecraft...");
config.minecraftJavaPath = ExtractZipFile(mcJarPath);
if (!config.temporairlyExtract)
config.overwriteConfig();
}
else {
error($"Minecraft Java ({config.targetMinecraftVersion}) is required to run CobbleBuild. Please specify a valid path to the minecraft .jar using the -g flag or the config file.");
}
}
Cleaner.Verify();
if (config.buildTasks.Contains("clean")) {
Console.WriteLine("Cleaning old build...");
Cleaner.Clean();
}
//I know my attitude has been to not care about performance but damn this takes a long time.
if (config.buildTasks.Contains("translate")) {
Console.WriteLine("Copying Translations...");
List<string> validLanguages = [];
var USAdditions = JsonConvert.DeserializeObject<TranslationKey>(File.ReadAllText(Path.Combine(config.projectPath, "base_translations/en_US.json")))!;
foreach (string file in Directory.GetFiles(Path.Combine(config.resourcesPath, "assets/cobblemon/lang"))) {
var languageIDArray = Path.GetFileNameWithoutExtension(file).ToCharArray();
//Capitalize characters 4 and 5 in the string.
languageIDArray[3] = char.ToUpper(languageIDArray[3]);
languageIDArray[4] = char.ToUpper(languageIDArray[4]);
var languageID = new string(languageIDArray);
var languageDictionary = DeserializeFromFile<TranslationKey>(file)!;
//Adds in bedrock specific translations (starts with US, and is overwritten by other languages)
languageDictionary.Merge(USAdditions);
//Adds in bedrock specific translation for each language
var languageAdditionsPath = Path.Combine(config.projectPath, $"base_translations/{languageID}.json");
if (File.Exists(languageAdditionsPath)) {
var languageAdditions = DeserializeFromFile<TranslationKey>(languageAdditionsPath)!;
languageDictionary.Merge(languageAdditions);
}
//languageDictionary.Prepare();
File.WriteAllText(Path.Combine(config.resourcePath, $"texts/{languageID}.lang"), languageDictionary.getBedrockKey());
validLanguages.Add(languageID);
}
SaveToJson(validLanguages, Path.Combine(config.resourcePath, "texts/languages.json"));
}
if (config.buildTasks.Contains("posers")) {
PoserRegistry.InitMappings();
string cachePath = Path.Combine(config.projectPath, "poserCache");
if (config.cachePosers && !Directory.Exists(cachePath))
Directory.CreateDirectory(cachePath);
Console.WriteLine("Parsing Kotlin Posers...");
var poserTimer = Stopwatch.StartNew();
var kotlinPosersRoot = Path.Combine(config.kotlinBasePath, "client/render/models/blockbench/pokemon");
string[] poserPaths = getAllFilesInDirandSubDirs(kotlinPosersRoot, kotlinPosersRoot);
var poserTasks = new List<Task>();
foreach (string poserPath in poserPaths) {
poserTasks.Add(Task.Run(async () => {
using (FileStream file = File.OpenRead(poserPath)) {
var poser = KotlinPoser.import(file);
string poserName = PoserRegistry.mappings![poser.poserName!];
PoserRegistry.posers[poserName] = poser;
if (config.cachePosers) {
await SaveToJsonAsync(poser, Path.Combine(cachePath, poserName + ".json"));
}
}
}));
}
printExceptionsToConsole(() => Task.WaitAll([.. poserTasks]));
poserTimer.Stop();
Console.WriteLine($"Finished in {poserTimer.ElapsedMilliseconds / 1000}s");
}
if (config.buildTasks.Contains("sounds") || config.buildTasks.Contains("importSounds")) {
Console.WriteLine("Importing Sounds..");
var javaSounds = LoadFromJson<JavaSoundJson>(Path.Combine(config.resourcesPath, "assets/cobblemon", "sounds.json"));
var bedrockSounds = javaSounds.toBedrock();
await Import.ImportAllSoundsFromSoundDef(bedrockSounds);
await SaveToJsonAsync(bedrockSounds, Path.Combine(config.resourcePath, "sounds", "sound_definitions.json"));
}
if (config.buildTasks.Contains("build")) {
if (PoserRegistry.posers.Count == 0) {
string poserCachePath = Path.Combine(config.projectPath, "poserCache");
if (config.cachePosers && Directory.Exists(poserCachePath) && Directory.GetFiles(poserCachePath).Length > 0) {
info("Using Poser Cache...");
var files = Directory.GetFiles(poserCachePath);
foreach (var file in files) {
if (!file.EndsWith(".json"))
return;
var poserName = Path.GetFileNameWithoutExtension(file);
var poser = await LoadFromJsonAsync<KotlinPoser>(file);
PoserRegistry.posers[poserName] = poser;
}
}
else {
warn("Posers have not been processed. Animation Controllers will not be created.");
}
}
//Read Item Exture Json Base
itemAtlasJson = LoadFromJson<AtlasJson>(Path.Combine(config.resourcePath, "textures/item_texture_base.json"));
//Create Terrain (Block) Atlas
blockAtlasJson = LoadFromJson<AtlasJson>(Path.Combine(config.resourcePath, "textures/terrain_texture_base.json"));
//Read Entity Textures List JSON (I found not including this in the pack meant that textures in subfolders won't load)
string EntityTexturesJsonText = File.ReadAllText(Path.Combine(config.resourcePath, "textures/textures_list_base.json"));
EntityTextures = JsonConvert.DeserializeObject<List<string>>(EntityTexturesJsonText)!;
SpawnConversion.initValues();
JavaData.populateData();
Console.WriteLine("Analyzing Species Data...");
//Scan files inside folders in common\src\main\resources\data\cobblemon\species\ to get all implimented pokemon and adds them to targetPokemon
string[] genFolders = Directory.GetDirectories(Path.Combine(config.resourcesPath, @"data\cobblemon\species\"));
foreach (string folder in genFolders) {
string[] files = Directory.GetFiles(folder);
foreach (string file in files) {
try {
string fileData = File.ReadAllText(file);
var data = JsonConvert.DeserializeObject<SpeciesData>(fileData);
if (data!.implemented == true) {
targetPokemon.Add(new Pokemon(data));
}
}
catch (Exception ex) {
softError("Could not parse json: " + ex.Message);
}
}
}
Console.WriteLine(targetPokemon.Count.ToString() + " are implimented in the source code.");
//I found that alot of the unused animations are broken, so I make a dictionary, and only the used animations are added to the pokemon.
//Also saves on space, technically
Console.WriteLine("Gathering Animations...");
var animationsRoot = Path.Combine(config.resourcesPath, "assets/cobblemon/bedrock/pokemon/animations");
var animationDirs = getAllFilesInDirandSubDirs(animationsRoot)
.Where(x => x.EndsWith(".json"));
foreach (var animationPath in animationDirs) {
var animationJson = await Import.ReadAnimation(animationPath);
foreach (var animation in animationJson.animations) {
animations.TryAdd(animation.Key, animation.Value);
}
};
Console.WriteLine("Implimenting Pokemon...");
var pokemonTasks = new List<Task>();
var pokemonStopWatch = Stopwatch.StartNew();
foreach (var pokemon in targetPokemon) {
pokemonTasks.Add(Tasks.ImportPokemon(pokemon));
}
printExceptionsToConsole(() => Task.WaitAll([.. pokemonTasks]));
pokemonStopWatch.Stop();
var pokemonCreateTime = pokemonStopWatch.ElapsedMilliseconds;
Console.WriteLine($"Implimented {sucessCounter}/{targetPokemon.Count} Pokemon in {pokemonCreateTime / 1000}s.");
Console.WriteLine("Implimenting Pokeballs...");
//Generic Pokeball Model
await Import.ImportModel("pokeball", Path.Combine(config.resourcesPath, @"assets\cobblemon\bedrock\poke_balls\models\poke_ball.geo.json"), Path.Combine(config.resourcePath, @"models\entity\pokeballs\poke_ball.geo.json"));
//Generic Pokeball Animation
string pokeballAnimationData = File.ReadAllText(Path.Combine(config.resourcesPath, @"assets\cobblemon\bedrock\poke_balls\animations\poke_ball.animation.json"));
AnimationJson pokeballAnimations = JsonConvert.DeserializeObject<AnimationJson>(pokeballAnimationData)!;
await File.WriteAllTextAsync(Path.Combine(config.resourcePath, @"animations\pokeballs\pokeball.animation.json"), JsonConvert.SerializeObject(pokeballAnimations, config.SerializerSettings));
//Read each pokeball variation
var pokeballTasks = new List<Task>();
string[] ballDirs = Directory.GetFiles(Path.Combine(config.resourcesPath, @"assets\cobblemon\bedrock\poke_balls\variations\"));
foreach (string file in ballDirs) {
pokeballTasks.Add(Tasks.ImportPokeball(file, pokeballAnimations));
}
printExceptionsToConsole(() => Task.WaitAll([.. pokemonTasks]));
//Imports all Apricorns using textures (best method we have without parsing kotlin code) (Uses Black Apricorn block and sappling as template)
Console.WriteLine("Importing Apricorns...");
var apricornTasks = new List<Task>();
foreach (string file in Directory.GetFiles(Path.Combine(config.resourcesPath, "assets/cobblemon/textures/item/"))) {
if (!file.EndsWith("apricorn.png")) {
continue;
}
string apricornName = Path.GetFileNameWithoutExtension(file);
apricornTasks.Add(Tasks.createApricorn(apricornName));
}
printExceptionsToConsole(() => Task.WaitAll([.. apricornTasks]));
//Read each pokeball variation
Console.WriteLine("Importing Berries...");
var berryTasks = new List<Task>();
string[] berryDirs = Directory.GetFiles(Path.Combine(config.resourcesPath, @"data/cobblemon/berries"));
foreach (string file in berryDirs) {
berryTasks.Add(Tasks.CreateBerry(file));
}
printExceptionsToConsole(() => Task.WaitAll([.. berryTasks]));
Console.WriteLine("Importing Other Resources...");
await Import.ImportAllTexturesFromFolder(Path.Combine(config.resourcesPath, "assets/cobblemon/textures/block"), Path.Combine(config.resourcePath, "textures/block"), Import.TextureType.Block);
await Import.ImportAllTexturesFromFolder(Path.Combine(config.resourcesPath, "assets/cobblemon/textures/item"), Path.Combine(config.resourcePath, "textures/item"), Import.TextureType.Item);
await Import.ImportAllTexturesFromFolder(Path.Combine(config.resourcesPath, "assets/cobblemon/textures/particle"), Path.Combine(config.resourcePath, "textures/particles"), Import.TextureType.Entity);
await Import.ImportAllRecipesInFolder(Path.Combine(config.resourcesPath, "data/cobblemon/recipes"), Path.Combine(config.behaviorPath, "recipes"));
//Unfortunately, model conversion is just too rough to automate
//Import.ImportAllRegisteredJavaModels();
//Copy all particles
foreach (string file in getAllFilesInDirandSubDirs(Path.Combine(config.resourcesPath, "assets/cobblemon/bedrock/particles"))) {
File.Copy(file, Path.Combine(config.resourcePath, "particles", Path.GetFileName(file)), true);
}
//Takes textures and creates functionless items with them only if that item isn't already implimented
//Creates list items that cannot be created because they already exist
List<string> existingItems = new List<string>();
foreach (string file in getAllFilesInDirandSubDirs(Path.Combine(config.behaviorPath, "items"), Path.Combine(config.behaviorPath, "items/generic"))) {
existingItems.Add(Path.GetFileNameWithoutExtension(file));
}
List<Task> genericItemTasks = new List<Task>();
foreach (string file in getAllFilesInDirandSubDirs(Path.Combine(config.resourcesPath, "assets/cobblemon/textures/item"), Path.Combine(config.resourcesPath, "assets/cobblemon/textures/item\\advancements"))) {
if (!existingItems.Contains(Path.GetFileNameWithoutExtension(file))) {
//genericItemTasks.Add(Tasks.CreateGemericItemFromTexture(file));
genericItemTasks.Add(Tasks.CreateGemericItemFromTexture(file));
}
}
printExceptionsToConsole(() => Task.WaitAll([.. genericItemTasks]));
Console.WriteLine("Finishing up...");
//Capping off Important Files
File.WriteAllText(Path.Combine(config.resourcePath, @"textures\item_texture.json"), JsonConvert.SerializeObject(itemAtlasJson, config.SerializerSettings));
File.WriteAllText(Path.Combine(config.resourcePath, @"textures\terrain_texture.json"), JsonConvert.SerializeObject(blockAtlasJson, config.SerializerSettings));
//Add liscence information
File.WriteAllText(Path.Combine(config.resourcePath, "models/entity/cobblemon/license"), Resources.CCPL);
//Creates Entity Texture List
string TextureListString = JsonConvert.SerializeObject(EntityTextures, config.SerializerSettings);
File.WriteAllText(Path.Combine(config.resourcePath, "textures/textures_list.json"), TextureListString);
//Deletes Temporary Folders
foreach (string path in temporaryFolders) {
Directory.Delete(path, true);
}
}
if (config.buildTasks.Contains("deploy")) {
if (!Directory.Exists(Path.Combine(config.projectPath, "node_modules"))) {
//Project has not been set up with npm
Console.WriteLine("Initializing Node Project...");
RunCmd("npm", "install", config.projectPath);
}
Console.WriteLine("Deploying...");
RunCmd("gulp", config.gulpArgs, config.projectPath);
}
if (config.buildTasks.Contains("launch")) {
if (config.useMinecraftPreview) {
OpenUri("minecraft-preview://");
}
else {
OpenUri("minecraft://");
}
}
}
}
}