Merge pull request #16 from quiqueck/main

Adopted WorldDataAPI for new DataFixerAPI
This commit is contained in:
paulevsGitch 2021-07-20 03:43:30 +03:00 committed by GitHub
commit 2e12635e66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 406 additions and 460 deletions

View file

@ -5,6 +5,7 @@ import net.fabricmc.api.ModInitializer;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import ru.bclib.api.TagAPI; import ru.bclib.api.TagAPI;
import ru.bclib.api.WorldDataAPI;
import ru.bclib.config.Configs; import ru.bclib.config.Configs;
import ru.bclib.recipes.CraftingRecipes; import ru.bclib.recipes.CraftingRecipes;
import ru.bclib.registry.BaseBlockEntities; import ru.bclib.registry.BaseBlockEntities;
@ -23,6 +24,7 @@ public class BCLib implements ModInitializer {
BCLSurfaceBuilders.register(); BCLSurfaceBuilders.register();
TagAPI.init(); TagAPI.init();
CraftingRecipes.init(); CraftingRecipes.init();
WorldDataAPI.registerModCache(MOD_ID);
Configs.save(); Configs.save();
} }

View file

@ -1,152 +0,0 @@
package ru.bclib.api;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.storage.RegionFile;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class DataFixerAPI {
private static final Map<String, String> REPLACEMENT = Maps.newHashMap();
private static final Map<String, Integer> FIX_VERSIONS = Maps.newHashMap();
public static void fixData(File dir) {
REPLACEMENT.clear(); // API is not finished yet!
if (REPLACEMENT.isEmpty()) {
return;
}
boolean shoudFix = false;
Collection<ModContainer> mods = FabricLoader.getInstance().getAllMods();
for (ModContainer mod : mods) {
String name = mod.getMetadata().getId();
int preVersion = WorldDataAPI.getIntModVersion(name);
int version = getModVersion(mod.getMetadata().getVersion().toString());
if (version > preVersion) {
int fixVersion = FIX_VERSIONS.getOrDefault(name, version);
shoudFix |= fixVersion < version && fixVersion >= preVersion;
}
}
;
if (!shoudFix) {
return;
}
List<File> regions = getAllRegions(dir, null);
regions.parallelStream().forEach((file) -> {
try {
System.out.println("Fixing " + file);
boolean[] changed = new boolean[1];
RegionFile region = new RegionFile(file, file.getParentFile(), true);
for (int x = 0; x < 32; x++) {
for (int z = 0; z < 32; z++) {
ChunkPos pos = new ChunkPos(x, z);
changed[0] = false;
if (region.hasChunk(pos)) {
DataInputStream input = region.getChunkDataInputStream(pos);
CompoundTag root = NbtIo.read(input);
input.close();
ListTag sections = root.getCompound("Level").getList("Sections", 10);
sections.forEach((tag) -> {
ListTag palette = ((CompoundTag) tag).getList("Palette", 10);
palette.forEach((blockTag) -> {
CompoundTag blockTagCompound = ((CompoundTag) blockTag);
String name = blockTagCompound.getString("Name");
String replace = REPLACEMENT.get(name);
if (replace != null) {
blockTagCompound.putString("Name", replace);
changed[0] = true;
}
});
});
if (changed[0]) {
System.out.println("Write!");
DataOutputStream output = region.getChunkDataOutputStream(pos);
NbtIo.write(root, output);
output.close();
}
}
}
}
region.close();
}
catch (Exception e) {
e.printStackTrace();
}
});
}
/**
* Register block data fix. Fix will be applied on world load if current mod version will be newer than specified one.
*
* @param modID - {@link String} mod id;
* @param modVersion - {@link String} mod version, should be in format: %d.%d.%d
* @param result - {@link String} new block name;
* @param names - array of {@link String}, old block names to convert.
*/
protected static void addFix(String modID, String modVersion, String result, String... names) {
FIX_VERSIONS.put(modID, getModVersion(modVersion));
for (String name : names) {
REPLACEMENT.put(name, result);
}
}
private static List<File> getAllRegions(File dir, List<File> list) {
if (list == null) {
list = Lists.newArrayList();
}
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
getAllRegions(file, list);
}
else if (file.isFile() && file.getName().endsWith(".mca")) {
list.add(file);
}
}
return list;
}
/**
* Get mod version from string. String should be in format: %d.%d.%d
*
* @param version - {@link String} mod version.
* @return int mod version.
*/
public static int getModVersion(String version) {
if (version.isEmpty()) {
return 0;
}
try {
String[] values = version.split("\\.");
return Integer.parseInt(values[0]) << 12 | Integer.parseInt(values[1]) << 6 | Integer.parseInt(values[2]);
}
catch (Exception e) {
return 0;
}
}
/**
* Get mod version from integer. String will be in format %d.%d.%d
*
* @param version - mod version in integer form.
* @return {@link String} mod version.
*/
public static String getModVersion(int version) {
int a = (version >> 12) & 63;
int b = (version >> 6) & 63;
int c = version & 63;
return String.format(Locale.ROOT, "%d.%d.%d", a, b, c);
}
}

View file

@ -1,299 +0,0 @@
package ru.bclib.api;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.storage.RegionFile;
import org.jetbrains.annotations.NotNull;
import ru.bclib.BCLib;
import ru.bclib.config.Configs;
import ru.bclib.config.PathConfig;
import ru.bclib.config.SessionConfig;
import ru.bclib.util.Logger;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class DataFixerAPI2 {
private static final Logger LOGGER = new Logger("DataFixerAPI");
public static void fixData(SessionConfig config) {
if (true || !Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_PATCH_CATEGORY, "applyPatches", true)) {
LOGGER.info("World Patches are disabled");
return;
}
final File dir = config.levelFolder;
Patch.MigrationData data = Patch.createMigrationData(config);
if (!data.hasAnyFixes()) {
LOGGER.info("Everything up to date");
return;
}
List<File> regions = getAllRegions(dir, null);
regions.parallelStream().forEach((file) -> {
try {
LOGGER.info("Inspecting " + file);
boolean[] changed = new boolean[1];
RegionFile region = new RegionFile(file, file.getParentFile(), true);
for (int x = 0; x < 32; x++) {
for (int z = 0; z < 32; z++) {
ChunkPos pos = new ChunkPos(x, z);
changed[0] = false;
if (region.hasChunk(pos)) {
DataInputStream input = region.getChunkDataInputStream(pos);
CompoundTag root = NbtIo.read(input);
// if ((root.toString().contains("betternether") || root.toString().contains("bclib")) && root.toString().contains("chest")) {
// NbtIo.write(root, new File(file.toString() + "-" + x + "-" + z + ".nbt"));
// }
input.close();
ListTag tileEntities = root.getCompound("Level").getList("TileEntities", 10);
fixItemArrayWithID(tileEntities, changed, data, true);
ListTag sections = root.getCompound("Level").getList("Sections", 10);
sections.forEach((tag) -> {
ListTag palette = ((CompoundTag) tag).getList("Palette", 10);
palette.forEach((blockTag) -> {
CompoundTag blockTagCompound = ((CompoundTag) blockTag);
changed[0] = data.replaceStringFromIDs(blockTagCompound, "Name");
});
});
if (changed[0]) {
LOGGER.warning("Writing '{}': {}/{}", file, x, z);
DataOutputStream output = region.getChunkDataOutputStream(pos);
NbtIo.write(root, output);
output.close();
}
}
}
}
region.close();
}
catch (Exception e) {
e.printStackTrace();
}
});
data.markApplied();
}
private static boolean fixItemArrayWithID(ListTag items, boolean[] changed, Patch.MigrationData data, boolean recursive) {
items.forEach(inTag -> {
final CompoundTag tag = (CompoundTag) inTag;
changed[0] |= data.replaceStringFromIDs(tag, "id");
if (recursive && tag.contains("Items")) {
changed[0] |= fixItemArrayWithID(tag.getList("Items", 10), changed, data, true);
}
});
return changed[0];
}
private static List<File> getAllRegions(File dir, List<File> list) {
if (list == null) {
list = new ArrayList<>();
}
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
getAllRegions(file, list);
}
else if (file.isFile() && file.getName().endsWith(".mca")) {
list.add(file);
}
}
return list;
}
public abstract static class Patch {
private static List<Patch> ALL = new ArrayList<>(10);
private final int level;
@NotNull
private final String modID;
static List<Patch> getALL() {
return ALL;
}
/**
* register a new Patch
*
* @param patch A #Supplier that will instantiate the new Patch Object
*/
public static void registerPatch(Supplier<Patch> patch) {
ALL.add(patch.get());
}
/**
* Returns the highest patch-level that is available for the given mod. If no patches were
* registerd for the mod, this will return 0
*
* @param modID The ID of the mod you want to query
* @return The highest Patch-Level that was found
*/
public static int maxPatchLevel(@NotNull String modID) {
return ALL.stream().filter(p -> p.getModID().equals(modID)).mapToInt(p -> p.level).max().orElse(0);
}
/**
* Called by inheriting classes.
* <p>
* Performs some sanity checks on the values and might throw a #RuntimeException if any
* inconsistencies are found.
*
* @param modID The ID of the Mod you want to register a patch for. This should be your
* ModID only. The ModID can not be {@code null} or an empty String.
* @param level The level of the Patch. This needs to be a non-zero positive value.
* Developers are responsible for registering their patches in the correct
* order (with increasing levels). You are not allowed to register a new
* Patch with a Patch-level lower or equal than
* {@link Patch#maxPatchLevel(String)}
*/
protected Patch(@NotNull String modID, int level) {
//Patchlevels need to be unique and registered in ascending order
if (modID == null || "".equals(modID)) {
throw new RuntimeException("[INTERNAL ERROR] Patches need a valid modID!");
}
if (!ALL.stream()
.filter(p -> p.getModID().equals(modID))
.noneMatch(p -> p.getLevel() >= level) || level <= 0) {
throw new RuntimeException(
"[INTERNAL ERROR] Patch-levels need to be created in ascending order beginning with 1.");
}
BCLib.LOGGER.info(
"Creating Patchlevel {} ({}, {})",
level,
ALL,
ALL.stream().noneMatch(p -> p.getLevel() >= level)
);
this.level = level;
this.modID = modID;
}
@Override
public String toString() {
return "Patch{" + "level=" + getModID() + ":" + getLevel() + '}';
}
final public int getLevel() {
return level;
}
/**
* The Mod-ID that registered this Patch
*
* @return The ID
*/
final public String getModID() {
return modID;
}
/**
* Return block data fixes. Fixes will be applied on world load if current patch-level for
* the linked mod is lower than the {@link #level}.
* <p>
* The default implementation of this method returns an empty map.
*
* @return The returned Map should contain the replacements. All occurences of the
* {@code KeySet} are replaced with the associated value.
*/
public Map<String, String> getIDReplacements() {
return new HashMap<String, String>();
}
/**
* Generates ready to use data for all currently registered patches. The list of
* patches is selected by the current patch-level of the world.
* <p>
* A {@link #Patch} with a given {@link #level} is only included if the patch-level of the
* world is less
*
* @return a new {@link MigrationData} Object.
*/
static MigrationData createMigrationData(PathConfig config) {
return new MigrationData(config);
}
static class MigrationData {
final Set<String> mods;
final Map<String, String> idReplacements;
private final PathConfig config;
private MigrationData(PathConfig config) {
this.config = config;
this.mods = Collections.unmodifiableSet(Patch.getALL()
.stream()
.map(p -> p.modID)
.collect(Collectors.toSet()));
HashMap<String, String> replacements = new HashMap<String, String>();
for (String modID : mods) {
Patch.getALL().stream().filter(p -> p.modID.equals(modID)).forEach(patch -> {
if (currentPatchLevel(modID) < patch.level) {
replacements.putAll(patch.getIDReplacements());
LOGGER.info("Applying " + patch);
}
else {
LOGGER.info("Ignoring " + patch);
}
});
}
this.idReplacements = Collections.unmodifiableMap(replacements);
}
final public void markApplied() {
for (String modID : mods) {
LOGGER.info(
"Updating Patch-Level for '{}' from {} to {} -> {}",
modID,
currentPatchLevel(modID),
Patch.maxPatchLevel(modID),
config.setInt(Configs.MAIN_PATCH_CATEGORY, modID, Patch.maxPatchLevel(modID))
);
}
config.saveChanges();
}
public int currentPatchLevel(@NotNull String modID) {
return config.getInt(Configs.MAIN_PATCH_CATEGORY, modID, 0);
}
public boolean hasAnyFixes() {
return idReplacements.size() > 0;
}
public boolean replaceStringFromIDs(@NotNull CompoundTag tag, @NotNull String key) {
if (!tag.contains(key)) return false;
final String val = tag.getString(key);
final String replace = idReplacements.get(val);
if (replace != null) {
LOGGER.warning("Replacing ID '{}' with '{}'.", val, replace);
tag.putString(key, replace);
return true;
}
return false;
}
}
}
}

View file

@ -7,6 +7,7 @@ import net.fabricmc.loader.api.ModContainer;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.NbtIo;
import ru.bclib.BCLib; import ru.bclib.BCLib;
import ru.bclib.api.datafixer.DataFixerAPI;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -24,7 +25,6 @@ public class WorldDataAPI {
MODS.stream().parallel().forEach(modID -> { MODS.stream().parallel().forEach(modID -> {
File file = new File(dataDir, modID + ".nbt"); File file = new File(dataDir, modID + ".nbt");
CompoundTag root = new CompoundTag(); CompoundTag root = new CompoundTag();
TAGS.put(modID, root);
if (file.exists()) { if (file.exists()) {
try { try {
root = NbtIo.readCompressed(file); root = NbtIo.readCompressed(file);
@ -38,7 +38,7 @@ public class WorldDataAPI {
if (optional.isPresent()) { if (optional.isPresent()) {
ModContainer modContainer = optional.get(); ModContainer modContainer = optional.get();
if (BCLib.isDevEnvironment()) { if (BCLib.isDevEnvironment()) {
root.putString("version", "63.63.63"); root.putString("version", "255.255.9999");
} }
else { else {
root.putString("version", modContainer.getMetadata().getVersion().toString()); root.putString("version", modContainer.getMetadata().getVersion().toString());
@ -46,6 +46,8 @@ public class WorldDataAPI {
saveFile(modID); saveFile(modID);
} }
} }
TAGS.put(modID, root);
}); });
} }

View file

@ -0,0 +1,183 @@
package ru.bclib.api.datafixer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.storage.RegionFile;
import ru.bclib.BCLib;
import ru.bclib.api.WorldDataAPI;
import ru.bclib.config.Configs;
import ru.bclib.util.Logger;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DataFixerAPI {
static final Logger LOGGER = new Logger("DataFixerAPI");
public static void fixData(File dir) {
if (!Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_PATCH_CATEGORY, "applyPatches", true)) {
LOGGER.info("World Patches are disabled");
return;
}
final CompoundTag patchConfig = WorldDataAPI.getCompoundTag(BCLib.MOD_ID, Configs.MAIN_PATCH_CATEGORY);
MigrationProfile data = Patch.createMigrationData(patchConfig);
if (!data.hasAnyFixes()) {
LOGGER.info("Everything up to date");
return;
}
List<File> regions = getAllRegions(dir, null);
boolean[] allOk = {true};
regions.parallelStream()
.forEach((file) -> {
try {
LOGGER.info("Inspecting " + file);
boolean[] changed = new boolean[1];
RegionFile region = new RegionFile(file, file.getParentFile(), true);
for (int x = 0; x < 32; x++) {
for (int z = 0; z < 32; z++) {
ChunkPos pos = new ChunkPos(x, z);
changed[0] = false;
if (region.hasChunk(pos)) {
DataInputStream input = region.getChunkDataInputStream(pos);
CompoundTag root = NbtIo.read(input);
// if ((root.toString().contains("betternether") || root.toString().contains("bclib")) && root.toString().contains("chest")) {
// NbtIo.write(root, new File(file.toString() + "-" + x + "-" + z + ".nbt"));
// }
input.close();
ListTag tileEntities = root.getCompound("Level")
.getList("TileEntities", 10);
fixItemArrayWithID(tileEntities, changed, data, true);
ListTag sections = root.getCompound("Level")
.getList("Sections", 10);
sections.forEach((tag) -> {
ListTag palette = ((CompoundTag) tag).getList("Palette", 10);
palette.forEach((blockTag) -> {
CompoundTag blockTagCompound = ((CompoundTag) blockTag);
changed[0] = data.replaceStringFromIDs(blockTagCompound, "Name");
});
});
if (changed[0]) {
LOGGER.warning("Writing '{}': {}/{}", file, x, z);
DataOutputStream output = region.getChunkDataOutputStream(pos);
NbtIo.write(root, output);
output.close();
}
}
}
}
region.close();
}
catch (Exception e) {
allOk[0] = false;
e.printStackTrace();
}
});
if (allOk[0]) {
data.markApplied();
WorldDataAPI.saveFile(BCLib.MOD_ID);
}
}
static CompoundTag patchConfTag = null;
static CompoundTag getPatchData(){
if (patchConfTag==null) {
patchConfTag = WorldDataAPI.getCompoundTag(BCLib.MOD_ID, Configs.MAIN_PATCH_CATEGORY);
}
return patchConfTag;
}
private static boolean fixItemArrayWithID(ListTag items, boolean[] changed, MigrationProfile data, boolean recursive) {
items.forEach(inTag -> {
final CompoundTag tag = (CompoundTag) inTag;
changed[0] |= data.replaceStringFromIDs(tag, "id");
if (recursive && tag.contains("Items")) {
changed[0] |= fixItemArrayWithID(tag.getList("Items", 10), changed, data, true);
}
});
return changed[0];
}
private static List<File> getAllRegions(File dir, List<File> list) {
if (list == null) {
list = new ArrayList<>();
}
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
getAllRegions(file, list);
}
else if (file.isFile() && file.getName()
.endsWith(".mca")) {
list.add(file);
}
}
return list;
}
/**
* register a new Patch
*
* @param patch A #Supplier that will instantiate the new Patch Object
*/
public static void registerPatch(Supplier<Patch> patch) {
Patch.getALL().add(patch.get());
}
/**
* Get mod version from string. String should be in format: %d.%d.%d
*
* @param version - {@link String} mod version.
* @return int mod version.
*/
public static int getModVersion(String version) {
if (version.isEmpty()) {
return 0;
}
try {
int res = 0;
final String semanticVersionPattern = "(\\d+)\\.(\\d+)\\.(\\d+)\\D*";
final Matcher matcher = Pattern.compile(semanticVersionPattern)
.matcher(version);
if (matcher.find()) {
if (matcher.groupCount() > 0) res = (Integer.parseInt(matcher.group(1)) & 0xFF) << 22;
if (matcher.groupCount() > 1) res |= (Integer.parseInt(matcher.group(2)) & 0xFF) << 14;
if (matcher.groupCount() > 2) res |= Integer.parseInt(matcher.group(3)) & 0x3FFF;
}
return res;
}
catch (Exception e) {
return 0;
}
}
/**
* Get mod version from integer. String will be in format %d.%d.%d
*
* @param version - mod version in integer form.
* @return {@link String} mod version.
*/
public static String getModVersion(int version) {
int a = (version >> 22) & 0xFF;
int b = (version >> 14) & 0xFF;
int c = version & 0x3FFF;
return String.format(Locale.ROOT, "%d.%d.%d", a, b, c);
}
}

View file

@ -0,0 +1,78 @@
package ru.bclib.api.datafixer;
import net.minecraft.nbt.CompoundTag;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
class MigrationProfile {
final Set<String> mods;
final Map<String, String> idReplacements;
private final CompoundTag config;
MigrationProfile(CompoundTag config) {
this.config = config;
this.mods = Collections.unmodifiableSet(Patch.getALL()
.stream()
.map(p -> p.modID)
.collect(Collectors.toSet()));
HashMap<String, String> replacements = new HashMap<String, String>();
for (String modID : mods) {
Patch.getALL()
.stream()
.filter(p -> p.modID.equals(modID))
.forEach(patch -> {
if (currentPatchLevel(modID) < patch.level) {
replacements.putAll(patch.getIDReplacements());
DataFixerAPI.LOGGER.info("Applying " + patch);
}
else {
DataFixerAPI.LOGGER.info("Ignoring " + patch);
}
});
}
this.idReplacements = Collections.unmodifiableMap(replacements);
}
final public void markApplied() {
for (String modID : mods) {
DataFixerAPI.LOGGER.info("Updating Patch-Level for '{}' from {} to {}", modID, currentPatchLevel(modID), Patch.maxPatchLevel(modID));
config.putString(modID, Patch.maxPatchVersion(modID));
}
}
public String currentPatchVersion(@NotNull String modID) {
if (!config.contains(modID)) return "0.0.0";
return config.getString(modID);
}
public int currentPatchLevel(@NotNull String modID) {
return DataFixerAPI.getModVersion(currentPatchVersion(modID));
}
public boolean hasAnyFixes() {
return idReplacements.size() > 0;
}
public boolean replaceStringFromIDs(@NotNull CompoundTag tag, @NotNull String key) {
if (!tag.contains(key)) return false;
final String val = tag.getString(key);
final String replace = idReplacements.get(val);
if (replace != null) {
DataFixerAPI.LOGGER.warning("Replacing ID '{}' with '{}'.", val, replace);
tag.putString(key, replace);
return true;
}
return false;
}
}

View file

@ -0,0 +1,135 @@
package ru.bclib.api.datafixer;
import net.minecraft.nbt.CompoundTag;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class Patch {
private static List<Patch> ALL = new ArrayList<>(10);
/**
* The Patch-Level derived from {@link #version}
*/
public final int level;
/**
* The Patch-Version string
*/
public final String version;
/**
* The Mod-ID that registered this Patch
*/
@NotNull
public final String modID;
static List<Patch> getALL() {
return ALL;
}
/**
* Returns the highest Patch-Version that is available for the given mod. If no patches were
* registerd for the mod, this will return 0.0.0
*
* @param modID The ID of the mod you want to query
* @return The highest Patch-Version that was found
*/
public static String maxPatchVersion(@NotNull String modID) {
return ALL.stream()
.filter(p -> p.modID
.equals(modID))
.map(p -> p.version)
.reduce((p, c) -> c)
.orElse("0.0.0");
}
/**
* Returns the highest patch-level that is available for the given mod. If no patches were
* registerd for the mod, this will return 0
*
* @param modID The ID of the mod you want to query
* @return The highest Patch-Level that was found
*/
public static int maxPatchLevel(@NotNull String modID) {
return ALL.stream()
.filter(p -> p.modID
.equals(modID))
.mapToInt(p -> p.level)
.max()
.orElse(0);
}
/**
* Called by inheriting classes.
* <p>
* Performs some sanity checks on the values and might throw a #RuntimeException if any
* inconsistencies are found.
*
* @param modID The ID of the Mod you want to register a patch for. This should be your
* ModID only. The ModID can not be {@code null} or an empty String.
* @param version The mod-version that introduces the patch. This needs Semantic-Version String
* like x.x.x. Developers are responsible for registering their patches in the correct
* order (with increasing versions). You are not allowed to register a new
* Patch with a version lower or equal than
* {@link Patch#maxPatchVersion(String)}
*/
protected Patch(@NotNull String modID, String version) {
//Patchlevels need to be unique and registered in ascending order
if (modID == null || "".equals(modID)) {
throw new RuntimeException("[INTERNAL ERROR] Patches need a valid modID!");
}
if (version == null || "".equals(version)) {
throw new RuntimeException("Invalid Mod-Version");
}
this.version = version;
this.level = DataFixerAPI.getModVersion(version);
if (!ALL.stream()
.filter(p -> p.modID
.equals(modID))
.noneMatch(p -> p.level >= this.level) || this.level <= 0) {
throw new RuntimeException("[INTERNAL ERROR] Patch-levels need to be created in ascending order beginning with 1.");
}
this.modID = modID;
}
@Override
public String toString() {
return "Patch{" + modID + ':' +version + ':' + level + '}';
}
/**
* Return block data fixes. Fixes will be applied on world load if current patch-level for
* the linked mod is lower than the {@link #level}.
* <p>
* The default implementation of this method returns an empty map.
*
* @return The returned Map should contain the replacements. All occurences of the
* {@code KeySet} are replaced with the associated value.
*/
public Map<String, String> getIDReplacements() {
return new HashMap<String, String>();
}
/**
* Generates ready to use data for all currently registered patches. The list of
* patches is selected by the current patch-level of the world.
* <p>
* A {@link #Patch} with a given {@link #level} is only included if the patch-level of the
* world is less
* @param config The current patch-level configuration
* @return a new {@link MigrationProfile} Object.
*/
static MigrationProfile createMigrationData(CompoundTag config) {
return new MigrationProfile(config);
}
}

View file

@ -18,8 +18,8 @@ public class SessionConfig extends PathConfig {
public final File levelFolder; public final File levelFolder;
public SessionConfig(String modID, String group, LevelStorageSource.LevelStorageAccess session, ServerLevel world) { public SessionConfig(String modID, String group, LevelStorageSource.LevelStorageAccess session, ServerLevel world) {
super(modID, group, new File(getWorldFolder(session, world), BCLib.MOD_ID)); super(modID, group, new File(getWorldFolder(session, world), BCLib.MOD_ID+"-config"));
this.levelFolder = new File(getWorldFolder(session, world), BCLib.MOD_ID); this.levelFolder = new File(getWorldFolder(session, world), BCLib.MOD_ID+"-config");
} }
} }

View file

@ -16,11 +16,9 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import ru.bclib.BCLib;
import ru.bclib.api.BiomeAPI; import ru.bclib.api.BiomeAPI;
import ru.bclib.api.DataFixerAPI2;
import ru.bclib.api.WorldDataAPI; import ru.bclib.api.WorldDataAPI;
import ru.bclib.config.SessionConfig; import ru.bclib.api.datafixer.DataFixerAPI;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
@ -51,8 +49,7 @@ public abstract class ServerLevelMixin extends Level {
dir = dir.getParentFile(); dir = dir.getParentFile();
} }
//DataFixerAPI.fixData(dir);
DataFixerAPI2.fixData(new SessionConfig(BCLib.MOD_ID, "patches", session, world));
WorldDataAPI.load(new File(dir, "data")); WorldDataAPI.load(new File(dir, "data"));
DataFixerAPI.fixData(dir);
} }
} }