*WIP*: Merge commit 'ce4cb8974f
' into 1.18
This commit is contained in:
commit
c6a7a1d4f7
76 changed files with 2754 additions and 738 deletions
|
@ -1,28 +1,42 @@
|
|||
package ru.bclib.api;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.biome.v1.NetherBiomes;
|
||||
import net.fabricmc.fabric.impl.biome.NetherBiomeData;
|
||||
import net.fabricmc.fabric.impl.biome.TheEndBiomeData;
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
|
||||
import net.fabricmc.fabric.mixin.biome.modification.GenerationSettingsAccessor;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.BuiltinRegistries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.Biome.ClimateParameters;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraft.world.level.biome.Climate;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep.Decoration;
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import ru.bclib.util.MHelper;
|
||||
import ru.bclib.world.biomes.BCLBiome;
|
||||
import ru.bclib.world.biomes.FabricBiomesData;
|
||||
import ru.bclib.world.features.BCLFeature;
|
||||
import ru.bclib.world.generator.BiomePicker;
|
||||
import ru.bclib.world.structures.BCLStructureFeature;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class BiomeAPI {
|
||||
/**
|
||||
|
@ -39,6 +53,9 @@ public class BiomeAPI {
|
|||
private static final Map<Biome, BCLBiome> CLIENT = Maps.newHashMap();
|
||||
private static Registry<Biome> biomeRegistry;
|
||||
|
||||
private static final Map<ResourceKey, List<BiConsumer<ResourceLocation, Biome>>> MODIFICATIONS = Maps.newHashMap();
|
||||
private static final Set<ResourceLocation> MODIFIED_BIOMES = Sets.newHashSet();
|
||||
|
||||
public static final BCLBiome NETHER_WASTES_BIOME = registerNetherBiome(getFromRegistry(Biomes.NETHER_WASTES));
|
||||
public static final BCLBiome CRIMSON_FOREST_BIOME = registerNetherBiome(getFromRegistry(Biomes.CRIMSON_FOREST));
|
||||
public static final BCLBiome WARPED_FOREST_BIOME = registerNetherBiome(getFromRegistry(Biomes.WARPED_FOREST));
|
||||
|
@ -325,4 +342,143 @@ public class BiomeAPI {
|
|||
private static boolean pickerHasBiome(BiomePicker picker, ResourceLocation key) {
|
||||
return picker.getBiomes().stream().filter(biome -> biome.getID().equals(key)).findFirst().isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers new biome modification for specified dimension. Will work both for mod and datapack biomes.
|
||||
* @param dimensionID {@link ResourceLocation} dimension ID, example: Level.OVERWORLD or "minecraft:overworld".
|
||||
* @param modification {@link BiConsumer} with {@link ResourceKey} biome ID and {@link Biome} parameters.
|
||||
*/
|
||||
public static void registerBiomeModification(ResourceKey dimensionID, BiConsumer<ResourceLocation, Biome> modification) {
|
||||
List<BiConsumer<ResourceLocation, Biome>> modifications = MODIFICATIONS.get(dimensionID);
|
||||
if (modifications == null) {
|
||||
modifications = Lists.newArrayList();
|
||||
MODIFICATIONS.put(dimensionID, modifications);
|
||||
}
|
||||
modifications.add(modification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers new biome modification for the Overworld. Will work both for mod and datapack biomes.
|
||||
* @param modification {@link BiConsumer} with {@link ResourceLocation} biome ID and {@link Biome} parameters.
|
||||
*/
|
||||
public static void registerOverworldBiomeModification(BiConsumer<ResourceLocation, Biome> modification) {
|
||||
registerBiomeModification(Level.OVERWORLD, modification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers new biome modification for the Nether. Will work both for mod and datapack biomes.
|
||||
* @param modification {@link BiConsumer} with {@link ResourceLocation} biome ID and {@link Biome} parameters.
|
||||
*/
|
||||
public static void registerNetherBiomeModification(BiConsumer<ResourceLocation, Biome> modification) {
|
||||
registerBiomeModification(Level.NETHER, modification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers new biome modification for the End. Will work both for mod and datapack biomes.
|
||||
* @param modification {@link BiConsumer} with {@link ResourceLocation} biome ID and {@link Biome} parameters.
|
||||
*/
|
||||
public static void registerEndBiomeModification(BiConsumer<ResourceLocation, Biome> modification) {
|
||||
registerBiomeModification(Level.END, modification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will apply biome modifications to world, internal usage only.
|
||||
* @param level
|
||||
*/
|
||||
public static void applyModifications(ServerLevel level) {
|
||||
List<BiConsumer<ResourceLocation, Biome>> modifications = MODIFICATIONS.get(level.dimension());
|
||||
if (modifications == null) {
|
||||
return;
|
||||
}
|
||||
BiomeSource source = level.getChunkSource().getGenerator().getBiomeSource();
|
||||
List<Biome> biomes = source.possibleBiomes();
|
||||
|
||||
biomes.forEach(biome -> {
|
||||
ResourceLocation biomeID = getBiomeID(biome);
|
||||
boolean modify = isDatapackBiome(biomeID);
|
||||
if (!modify && !MODIFIED_BIOMES.contains(biomeID)) {
|
||||
MODIFIED_BIOMES.add(biomeID);
|
||||
modify = true;
|
||||
}
|
||||
if (modify) {
|
||||
modifications.forEach(consumer -> {
|
||||
consumer.accept(biomeID, biome);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new features to existing biome.
|
||||
* @param biome {@link Biome} to add features in.
|
||||
* @param feature {@link ConfiguredFeature} to add.
|
||||
* @param step a {@link Decoration} step for the feature.
|
||||
*/
|
||||
public static void addBiomeFeature(Biome biome, ConfiguredFeature feature, Decoration step) {
|
||||
GenerationSettingsAccessor accessor = (GenerationSettingsAccessor) biome.getGenerationSettings();
|
||||
List<List<Supplier<ConfiguredFeature<?, ?>>>> biomeFeatures = getMutableList(accessor.fabric_getFeatures());
|
||||
int index = step.ordinal();
|
||||
if (biomeFeatures.size() < index) {
|
||||
for (int i = biomeFeatures.size(); i <= index; i++) {
|
||||
biomeFeatures.add(Lists.newArrayList());
|
||||
}
|
||||
}
|
||||
List<Supplier<ConfiguredFeature<?, ?>>> list = getMutableList(biomeFeatures.get(index));
|
||||
list.add(() -> feature);
|
||||
accessor.fabric_setFeatures(biomeFeatures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new features to existing biome.
|
||||
* @param biome {@link Biome} to add features in.
|
||||
* @param features array of {@link BCLFeature} to add.
|
||||
*/
|
||||
public static void addBiomeFeatures(Biome biome, BCLFeature... features) {
|
||||
GenerationSettingsAccessor accessor = (GenerationSettingsAccessor) biome.getGenerationSettings();
|
||||
List<List<Supplier<ConfiguredFeature<?, ?>>>> biomeFeatures = getMutableList(accessor.fabric_getFeatures());
|
||||
for (BCLFeature feature: features) {
|
||||
int index = feature.getFeatureStep().ordinal();
|
||||
if (biomeFeatures.size() < index) {
|
||||
for (int i = biomeFeatures.size(); i <= index; i++) {
|
||||
biomeFeatures.add(Lists.newArrayList());
|
||||
}
|
||||
}
|
||||
List<Supplier<ConfiguredFeature<?, ?>>> list = getMutableList(biomeFeatures.get(index));
|
||||
list.add(feature::getFeatureConfigured);
|
||||
}
|
||||
accessor.fabric_setFeatures(biomeFeatures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new structure feature to existing biome.
|
||||
* @param biome {@link Biome} to add structure feature in.
|
||||
* @param structure {@link ConfiguredStructureFeature} to add.
|
||||
*/
|
||||
public static void addBiomeStructure(Biome biome, ConfiguredStructureFeature structure) {
|
||||
GenerationSettingsAccessor accessor = (GenerationSettingsAccessor) biome.getGenerationSettings();
|
||||
List<Supplier<ConfiguredStructureFeature<?, ?>>> biomeStructures = getMutableList(accessor.fabric_getStructureFeatures());
|
||||
biomeStructures.add(() -> structure);
|
||||
accessor.fabric_setStructureFeatures(biomeStructures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new structure features to existing biome.
|
||||
* @param biome {@link Biome} to add structure features in.
|
||||
* @param structures array of {@link BCLStructureFeature} to add.
|
||||
*/
|
||||
public static void addBiomeStructures(Biome biome, BCLStructureFeature... structures) {
|
||||
GenerationSettingsAccessor accessor = (GenerationSettingsAccessor) biome.getGenerationSettings();
|
||||
List<Supplier<ConfiguredStructureFeature<?, ?>>> biomeStructures = getMutableList(accessor.fabric_getStructureFeatures());
|
||||
for (BCLStructureFeature structure: structures) {
|
||||
biomeStructures.add(structure::getFeatureConfigured);
|
||||
}
|
||||
accessor.fabric_setStructureFeatures(biomeStructures);
|
||||
}
|
||||
|
||||
private static <T extends Object> List<T> getMutableList(List<T> input) {
|
||||
if (input instanceof ImmutableList) {
|
||||
return Lists.newArrayList(input);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,9 +34,16 @@ public class TagAPI {
|
|||
public static final Tag.Named<Block> BLOCK_END_GROUND = makeBlockTag(BCLib.MOD_ID, "end_ground");
|
||||
|
||||
public static final Tag.Named<Block> BLOCK_CHEST = makeCommonBlockTag("chest");
|
||||
public static final Tag.Named<Block> BLOCK_WOODEN_CHEST = makeCommonBlockTag("wooden_chests");
|
||||
public static final Tag.Named<Block> BLOCK_BARREL = makeCommonBlockTag("barrel");
|
||||
public static final Tag.Named<Block> BLOCK_WOODEN_BARREL = makeCommonBlockTag("wooden_barrels");
|
||||
public static final Tag.Named<Block> BLOCK_END_STONES = makeCommonBlockTag("end_stones");
|
||||
public static final Tag.Named<Block> BLOCK_NETHER_STONES = makeCommonBlockTag("nether_stones");
|
||||
public static final Tag.Named<Block> BLOCK_WORKBENCHES = makeCommonBlockTag("workbenches");
|
||||
public static final Tag.Named<Block> BLOCK_NETHER_PORTAL_FRAME = makeCommonBlockTag("nether_pframe");
|
||||
public static final Tag.Named<Block> BLOCK_WORKBENCHES = makeCommonBlockTag("workbench");
|
||||
public static final Tag.Named<Block> BLOCK_SAPLINGS = makeCommonBlockTag("saplings");
|
||||
public static final Tag.Named<Block> BLOCK_LEAVES = makeCommonBlockTag("leaves");
|
||||
public static final Tag.Named<Block> BLOCK_IMMOBILE = makeCommonBlockTag("immobile");
|
||||
|
||||
public static final Tag.Named<Block> BLOCK_DRAGON_IMMUNE = getMCBlockTag("dragon_immune");
|
||||
|
||||
|
@ -47,11 +54,19 @@ public class TagAPI {
|
|||
|
||||
// Item Tags
|
||||
public static final Tag.Named<Item> ITEM_CHEST = makeCommonItemTag("chest");
|
||||
public static final Tag.Named<Item> ITEM_WOODEN_CHEST = makeCommonItemTag("wooden_chests");
|
||||
public static final Tag.Named<Item> ITEM_BARREL = makeCommonItemTag("barrel");
|
||||
public static final Tag.Named<Item> ITEM_WOODEN_BARREL = makeCommonItemTag("wooden_barrels");
|
||||
public static final Tag.Named<Item> ITEM_IRON_INGOTS = makeCommonItemTag("iron_ingots");
|
||||
public static final Tag.Named<Item> ITEM_FURNACES = makeCommonItemTag("furnaces");
|
||||
public static final Tag.Named<Item> ITEM_WORKBENCHES = makeCommonItemTag("workbenches");
|
||||
public static final Tag.Named<Item> ITEM_WORKBENCHES = makeCommonItemTag("workbench");
|
||||
public final static Tag.Named<Item> ITEM_HAMMERS = makeCommonItemTag("hammers");
|
||||
|
||||
public static final Tag.Named<Item> ITEM_SAPLINGS = makeCommonItemTag("saplings");
|
||||
public static final Tag.Named<Item> ITEM_LEAVES = makeCommonItemTag("leaves");
|
||||
public static final Tag.Named<Item> ITEM_SHEARS = getMCItemTag("shears");
|
||||
public static final Tag.Named<Item> ITEM_COMMON_SHEARS = makeCommonItemTag("shears");
|
||||
|
||||
|
||||
/**
|
||||
* Get or create {@link Tag.Named}.
|
||||
*
|
||||
|
@ -122,6 +137,18 @@ public class TagAPI {
|
|||
return tag == null ? (Named<Block>) TagFactory.BLOCK.create(id): (Named<Block>) tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create Minecraft {@link Item} {@link Tag.Named}.
|
||||
*
|
||||
* @param name - {@link String} tag name.
|
||||
* @return {@link Item} {@link Tag.Named}.
|
||||
*/
|
||||
public static Tag.Named<Item> getMCItemTag(String name) {
|
||||
ResourceLocation id = new ResourceLocation(name);
|
||||
Tag<Item> tag = ItemTags.getAllTags().getTag(id);
|
||||
return tag == null ? (Named<Item>) TagRegistry.item(id) : (Named<Item>) tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds {@link Block} to NETHER_GROUND and GEN_TERRAIN tags to process it properly in terrain generators and block logic.
|
||||
*
|
||||
|
|
|
@ -135,7 +135,7 @@ public class AutoSync {
|
|||
}
|
||||
|
||||
private static boolean didRegisterAdditionalMods = false;
|
||||
//we call this from HelloClient on the Srérver to prepare transfer
|
||||
//we call this from HelloClient on the Server to prepare transfer
|
||||
protected static void loadSyncFolder() {
|
||||
if (Configs.SERVER_CONFIG.isOfferingFiles()) {
|
||||
syncFolderDescriptions.forEach(desc -> desc.loadCache());
|
||||
|
|
|
@ -4,10 +4,10 @@ import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
|||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.ProgressListener;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.ProgressListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import ru.bclib.BCLib;
|
||||
|
|
|
@ -16,6 +16,7 @@ import ru.bclib.api.dataexchange.DataHandlerDescriptor;
|
|||
import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID.WithContentOverride;
|
||||
import ru.bclib.api.dataexchange.handler.autosync.SyncFolderDescriptor.SubFile;
|
||||
import ru.bclib.config.Configs;
|
||||
import ru.bclib.config.ServerConfig;
|
||||
import ru.bclib.gui.screens.ModListScreen;
|
||||
import ru.bclib.gui.screens.ProgressScreen;
|
||||
import ru.bclib.gui.screens.SyncFilesScreen;
|
||||
|
@ -91,6 +92,11 @@ public class HelloClient extends DataHandler.FromServer {
|
|||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
|
||||
mods = mods
|
||||
.stream()
|
||||
.filter(entry -> !Configs.SERVER_CONFIG.get(ServerConfig.EXCLUDED_MODS).contains(entry))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//write Plugin Versions
|
||||
buf.writeInt(mods.size());
|
||||
|
@ -394,6 +400,9 @@ public class HelloClient extends DataHandler.FromServer {
|
|||
requestBCLibDownload();
|
||||
|
||||
this.onCloseSyncFilesScreen();
|
||||
} else {
|
||||
Minecraft.getInstance()
|
||||
.setScreen(null);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import ru.bclib.api.WorldDataAPI;
|
|||
import ru.bclib.config.Configs;
|
||||
import ru.bclib.gui.screens.AtomicProgressListener;
|
||||
import ru.bclib.gui.screens.ConfirmFixScreen;
|
||||
import ru.bclib.gui.screens.LevelFixErrorScreen;
|
||||
import ru.bclib.gui.screens.LevelFixErrorScreen.Listener;
|
||||
import ru.bclib.gui.screens.ProgressScreen;
|
||||
import ru.bclib.util.Logger;
|
||||
|
||||
|
@ -39,6 +41,7 @@ import java.util.function.BiConsumer;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
/**
|
||||
* API to manage Patches that need to get applied to a world
|
||||
|
@ -47,6 +50,24 @@ public class DataFixerAPI {
|
|||
static final Logger LOGGER = new Logger("DataFixerAPI");
|
||||
static class State {
|
||||
public boolean didFail = false;
|
||||
protected ArrayList<String> errors = new ArrayList<>();
|
||||
|
||||
public void addError(String s){
|
||||
errors.add(s);
|
||||
}
|
||||
|
||||
public boolean hasError(){
|
||||
return errors.size()>0;
|
||||
}
|
||||
|
||||
public String getErrorMessage(){
|
||||
return errors.stream().reduce("", (a, b) -> a + " - " + b + "\n");
|
||||
}
|
||||
|
||||
public String[] getErrorMessages(){
|
||||
String[] res = new String[errors.size()];
|
||||
return errors.toArray(res);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
|
@ -200,30 +221,48 @@ public class DataFixerAPI {
|
|||
progress = null;
|
||||
}
|
||||
|
||||
Runnable runner = () -> {
|
||||
Supplier<State> runner = () -> {
|
||||
if (createBackup) {
|
||||
progress.progressStage(new TranslatableComponent("message.bclib.datafixer.progress.waitbackup"));
|
||||
EditWorldScreen.makeBackupAndShowToast(Minecraft.getInstance().getLevelSource(), levelID);
|
||||
}
|
||||
|
||||
if (applyFixes) {
|
||||
runDataFixes(dir, profile, progress);
|
||||
return runDataFixes(dir, profile, progress);
|
||||
}
|
||||
|
||||
return new State();
|
||||
};
|
||||
|
||||
if (showUI) {
|
||||
Thread fixerThread = new Thread(() -> {
|
||||
runner.run();
|
||||
|
||||
Minecraft.getInstance().execute(() -> {
|
||||
if (profile != null && showUI) {
|
||||
onResume.accept(applyFixes);
|
||||
}
|
||||
});
|
||||
final State state = runner.get();
|
||||
|
||||
Minecraft.getInstance()
|
||||
.execute(() -> {
|
||||
if (profile != null && showUI) {
|
||||
//something went wrong, show the user our error
|
||||
if (state.didFail || state.hasError()){
|
||||
showLevelFixErrorScreen(state, (markFixed)->{
|
||||
if (markFixed) {
|
||||
profile.markApplied();
|
||||
}
|
||||
onResume.accept(applyFixes);
|
||||
});
|
||||
} else {
|
||||
onResume.accept(applyFixes);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
fixerThread.start();
|
||||
} else {
|
||||
runner.run();
|
||||
State state = runner.get();
|
||||
if (state.hasError()){
|
||||
LOGGER.error("There were Errors while fixing the Level:");
|
||||
LOGGER.error(state.getErrorMessage());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -240,6 +279,11 @@ public class DataFixerAPI {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
@Environment(EnvType.CLIENT)
|
||||
private static void showLevelFixErrorScreen(State state, Listener onContinue){
|
||||
Minecraft.getInstance()
|
||||
.setScreen(new LevelFixErrorScreen(Minecraft.getInstance().screen, state.getErrorMessages(), onContinue));
|
||||
}
|
||||
|
||||
private static MigrationProfile loadProfileIfNeeded(File levelBaseDir){
|
||||
if (!Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_PATCH_CATEGORY, "applyPatches", true)) {
|
||||
|
@ -270,7 +314,7 @@ public class DataFixerAPI {
|
|||
Minecraft.getInstance().setScreen(new ConfirmFixScreen((Screen) null, whenFinished::accept));
|
||||
}
|
||||
|
||||
private static void runDataFixes(File dir, MigrationProfile profile, AtomicProgressListener progress) {
|
||||
private static State runDataFixes(File dir, MigrationProfile profile, AtomicProgressListener progress) {
|
||||
State state = new State();
|
||||
progress.resetAtomic();
|
||||
|
||||
|
@ -295,6 +339,7 @@ public class DataFixerAPI {
|
|||
profile.patchWorldData();
|
||||
} catch (PatchDidiFailException e){
|
||||
state.didFail = true;
|
||||
state.addError("Failed fixing worldconfig (" + e.getMessage() + ")");
|
||||
BCLib.LOGGER.error(e.getMessage());
|
||||
}
|
||||
progress.incAtomic(maxProgress);
|
||||
|
@ -313,6 +358,8 @@ public class DataFixerAPI {
|
|||
progress.incAtomic(maxProgress);
|
||||
|
||||
progress.stop();
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
private static void fixLevel(MigrationProfile profile, State state, File levelBaseDir) {
|
||||
|
@ -342,15 +389,17 @@ public class DataFixerAPI {
|
|||
}
|
||||
catch (Exception e) {
|
||||
BCLib.LOGGER.error("Failed fixing Level-Data.");
|
||||
state.addError("Failed fixing Level-Data in level.dat (" + e.getMessage() + ")");
|
||||
state.didFail = true;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void fixPlayer(MigrationProfile data, State state, File file) {
|
||||
try {
|
||||
LOGGER.info("Inspecting " + file);
|
||||
CompoundTag player = NbtIo.readCompressed(file);
|
||||
|
||||
CompoundTag player = readNbt(file);
|
||||
boolean[] changed = { false };
|
||||
fixPlayerNbt(player, changed, data);
|
||||
|
||||
|
@ -361,11 +410,12 @@ public class DataFixerAPI {
|
|||
}
|
||||
catch (Exception e) {
|
||||
BCLib.LOGGER.error("Failed fixing Player-Data.");
|
||||
state.addError("Failed fixing Player-Data in " + file.getName() + " (" + e.getMessage() + ")");
|
||||
state.didFail = true;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void fixPlayerNbt(CompoundTag player, boolean[] changed, MigrationProfile data) {
|
||||
//Checking Inventory
|
||||
ListTag inventory = player.getList("Inventory", Tag.TAG_COMPOUND);
|
||||
|
@ -416,7 +466,7 @@ public class DataFixerAPI {
|
|||
for (int z = 0; z < 32; z++) {
|
||||
ChunkPos pos = new ChunkPos(x, z);
|
||||
changed[0] = false;
|
||||
if (region.hasChunk(pos)) {
|
||||
if (region.hasChunk(pos) && !state.didFail) {
|
||||
DataInputStream input = region.getChunkDataInputStream(pos);
|
||||
CompoundTag root = NbtIo.read(input);
|
||||
// if ((root.toString().contains("betternether:chest") || root.toString().contains("bclib:chest"))) {
|
||||
|
@ -442,6 +492,17 @@ public class DataFixerAPI {
|
|||
CompoundTag blockTagCompound = ((CompoundTag) blockTag);
|
||||
changed[0] |= data.replaceStringFromIDs(blockTagCompound, "Name");
|
||||
});
|
||||
|
||||
try {
|
||||
changed[0] |= data.patchBlockState(palette, ((CompoundTag) tag).getList("BlockStates", Tag.TAG_LONG));
|
||||
}
|
||||
catch (PatchDidiFailException e) {
|
||||
BCLib.LOGGER.error("Failed fixing BlockState in " + pos);
|
||||
state.addError("Failed fixing BlockState in " + pos + " (" + e.getMessage() + ")");
|
||||
state.didFail = true;
|
||||
changed[0] = false;
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
if (changed[0]) {
|
||||
|
@ -450,7 +511,6 @@ public class DataFixerAPI {
|
|||
DataOutputStream output = region.getChunkDataOutputStream(pos);
|
||||
NbtIo.write(root, output);
|
||||
output.close();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -458,12 +518,13 @@ public class DataFixerAPI {
|
|||
region.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
BCLib.LOGGER.error("Failed fixing Player Data.");
|
||||
BCLib.LOGGER.error("Failed fixing Region.");
|
||||
state.addError("Failed fixing Region in " + file.getName() + " (" + e.getMessage() + ")");
|
||||
state.didFail = true;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static CompoundTag patchConfTag = null;
|
||||
static CompoundTag getPatchData(){
|
||||
if (patchConfTag==null) {
|
||||
|
@ -540,4 +601,12 @@ public class DataFixerAPI {
|
|||
Patch.getALL().add(patch.get());
|
||||
}
|
||||
|
||||
private static CompoundTag readNbt(File file) throws IOException {
|
||||
try {
|
||||
return NbtIo.readCompressed(file);
|
||||
} catch (ZipException e){
|
||||
return NbtIo.read(file);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
47
src/main/java/ru/bclib/api/datafixer/ForcedLevelPatch.java
Normal file
47
src/main/java/ru/bclib/api/datafixer/ForcedLevelPatch.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
package ru.bclib.api.datafixer;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* A Patch for level.dat that is always executed no matter what Patchlevel is set in a world.
|
||||
*/
|
||||
public abstract class ForcedLevelPatch extends Patch {
|
||||
protected ForcedLevelPatch(@NotNull String modID, String version) {
|
||||
super(modID, version, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map<String, String> getIDReplacements() {
|
||||
return new HashMap<String, String>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final PatchFunction<CompoundTag, Boolean> getWorldDataPatcher() { return null; }
|
||||
|
||||
@Override
|
||||
public final PatchBiFunction<ListTag, ListTag, Boolean> getBlockStatePatcher() { return null; }
|
||||
|
||||
@Override
|
||||
public final List<String> getWorldDataIDPaths() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PatchFunction<CompoundTag, Boolean> getLevelDatPatcher() { return this::runLevelDatPatch; }
|
||||
|
||||
/**
|
||||
* Called with the contents of level.dat in {@code root}
|
||||
* @param root The contents of level.dat
|
||||
* @param profile The active migration profile
|
||||
* @return true, if the run did change the contents of root
|
||||
*/
|
||||
abstract protected Boolean runLevelDatPatch(CompoundTag root, MigrationProfile profile);
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import ru.bclib.util.ModUtil;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
@ -23,6 +24,7 @@ public class MigrationProfile {
|
|||
final Set<String> mods;
|
||||
final Map<String, String> idReplacements;
|
||||
final List<PatchFunction<CompoundTag, Boolean>> levelPatchers;
|
||||
final List<PatchBiFunction<ListTag, ListTag, Boolean>> statePatchers;
|
||||
final List<Patch> worldDataPatchers;
|
||||
final Map<String, List<String>> worldDataIDPaths;
|
||||
|
||||
|
@ -33,7 +35,7 @@ public class MigrationProfile {
|
|||
private boolean didRunPrePatch;
|
||||
private Exception prePatchException;
|
||||
|
||||
MigrationProfile(CompoundTag config) {
|
||||
MigrationProfile(CompoundTag config, boolean applyAll) {
|
||||
this.config = config;
|
||||
|
||||
this.mods = Collections.unmodifiableSet(Patch.getALL()
|
||||
|
@ -44,6 +46,7 @@ public class MigrationProfile {
|
|||
HashMap<String, String> replacements = new HashMap<String, String>();
|
||||
List<PatchFunction<CompoundTag, Boolean>> levelPatches = new LinkedList<>();
|
||||
List<Patch> worldDataPatches = new LinkedList<>();
|
||||
List<PatchBiFunction<ListTag, ListTag, Boolean>> statePatches = new LinkedList<>();
|
||||
HashMap<String, List<String>> worldDataIDPaths = new HashMap<>();
|
||||
for (String modID : mods) {
|
||||
|
||||
|
@ -54,12 +57,14 @@ public class MigrationProfile {
|
|||
List<String> paths = patch.getWorldDataIDPaths();
|
||||
if (paths!=null) worldDataIDPaths.put(modID, paths);
|
||||
|
||||
if (currentPatchLevel(modID) < patch.level) {
|
||||
if (applyAll || currentPatchLevel(modID) < patch.level || patch.alwaysApply) {
|
||||
replacements.putAll(patch.getIDReplacements());
|
||||
if (patch.getLevelDatPatcher()!=null)
|
||||
levelPatches.add(patch.getLevelDatPatcher());
|
||||
if (patch.getWorldDataPatcher()!=null)
|
||||
worldDataPatches.add(patch);
|
||||
if (patch.getBlockStatePatcher()!=null)
|
||||
statePatches.add(patch.getBlockStatePatcher());
|
||||
DataFixerAPI.LOGGER.info("Applying " + patch);
|
||||
}
|
||||
else {
|
||||
|
@ -72,6 +77,54 @@ public class MigrationProfile {
|
|||
this.idReplacements = Collections.unmodifiableMap(replacements);
|
||||
this.levelPatchers = Collections.unmodifiableList(levelPatches);
|
||||
this.worldDataPatchers = Collections.unmodifiableList(worldDataPatches);
|
||||
this.statePatchers = Collections.unmodifiableList(statePatches);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is supposed to be used by developers to apply id-patches to custom nbt structures. It is only
|
||||
* available in Developer-Mode
|
||||
*
|
||||
*/
|
||||
public static void fixCustomFolder(File dir){
|
||||
if (!BCLib.isDevEnvironment()) return;
|
||||
MigrationProfile profile = Patch.createMigrationData();
|
||||
List<File> nbts = getAllNbts(dir, null);
|
||||
nbts.parallelStream().forEach((file) -> {
|
||||
DataFixerAPI.LOGGER.info("Loading NBT " + file);
|
||||
try {
|
||||
CompoundTag root = NbtIo.readCompressed(file);
|
||||
boolean[] changed = {false};
|
||||
if (root.contains("palette")){
|
||||
ListTag items = root.getList("palette", Tag.TAG_COMPOUND);
|
||||
items.forEach(inTag -> {
|
||||
CompoundTag tag = (CompoundTag)inTag;
|
||||
changed[0] |= profile.replaceStringFromIDs(tag, "Name");
|
||||
});
|
||||
}
|
||||
|
||||
if (changed[0]){
|
||||
DataFixerAPI.LOGGER.info("Writing NBT " + file);
|
||||
NbtIo.writeCompressed(root, file);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static List<File> getAllNbts(File dir, List<File> list) {
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
}
|
||||
for (File file : dir.listFiles()) {
|
||||
if (file.isDirectory()) {
|
||||
getAllNbts(file, list);
|
||||
} else if (file.isFile() && file.getName().endsWith(".nbt")) {
|
||||
list.add(file);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
final public CompoundTag getLevelDat(File levelBaseDir){
|
||||
|
@ -124,12 +177,13 @@ public class MigrationProfile {
|
|||
final public void markApplied() {
|
||||
for (String modID : mods) {
|
||||
DataFixerAPI.LOGGER.info("Updating Patch-Level for '{}' from {} to {}", modID, ModUtil.convertModVersion(currentPatchLevel(modID)), ModUtil.convertModVersion(Patch.maxPatchLevel(modID)));
|
||||
config.putString(modID, Patch.maxPatchVersion(modID));
|
||||
if (config!=null)
|
||||
config.putString(modID, Patch.maxPatchVersion(modID));
|
||||
}
|
||||
}
|
||||
|
||||
public String currentPatchVersion(@NotNull String modID) {
|
||||
if (!config.contains(modID)) return "0.0.0";
|
||||
if (config==null || !config.contains(modID)) return "0.0.0";
|
||||
return config.getString(modID);
|
||||
}
|
||||
|
||||
|
@ -255,4 +309,12 @@ public class MigrationProfile {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean patchBlockState(ListTag palette, ListTag states) throws PatchDidiFailException{
|
||||
boolean changed = false;
|
||||
for (PatchBiFunction<ListTag, ListTag, Boolean> f : statePatchers) {
|
||||
changed |= f.apply(palette, states, this);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ru.bclib.api.datafixer;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import ru.bclib.util.ModUtil;
|
||||
|
||||
|
@ -30,6 +31,11 @@ public abstract class Patch {
|
|||
@NotNull
|
||||
public final String modID;
|
||||
|
||||
/**
|
||||
* This Mod is tested for each level start
|
||||
*/
|
||||
public final boolean alwaysApply;
|
||||
|
||||
static List<Patch> getALL() {
|
||||
return ALL;
|
||||
}
|
||||
|
@ -81,6 +87,19 @@ public abstract class Patch {
|
|||
* {@link Patch#maxPatchVersion(String)}
|
||||
*/
|
||||
protected Patch(@NotNull String modID, String version) {
|
||||
this(modID, version, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal Constructor used to create patches that can allways run (no matter what patchlevel a level has)
|
||||
* @param modID The ID of the Mod
|
||||
* @param version The mod-version that introduces the patch. When {@Code runAllways} is set, this version will
|
||||
* determine the patchlevel that is written to the level
|
||||
* @param alwaysApply When true, this patch is always active, no matter the patchlevel of the world.
|
||||
* This should be used sparingly and just for patches that apply to level.dat (as they only take
|
||||
* effect when changes are detected). Use {@link ForcedLevelPatch} to instatiate.
|
||||
*/
|
||||
Patch(@NotNull String modID, String version, boolean alwaysApply) {
|
||||
//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!");
|
||||
|
@ -91,6 +110,7 @@ public abstract class Patch {
|
|||
}
|
||||
|
||||
this.version = version;
|
||||
this.alwaysApply = alwaysApply;
|
||||
this.level = ModUtil.convertModVersion(version);
|
||||
if (!ALL.stream()
|
||||
.filter(p -> p.modID
|
||||
|
@ -145,22 +165,46 @@ public abstract class Patch {
|
|||
*/
|
||||
public PatchFunction<CompoundTag, Boolean> getWorldDataPatcher() { return null; }
|
||||
|
||||
/**
|
||||
* Return a {@link PatchBiFunction} that is called with pallette and blockstate of
|
||||
* each chunk in every region. This method is called AFTER all ID replacements
|
||||
* from {@link #getIDReplacements()} were applied to the pallete.
|
||||
*
|
||||
* The first parameter is the palette and the second is the blockstate.
|
||||
*
|
||||
* The function needs to return {@code true}, if changes were made to the data.
|
||||
* If an error occurs, the method should throw a {@link PatchDidiFailException}
|
||||
*
|
||||
* The default implementation of this method returns null.
|
||||
*
|
||||
* @return {@code true} if changes were applied and we need to save the data
|
||||
*/
|
||||
public PatchBiFunction<ListTag, ListTag, Boolean> getBlockStatePatcher() { return null; }
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param levelBaseDir The location of the level
|
||||
* @param config The current patch-level configuration*
|
||||
* @return a new {@link MigrationProfile} Object.
|
||||
*/
|
||||
static MigrationProfile createMigrationData(CompoundTag config) {
|
||||
return new MigrationProfile(config);
|
||||
return new MigrationProfile(config, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is supposed to be used by developers to apply id-patches to custom nbt structures. It is only
|
||||
* available in Developer-Mode
|
||||
*
|
||||
*/
|
||||
static MigrationProfile createMigrationData() {
|
||||
return new MigrationProfile(null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of paths,where your mod may IDs in your {@link ru.bclib.api.WorldDataAPI}-File.
|
||||
* Returns a list of paths where your mod stores IDs in your {@link ru.bclib.api.WorldDataAPI}-File.
|
||||
* <p>
|
||||
* {@link DataFixerAPI} will use information from the latest patch that returns a non-null-result. This list is used
|
||||
* to automatically fix changed IDs from all active patches (see {@link Patch#getIDReplacements()}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package ru.bclib.api.datafixer;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PatchBiFunction<U, V, R> {
|
||||
R apply(U t, V v, MigrationProfile profile) throws PatchDidiFailException;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue