Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MTE registries datafixer for BQu #2547

Merged
merged 3 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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