Merge pull request #21 from quiqueck/main
Initialize new worlds as fully patched
This commit is contained in:
commit
b10e505ed3
4 changed files with 102 additions and 27 deletions
|
@ -6,6 +6,7 @@ import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.fabricmc.loader.api.ModContainer;
|
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 net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess;
|
||||||
import ru.bclib.BCLib;
|
import ru.bclib.BCLib;
|
||||||
import ru.bclib.api.datafixer.DataFixerAPI;
|
import ru.bclib.api.datafixer.DataFixerAPI;
|
||||||
|
|
||||||
|
@ -14,7 +15,15 @@ import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mod-specifix data-storage for a world.
|
||||||
|
*
|
||||||
|
* This class provides the ability for mod to store persistent data inside a world. The Storage for the world is
|
||||||
|
* currently initialized as part of the {@link DataFixerAPI} in {@link DataFixerAPI#fixData(LevelStorageAccess, boolean, Consumer)}
|
||||||
|
* or {@link DataFixerAPI#initializeWorldData(File, boolean)}
|
||||||
|
*/
|
||||||
public class WorldDataAPI {
|
public class WorldDataAPI {
|
||||||
private static final Map<String, CompoundTag> TAGS = Maps.newHashMap();
|
private static final Map<String, CompoundTag> TAGS = Maps.newHashMap();
|
||||||
private static final List<String> MODS = Lists.newArrayList();
|
private static final List<String> MODS = Lists.newArrayList();
|
||||||
|
@ -104,6 +113,9 @@ public class WorldDataAPI {
|
||||||
*/
|
*/
|
||||||
public static void saveFile(String modID) {
|
public static void saveFile(String modID) {
|
||||||
try {
|
try {
|
||||||
|
if (!dataDir.exists()){
|
||||||
|
dataDir.mkdirs();
|
||||||
|
}
|
||||||
NbtIo.writeCompressed(getRootTag(modID), new File(dataDir, modID + ".nbt"));
|
NbtIo.writeCompressed(getRootTag(modID), new File(dataDir, modID + ".nbt"));
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
|
|
@ -17,6 +17,8 @@ import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.chunk.storage.RegionFile;
|
import net.minecraft.world.level.chunk.storage.RegionFile;
|
||||||
import net.minecraft.world.level.storage.LevelResource;
|
import net.minecraft.world.level.storage.LevelResource;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
|
import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import ru.bclib.BCLib;
|
import ru.bclib.BCLib;
|
||||||
import ru.bclib.api.WorldDataAPI;
|
import ru.bclib.api.WorldDataAPI;
|
||||||
import ru.bclib.config.Configs;
|
import ru.bclib.config.Configs;
|
||||||
|
@ -31,6 +33,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -49,6 +52,29 @@ public class DataFixerAPI {
|
||||||
public void call();
|
public void call();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean wrapCall(LevelStorageSource levelSource, String levelID, Function<LevelStorageAccess, Boolean> runWithLevel) {
|
||||||
|
|
||||||
|
LevelStorageSource.LevelStorageAccess levelStorageAccess;
|
||||||
|
try {
|
||||||
|
levelStorageAccess = levelSource.createAccess(levelID);
|
||||||
|
} catch (IOException e) {
|
||||||
|
BCLib.LOGGER.warning((String)"Failed to read level {} data", levelID, e);
|
||||||
|
SystemToast.onWorldAccessFailure(Minecraft.getInstance(), levelID);
|
||||||
|
Minecraft.getInstance().setScreen((Screen)null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean returnValue = runWithLevel.apply(levelStorageAccess);
|
||||||
|
|
||||||
|
try {
|
||||||
|
levelStorageAccess.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
BCLib.LOGGER.warning((String)"Failed to unlock access to level {}", levelID, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will apply necessary Patches to the world.
|
* Will apply necessary Patches to the world.
|
||||||
*
|
*
|
||||||
|
@ -63,26 +89,7 @@ public class DataFixerAPI {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public static boolean fixData(LevelStorageSource levelSource, String levelID, boolean showUI, Consumer<Boolean> onResume) {
|
public static boolean fixData(LevelStorageSource levelSource, String levelID, boolean showUI, Consumer<Boolean> onResume) {
|
||||||
|
return wrapCall(levelSource, levelID, (levelStorageAccess) -> fixData(levelStorageAccess, showUI, onResume));
|
||||||
LevelStorageSource.LevelStorageAccess levelStorageAccess;
|
|
||||||
try {
|
|
||||||
levelStorageAccess = levelSource.createAccess(levelID);
|
|
||||||
} catch (IOException e) {
|
|
||||||
BCLib.LOGGER.warning((String)"Failed to read level {} data", levelID, e);
|
|
||||||
SystemToast.onWorldAccessFailure(Minecraft.getInstance(), levelID);
|
|
||||||
Minecraft.getInstance().setScreen((Screen)null);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean cancelRun = fixData(levelStorageAccess, showUI, onResume);
|
|
||||||
|
|
||||||
try {
|
|
||||||
levelStorageAccess.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
BCLib.LOGGER.warning((String)"Failed to unlock access to level {}", levelID, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cancelRun;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,12 +105,54 @@ public class DataFixerAPI {
|
||||||
*/
|
*/
|
||||||
public static boolean fixData(LevelStorageSource.LevelStorageAccess levelStorageAccess, boolean showUI, Consumer<Boolean> onResume){
|
public static boolean fixData(LevelStorageSource.LevelStorageAccess levelStorageAccess, boolean showUI, Consumer<Boolean> onResume){
|
||||||
File levelPath = levelStorageAccess.getLevelPath(LevelResource.ROOT).toFile();
|
File levelPath = levelStorageAccess.getLevelPath(LevelResource.ROOT).toFile();
|
||||||
WorldDataAPI.load(new File(levelPath, "data"));
|
|
||||||
|
boolean newWorld = false;
|
||||||
|
if (!levelPath.exists()) {
|
||||||
|
BCLib.LOGGER.info("Creating a new World, no fixes needed");
|
||||||
|
newWorld = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeWorldData(levelPath, newWorld);
|
||||||
|
if (newWorld) return false;
|
||||||
|
|
||||||
return fixData(levelPath, levelStorageAccess.getLevelId(), showUI, onResume);
|
return fixData(levelPath, levelStorageAccess.getLevelId(), showUI, onResume);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the
|
||||||
|
* current versions of the plugins.
|
||||||
|
* <p>
|
||||||
|
* This implementation will create a new {@link LevelStorageAccess} and call {@link #initializeWorldData(File, boolean)}
|
||||||
|
* using the provided root path.
|
||||||
|
* @param levelSource The SourceStorage for this Minecraft instance, You can get this using
|
||||||
|
* {@code Minecraft.getInstance().getLevelSource()}
|
||||||
|
* @param levelID The ID of the Level you want to patch
|
||||||
|
* @param newWorld {@code true} if this is a fresh world
|
||||||
|
*/
|
||||||
|
public static void initializeWorldData(LevelStorageSource levelSource, String levelID, boolean newWorld) {
|
||||||
|
wrapCall(levelSource, levelID, (levelStorageAccess) -> {
|
||||||
|
initializeWorldData(levelStorageAccess.getLevelPath(LevelResource.ROOT).toFile(), newWorld);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the
|
||||||
|
* current versions of the plugins.
|
||||||
|
* @param levelPath Folder of the world
|
||||||
|
* @param newWorld {@code true} if this is a fresh world
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static void initializeWorldData(File levelPath, boolean newWorld){
|
||||||
|
WorldDataAPI.load(new File(levelPath, "data"));
|
||||||
|
|
||||||
|
if (newWorld){
|
||||||
|
getMigrationProfile().markApplied();
|
||||||
|
WorldDataAPI.saveFile(BCLib.MOD_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean fixData(File dir, String levelID, boolean showUI, Consumer<Boolean> onResume) {
|
private static boolean fixData(File dir, String levelID, boolean showUI, Consumer<Boolean> onResume) {
|
||||||
MigrationProfile profile = loadProfile(dir, levelID);
|
MigrationProfile profile = loadProfileIfNeeded();
|
||||||
|
|
||||||
Consumer<Boolean> runFixes = (applyFixes) -> {
|
Consumer<Boolean> runFixes = (applyFixes) -> {
|
||||||
if (applyFixes) {
|
if (applyFixes) {
|
||||||
|
@ -153,16 +202,15 @@ public class DataFixerAPI {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static MigrationProfile loadProfile(File dir, String levelID){
|
private static MigrationProfile loadProfileIfNeeded(){
|
||||||
if (!Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_PATCH_CATEGORY, "applyPatches", true)) {
|
if (!Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_PATCH_CATEGORY, "applyPatches", true)) {
|
||||||
LOGGER.info("World Patches are disabled");
|
LOGGER.info("World Patches are disabled");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final CompoundTag patchConfig = WorldDataAPI.getCompoundTag(BCLib.MOD_ID, Configs.MAIN_PATCH_CATEGORY);
|
MigrationProfile profile = getMigrationProfile();
|
||||||
MigrationProfile profile = Patch.createMigrationData(patchConfig);
|
|
||||||
|
|
||||||
if (!profile.hasAnyFixes()) {
|
if (!profile.hasAnyFixes()) {
|
||||||
LOGGER.info("Everything up to date");
|
LOGGER.info("Everything up to date");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -170,6 +218,13 @@ public class DataFixerAPI {
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static MigrationProfile getMigrationProfile() {
|
||||||
|
final CompoundTag patchConfig = WorldDataAPI.getCompoundTag(BCLib.MOD_ID, Configs.MAIN_PATCH_CATEGORY);
|
||||||
|
MigrationProfile profile = Patch.createMigrationData(patchConfig);
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
static void showBackupWarning(String levelID, Consumer<Boolean> whenFinished){
|
static void showBackupWarning(String levelID, Consumer<Boolean> whenFinished){
|
||||||
TranslatableComponent promptText = new TranslatableComponent("bclib.datafixer.backupWarning.prompt");
|
TranslatableComponent promptText = new TranslatableComponent("bclib.datafixer.backupWarning.prompt");
|
||||||
|
|
|
@ -5,6 +5,9 @@ import net.minecraft.client.color.block.BlockColors;
|
||||||
import net.minecraft.client.color.item.ItemColors;
|
import net.minecraft.client.color.item.ItemColors;
|
||||||
import net.minecraft.client.main.GameConfig;
|
import net.minecraft.client.main.GameConfig;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.RegistryAccess.RegistryHolder;
|
||||||
|
import net.minecraft.world.level.LevelSettings;
|
||||||
|
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
@ -67,6 +70,11 @@ public abstract class MinecraftMixin {
|
||||||
return levelID;
|
return levelID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject(method="createLevel", at=@At("HEAD"))
|
||||||
|
private void bclib_initPatchData(String levelID, LevelSettings levelSettings, RegistryHolder registryHolder, WorldGenSettings worldGenSettings, CallbackInfo ci) {
|
||||||
|
DataFixerAPI.initializeWorldData(this.levelSource, levelID, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// @Inject(method="doLoadLevel", cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD, at=@At(value="INVOKE", target="Lnet/minecraft/client/Minecraft;makeServerStem(Lnet/minecraft/core/RegistryAccess$RegistryHolder;Ljava/util/function/Function;Lcom/mojang/datafixers/util/Function4;ZLnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;)Lnet/minecraft/client/Minecraft$ServerStem;"))
|
// @Inject(method="doLoadLevel", cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD, at=@At(value="INVOKE", target="Lnet/minecraft/client/Minecraft;makeServerStem(Lnet/minecraft/core/RegistryAccess$RegistryHolder;Ljava/util/function/Function;Lcom/mojang/datafixers/util/Function4;ZLnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;)Lnet/minecraft/client/Minecraft$ServerStem;"))
|
||||||
// private void bclib_onCallFixer(
|
// private void bclib_onCallFixer(
|
||||||
|
|
|
@ -11,7 +11,7 @@ import ru.bclib.api.datafixer.DataFixerAPI;
|
||||||
abstract public class MainMixin {
|
abstract public class MainMixin {
|
||||||
@ModifyArg(method="main", at=@At(value="INVOKE", target="Lnet/minecraft/server/MinecraftServer;convertFromRegionFormatIfNeeded(Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;)V"))
|
@ModifyArg(method="main", at=@At(value="INVOKE", target="Lnet/minecraft/server/MinecraftServer;convertFromRegionFormatIfNeeded(Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;)V"))
|
||||||
private static LevelStorageSource.LevelStorageAccess bclib_callServerFix(LevelStorageSource.LevelStorageAccess session){
|
private static LevelStorageSource.LevelStorageAccess bclib_callServerFix(LevelStorageSource.LevelStorageAccess session){
|
||||||
DataFixerAPI.fixData(session, false, (didFix)->{});
|
DataFixerAPI.fixData(session, false, (didFix)->{/* not called when showUI==false */});
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue