Skip to content

Commit

Permalink
MTE registries datafixer for BQu (GregTechCEu#2547)
Browse files Browse the repository at this point in the history
  • Loading branch information
TechLord22 authored Jul 26, 2024
1 parent e0a7e53 commit 9f3bb32
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/main/java/gregtech/api/util/Mods.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public enum Mods {
AdvancedRocketry(Names.ADVANCED_ROCKETRY),
AppliedEnergistics2(Names.APPLIED_ENERGISTICS2),
Baubles(Names.BAUBLES),
BetterQuestingUnofficial(Names.BETTER_QUESTING, mod -> {
var container = Loader.instance().getIndexedModList().get(Names.BETTER_QUESTING);
return container.getVersion().startsWith("4.");
}),
BinnieCore(Names.BINNIE_CORE),
BiomesOPlenty(Names.BIOMES_O_PLENTY),
BuildCraftCore(Names.BUILD_CRAFT_CORE),
Expand Down Expand Up @@ -96,6 +100,7 @@ public static class Names {
public static final String ADVANCED_ROCKETRY = "advancedrocketry";
public static final String APPLIED_ENERGISTICS2 = "appliedenergistics2";
public static final String BAUBLES = "baubles";
public static final String BETTER_QUESTING = "betterquesting";
public static final String BINNIE_CORE = "binniecore";
public static final String BIOMES_O_PLENTY = "biomesoplenty";
public static final String BUILD_CRAFT_CORE = "buildcraftcore";
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/gregtech/core/CoreModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import gregtech.api.unification.material.event.PostMaterialEvent;
import gregtech.api.unification.material.registry.MarkerMaterialRegistry;
import gregtech.api.util.CapesRegistry;
import gregtech.api.util.Mods;
import gregtech.api.util.VirtualTankRegistry;
import gregtech.api.util.input.KeyBind;
import gregtech.api.util.oreglob.OreGlob;
Expand Down Expand Up @@ -71,6 +72,8 @@
import gregtech.core.sound.GTSoundEvents;
import gregtech.core.sound.internal.SoundManager;
import gregtech.core.unification.material.internal.MaterialRegistryManager;
import gregtech.datafix.command.CommandDataFix;
import gregtech.integration.bq.BQuDataFixer;
import gregtech.loaders.dungeon.DungeonLootLoader;
import gregtech.modules.GregTechModules;

Expand Down Expand Up @@ -316,7 +319,12 @@ public void serverStarting(FMLServerStartingEvent event) {
GregTechAPI.commandManager.addCommand(new CommandHand());
GregTechAPI.commandManager.addCommand(new CommandRecipeCheck());
GregTechAPI.commandManager.addCommand(new CommandShaders());
GregTechAPI.commandManager.addCommand(new CommandDataFix());
CapesRegistry.load();

if (Mods.BetterQuestingUnofficial.isModLoaded()) {
BQuDataFixer.onServerStarting(event.getServer());
}
}

@Override
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/gregtech/datafix/command/CommandDataFix.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package gregtech.datafix.command;

import gregtech.api.util.Mods;
import gregtech.integration.bq.CommandBQuDataFix;

import net.minecraft.command.ICommandSender;
import net.minecraftforge.server.command.CommandTreeBase;

import org.jetbrains.annotations.NotNull;

public final class CommandDataFix extends CommandTreeBase {

public CommandDataFix() {
if (Mods.BetterQuestingUnofficial.isModLoaded()) {
addSubcommand(new CommandBQuDataFix());
}
}

@Override
public @NotNull String getName() {
return "datafix";
}

@Override
public int getRequiredPermissionLevel() {
return 3;
}

@Override
public @NotNull String getUsage(@NotNull ICommandSender sender) {
return "gregtech.command.datafix.usage";
}
}
245 changes: 245 additions & 0 deletions src/main/java/gregtech/integration/bq/BQuDataFixer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
package gregtech.integration.bq;

import gregtech.api.GregTechAPI;
import gregtech.api.util.Mods;
import gregtech.datafix.migration.lib.MTERegistriesMigrator;

import net.minecraft.command.ICommandSender;
import net.minecraft.util.ResourceLocation;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public final class BQuDataFixer {

private static final Logger LOG = LogManager.getLogger(BQuDataFixer.class);

/**
* {@code saves/<world>/betterquesting/QuestDatabase.json}
*/
private static final String QUEST_DB_JSON_FILE = "QuestDatabase.json";
/**
* {@code config/betterquesting/resources/}
*/
private static final String BQ_RESOURCES_DIR = "resources";
private static final String MC_CONFIG_DIR = "config";

private static final String ID_8 = "id:8";
private static final String DAMAGE_2 = "Damage:2";
private static final String TAG_10 = "tag:10";
private static final String ORIG_ID_8 = "orig_id:8";
private static final String ORIG_META_3 = "orig_meta:3";

private static final String PLACEHOLDER = "betterquesting:placeholder";

private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();

private BQuDataFixer() {}

public static void onServerStarting(@NotNull ICommandSender server) {
LOG.info("Attempting to apply BQu DataFixers");
Path worldDir = server.getEntityWorld().getSaveHandler().getWorldDirectory().toPath();
if (processWorldDir(worldDir)) {
LOG.info("Successfully applied BQu data fixers");
} else {
LOG.error("Failed to apply BQu data fixers");
}
}

/**
* Processes the current world directory and applies data fixers
*
* @param worldDir the current world's directory
* @return if processing was successful
*/
public static boolean processWorldDir(@NotNull Path worldDir) {
LOG.info("Processing world directory");
Path bqDir = worldDir.resolve(Mods.Names.BETTER_QUESTING);
Path questDbPath = bqDir.resolve(QUEST_DB_JSON_FILE);
if (!Files.isRegularFile(questDbPath)) {
LOG.info("Unable to find BQ Quest Database, assuming this is a new world");
return true;
}

JsonObject json;
try {
json = readJson(questDbPath);
} catch (FileNotFoundException e) {
LOG.info("Unable to find BQ Quest Database, assuming this is a new world");
return true;
} catch (IOException e) {
LOG.error("Failed to read BQ Quest Database in World Folder", e);
return false;
}

for (var entry : json.entrySet()) {
recurseJsonApplyFix(entry.getValue());
}

try {
writeJson(questDbPath, json);
} catch (IOException e) {
LOG.error("Failed to write BQ Quest DataBase in World Folder", e);
}

return true;
}

/**
* Processes the BQu config directory and applies data fixers
*
* @param worldDir the current world's directory
* @return if processing was successful
*/
public static boolean processConfigDir(@NotNull Path worldDir) {
LOG.info("Processing config directory");
Path mcDir = worldDir.getParent().getParent();
Path configDir = mcDir.resolve(MC_CONFIG_DIR);
Path bqConfigDir = configDir.resolve(Mods.Names.BETTER_QUESTING);

List<Path> paths;
try (var stream = Files.walk(bqConfigDir, 4)) {
paths = stream.filter(p -> !p.endsWith(BQ_RESOURCES_DIR)) // do not walk down the resources dir
.filter(Files::isRegularFile)
.filter(path -> path.toFile().getName().endsWith(".json"))
.collect(Collectors.toList());
} catch (IOException e) {
LOG.error("Failed to read BQ Quest Database in Config Folder", e);
return false;
}

Map<Path, JsonObject> map = new Object2ObjectOpenHashMap<>(paths.size());
for (Path path : paths) {
try {
map.put(path, readJson(path));
} catch (IOException e) {
LOG.error("Failed to read BQ Quest File from Config Folder", e);
}
}

map.values().stream()
.flatMap(jsonObject -> jsonObject.entrySet().stream())
.map(Map.Entry::getValue)
.forEach(BQuDataFixer::recurseJsonApplyFix);

for (var entry : map.entrySet()) {
try {
writeJson(entry.getKey(), entry.getValue());
} catch (IOException e) {
LOG.error("Failed to write BQ Quest File in Config Folder", e);
}
}

return true;
}

/**
* @param path the path to read
* @return the json object stored at the path
* @throws IOException if failure
*/
private static @NotNull JsonObject readJson(@NotNull Path path) throws IOException {
LOG.info("Reading file at path {}", path);
try (Reader reader = Files.newBufferedReader(path)) {
return GSON.fromJson(reader, JsonObject.class);
}
}

/**
* Recursively walks a JSON file and applies datafixers to matching sub-objects
*
* @param element the json element to recurse
*/
private static void recurseJsonApplyFix(@NotNull JsonElement element) {
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
if (object.has(ID_8)) {
applyDataFix(object);
} else {
for (var entry : object.entrySet()) {
recurseJsonApplyFix(entry.getValue());
}
}
} else if (element.isJsonArray()) {
for (JsonElement value : element.getAsJsonArray()) {
recurseJsonApplyFix(value);
}
}
}

/**
* @param path the path to write the file
* @param jsonObject the object to write
* @throws IOException if failure
*/
private static void writeJson(@NotNull Path path, @NotNull JsonObject jsonObject) throws IOException {
LOG.info("Writing file to path {}", path);
try (Writer writer = Files.newBufferedWriter(path)) {
GSON.toJson(jsonObject, writer);
}
}

/**
* Applies {@link MTERegistriesMigrator} fixes to a BQu json file
*
* @param jsonObject the json to fix
*/
private static void applyDataFix(@NotNull JsonObject jsonObject) {
MTERegistriesMigrator migrator = GregTechAPI.MIGRATIONS.registriesMigrator();

ResourceLocation itemBlockId;
short meta;
String id = jsonObject.get(ID_8).getAsString();

boolean isPlaceHolder = PLACEHOLDER.equals(id);

if (isPlaceHolder) {
// fix cases where BQu marks items as missing with placeholders
JsonObject orig = jsonObject.getAsJsonObject(TAG_10);
if (orig == null) {
return;
}

if (!orig.has(ORIG_ID_8) || !orig.has(ORIG_META_3)) {
return;
}

itemBlockId = new ResourceLocation(orig.get(ORIG_ID_8).getAsString());
meta = orig.get(ORIG_META_3).getAsShort();
} else {
itemBlockId = new ResourceLocation(id);
meta = jsonObject.get(DAMAGE_2).getAsShort();
}

ResourceLocation fixedName = migrator.fixItemName(itemBlockId, meta);
if (fixedName != null) {
jsonObject.add(ID_8, new JsonPrimitive(fixedName.toString()));
}

short fixedMeta = migrator.fixItemMeta(itemBlockId, meta);
if (fixedMeta != meta) {
jsonObject.add(DAMAGE_2, new JsonPrimitive(fixedMeta));
}

if (isPlaceHolder) {
jsonObject.remove(TAG_10);
}
}
}
54 changes: 54 additions & 0 deletions src/main/java/gregtech/integration/bq/CommandBQuDataFix.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package gregtech.integration.bq;

import net.minecraft.command.CommandBase;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.util.text.TextFormatting;

import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

import java.nio.file.Path;

@ApiStatus.Internal
public final class CommandBQuDataFix extends CommandBase {

@Override
public @NotNull String getName() {
return "bqu";
}

@Override
public @NotNull String getUsage(@NotNull ICommandSender sender) {
return "gregtech.command.datafix.bqu.usage";
}

@Override
public int getRequiredPermissionLevel() {
return 3;
}

@Override
public void execute(@NotNull MinecraftServer server, @NotNull ICommandSender sender, String @NotNull [] args) {
if (args.length < 1 || !"confirm".equalsIgnoreCase(args[0])) {
sender.sendMessage(new TextComponentTranslation("gregtech.command.datafix.bqu.backup")
.setStyle(new Style().setColor(TextFormatting.YELLOW)));
return;
}

sender.sendMessage(new TextComponentTranslation("gregtech.command.datafix.bqu.start")
.setStyle(new Style().setColor(TextFormatting.YELLOW)));

Path worldDir = server.getEntityWorld().getSaveHandler().getWorldDirectory().toPath();

if (BQuDataFixer.processConfigDir(worldDir) && BQuDataFixer.processWorldDir(worldDir)) {
sender.sendMessage(new TextComponentTranslation("gregtech.command.datafix.bqu.complete")
.setStyle(new Style().setColor(TextFormatting.GREEN)));
} else {
sender.sendMessage(new TextComponentTranslation("gregtech.command.datafix.bqu.failed")
.setStyle(new Style().setColor(TextFormatting.RED)));
}
}
}
Loading

0 comments on commit 9f3bb32

Please sign in to comment.