Merge remote-tracking branch 'origin/main'

This commit is contained in:
paulevsGitch 2022-02-14 03:08:12 +03:00
commit f756673128
16 changed files with 230 additions and 96 deletions

View file

@ -8,13 +8,13 @@ loom_version=0.10-SNAPSHOT
# check these on https://fabricmc.net/versions.html
minecraft_version= 1.18.1
loader_version= 0.12.12
fabric_version = 0.44.0+1.18
fabric_version = 0.46.2+1.18
# Mod Properties
mod_version = 1.3.0
mod_version = 1.3.1
maven_group = ru.bclib
archives_base_name = bclib
# Dependencies
patchouli_version = 50-FABRIC
modmenu_version=3.0.0
modmenu_version=3.0.1

View file

@ -17,6 +17,7 @@ import ru.bclib.recipes.CraftingRecipes;
import ru.bclib.registry.BaseBlockEntities;
import ru.bclib.registry.BaseRegistry;
import ru.bclib.util.Logger;
import ru.bclib.util.ModUtil;
import ru.bclib.world.generator.BCLibEndBiomeSource;
import ru.bclib.world.generator.BCLibNetherBiomeSource;
import ru.bclib.world.generator.GeneratorOptions;

View file

@ -2,7 +2,7 @@ package ru.bclib.api;
import com.google.common.collect.Lists;
import net.fabricmc.loader.api.FabricLoader;
import ru.bclib.integration.modmenu.ModIntegration;
import ru.bclib.integration.ModIntegration;
import java.util.List;

View file

@ -501,8 +501,11 @@ public class BiomeAPI {
.worldGenSettings()
.dimensions()
.stream()
.filter(dim->dim.generator().getSettings()==settings)
.map(dim->((NoiseGeneratorSettingsProvider)dim.generator()).bclib_getNoiseGeneratorSettings()).findFirst().orElse(null);;
.map(dim->dim.generator())
.filter(gen->(gen instanceof NoiseGeneratorSettingsProvider) && gen.getSettings()==settings)
.map(gen->((NoiseGeneratorSettingsProvider)gen).bclib_getNoiseGeneratorSettings())
.findFirst()
.orElse(null);
// Datapacks (like Amplified Nether)will change the GeneratorSettings upon load, so we will
// only use the default Setting for Nether/End if we were unable to find a settings object
@ -515,14 +518,11 @@ public class BiomeAPI {
}
List<BiConsumer<ResourceLocation, Biome>> modifications = MODIFICATIONS.get(level.dimension());
if (modifications == null) {
biomes.forEach(biome -> sortBiomeFeatures(biome));
} else {
biomes.forEach(biome -> {
applyModificationsToBiome(modifications, biome);
});
for (Biome biome : biomes) {
applyModificationsAndUpdateFeatures(modifications, biome);
}
if (generator != null) {
final SurfaceRuleProvider provider = SurfaceRuleProvider.class.cast(generator);
// Multiple Biomes can use the same generator. So we need to keep track of all Biomes that are
@ -548,11 +548,13 @@ public class BiomeAPI {
((BiomeSourceAccessor) source).bclRebuildFeatures();
}
private static void applyModificationsToBiome(List<BiConsumer<ResourceLocation, Biome>> modifications, Biome biome) {
private static void applyModificationsAndUpdateFeatures(List<BiConsumer<ResourceLocation, Biome>> modifications, Biome biome) {
ResourceLocation biomeID = getBiomeID(biome);
modifications.forEach(consumer -> {
consumer.accept(biomeID, biome);
});
if (modifications!=null) {
modifications.forEach(consumer -> {
consumer.accept(biomeID, biome);
});
}
final BCLBiome bclBiome = BiomeAPI.getBiome(biome);
if (bclBiome != null) {
@ -658,7 +660,7 @@ public class BiomeAPI {
/**
* For internal use only!
*
* Adds new features to existing biome. Called from {@link #applyModificationsToBiome(List, Biome)} when the Biome is
* Adds new features to existing biome. Called from {@link #applyModificationsAndUpdateFeatures(List, Biome)} when the Biome is
* present in any {@link BiomeSource}
* @param biome {@link Biome} to add features in.
* @param featureMap Map of {@link ConfiguredFeature} to add.

View file

@ -177,8 +177,6 @@ public class HelloClient extends DataHandler.FromServer {
protected void deserializeIncomingDataOnClient(FriendlyByteBuf buf, PacketSender responseSender) {
//read BCLibVersion (=protocol version)
bclibVersion = ModUtil.convertModVersion(buf.readInt());
final boolean protocolVersion_0_4_1 = ModUtil.isLargerOrEqualVersion(bclibVersion, "0.4.1");
//read Plugin Versions
modVersion = new ServerModMap();
@ -189,14 +187,8 @@ public class HelloClient extends DataHandler.FromServer {
final int size;
final boolean canDownload;
//since v0.4.1 we also send the size of the mod-File
if (protocolVersion_0_4_1) {
size = buf.readInt();
canDownload = buf.readBoolean();
}
else {
size = 0;
canDownload = true;
}
size = buf.readInt();
canDownload = buf.readBoolean();
modVersion.put(id, new OfferedModInfo(version, size, canDownload));
}
@ -213,15 +205,13 @@ public class HelloClient extends DataHandler.FromServer {
autoSynFolders = new ArrayList<>(1);
//since v0.4.1 we also send the sync folders
if (protocolVersion_0_4_1) {
final int folderCount = buf.readInt();
for (int i = 0; i < folderCount; i++) {
SyncFolderDescriptor desc = SyncFolderDescriptor.deserialize(buf);
autoSynFolders.add(desc);
}
serverPublishedModInfo = buf.readBoolean();
final int folderCount = buf.readInt();
for (int i = 0; i < folderCount; i++) {
SyncFolderDescriptor desc = SyncFolderDescriptor.deserialize(buf);
autoSynFolders.add(desc);
}
serverPublishedModInfo = buf.readBoolean();
}
@Environment(EnvType.CLIENT)
@ -333,11 +323,14 @@ public class HelloClient extends DataHandler.FromServer {
@Environment(EnvType.CLIENT)
private void processModFileSync(final List<AutoSyncID> filesToRequest, final Set<String> mismatchingMods) {
for (Entry<String, OfferedModInfo> e : modVersion.entrySet()) {
final String localVersion = ModUtil.getModVersion(e.getKey());
final String localVersion = ModUtil.convertModVersion(ModUtil.convertModVersion(ModUtil.getModVersion(e.getKey())));
final OfferedModInfo serverInfo = e.getValue();
final boolean requestMod = !serverInfo.version.equals(localVersion) && serverInfo.size > 0 && serverInfo.canDownload;
BCLib.LOGGER.info(" - " + e.getKey() + " (client=" + localVersion + ", server=" + serverInfo.version + ", size=" + PathUtil.humanReadableFileSize(serverInfo.size) + (requestMod ? ", requesting" : "") + (serverInfo.canDownload ? "" :", not offered")+ ")");
ModInfo nfo = ModUtil.getModInfo(e.getKey());
final boolean clientOnly = nfo!=null && nfo.metadata.getEnvironment()==ModEnvironment.CLIENT;
final boolean requestMod = !clientOnly && !serverInfo.version.equals(localVersion) && serverInfo.size > 0 && serverInfo.canDownload;
BCLib.LOGGER.info(" - " + e.getKey() + " (client=" + localVersion + ", server=" + serverInfo.version + ", size=" + PathUtil.humanReadableFileSize(serverInfo.size) + (requestMod ? ", requesting" : "") + (serverInfo.canDownload ? "" :", not offered") + (clientOnly?", client only":"")+ ")");
if (requestMod) {
filesToRequest.add(new AutoSyncID.ForModFileRequest(e.getKey(), serverInfo.version));
}

View file

@ -8,6 +8,7 @@ import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.LeavesBlock;
@ -71,21 +72,25 @@ public class BaseLeavesBlock extends LeavesBlock implements BlockModelProvider,
@Override
@SuppressWarnings("deprecation")
public List<ItemStack> getDrops(BlockState state, LootContext.Builder builder) {
return BaseLeavesBlock.getLeaveDrops(this, this.sapling, builder, 16, 16);
}
public static List<ItemStack> getLeaveDrops(ItemLike leaveBlock, Block sapling, LootContext.Builder builder, int fortuneRate, int dropRate) {
ItemStack tool = builder.getParameter(LootContextParams.TOOL);
if (tool != null) {
if (BaseShearsItem.isShear(tool) || EnchantmentHelper.getItemEnchantmentLevel(
Enchantments.SILK_TOUCH,
tool
) > 0) {
return Collections.singletonList(new ItemStack(this));
return Collections.singletonList(new ItemStack(leaveBlock));
}
int fortune = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool);
if (MHelper.RANDOM.nextInt(16) <= fortune) {
if (MHelper.RANDOM.nextInt(fortuneRate) <= fortune) {
return Lists.newArrayList(new ItemStack(sapling));
}
return Lists.newArrayList();
}
return MHelper.RANDOM.nextInt(16) == 0 ? Lists.newArrayList(new ItemStack(sapling)) : Lists.newArrayList();
return MHelper.RANDOM.nextInt(dropRate) == 0 ? Lists.newArrayList(new ItemStack(sapling)) : Lists.newArrayList();
}
@Override

View file

@ -76,6 +76,7 @@ public class ModListScreen extends BCLibScreen {
final int STATE_OK = 6;
final int STATE_SERVER_MISSING_CLIENT_MOD = 5;
final int STATE_MISSING_NOT_OFFERED = 4;
final int STATE_VERSION_CLIENT_ONLY = 7;
final int STATE_VERSION_NOT_OFFERED = 3;
final int STATE_VERSION = 2;
final int STATE_SERVER_MISSING = 1;
@ -114,7 +115,10 @@ public class ModListScreen extends BCLibScreen {
final String modVer = data.version();
final int size = data.size();
if (!modVer.equals(mod.getVersion())) {
state = data.canDownload()?STATE_VERSION:STATE_VERSION_NOT_OFFERED;
if (mod.metadata.getEnvironment() == ModEnvironment.CLIENT)
state = STATE_VERSION_CLIENT_ONLY;
else
state = data.canDownload()?STATE_VERSION:STATE_VERSION_NOT_OFFERED;
serverVersion = modVer;
serverSize = size;
}
@ -150,10 +154,12 @@ public class ModListScreen extends BCLibScreen {
int color = GridLayout.COLOR_RED;
final String typeText;
if (state==STATE_VERSION || state==STATE_VERSION_NOT_OFFERED) {
if (state==STATE_VERSION || state==STATE_VERSION_NOT_OFFERED || state==STATE_VERSION_CLIENT_ONLY) {
typeText = "[VERSION]";
if (state == STATE_VERSION_NOT_OFFERED) {
color = GridLayout.COLOR_YELLOW;
} else if (state == STATE_VERSION_CLIENT_ONLY) {
color = GridLayout.COLOR_DARK_GREEN;
}
} else if (state==STATE_MISSING || state==STATE_MISSING_NOT_OFFERED) {
typeText = "[MISSING]";

View file

@ -1,4 +1,4 @@
package ru.bclib.integration.modmenu;
package ru.bclib.integration;
import net.fabricmc.fabric.api.tag.TagFactory;
import net.fabricmc.loader.api.FabricLoader;

View file

@ -22,7 +22,7 @@ public abstract class BiomeSourceMixin implements BiomeSourceAccessor {
@Mutable @Shadow @Final private List<StepFeatureData> featuresPerStep;
public void bclRebuildFeatures(){
BCLib.LOGGER.info("Rebuilding features in BiomeSource " + this.getClass());
BCLib.LOGGER.info("Rebuilding features in BiomeSource " + this);
featuresPerStep = buildFeaturesPerStep(this.possibleBiomes().stream().toList(), true);
}
}

View file

@ -1,8 +1,11 @@
package ru.bclib.mixin.common;
import joptsimple.AbstractOptionSpec;
import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.NonOptionArgumentSpec;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpecBuilder;
import net.minecraft.server.Main;
import net.minecraft.server.dedicated.DedicatedServerSettings;
import net.minecraft.world.level.storage.LevelStorageSource;
@ -23,8 +26,25 @@ abstract public class MainMixin {
@Inject(method="main", at=@At(value="INVOKE", target="Lnet/minecraft/world/level/storage/LevelStorageSource;createDefault(Ljava/nio/file/Path;)Lnet/minecraft/world/level/storage/LevelStorageSource;"))
private static void bclib_callServerFix(String[] args, CallbackInfo ci){
OptionParser parser = new OptionParser();
ArgumentAcceptingOptionSpec<String> optionUniverse = parser.accepts("universe").withRequiredArg().defaultsTo(".", new String[0]);
ArgumentAcceptingOptionSpec<String> optionUniverse = parser.accepts("universe").withRequiredArg().defaultsTo(".", (String[])new String[0]);
ArgumentAcceptingOptionSpec<String> optionWorld = parser.accepts("world").withRequiredArg();
//this is only for compat reasons, we do not need to read thise options in our mixin, but it seems to cause
//errors if they are not defined
parser.accepts("nogui");
parser.accepts("initSettings", "Initializes 'server.properties' and 'eula.txt', then quits");
parser.accepts("demo");
parser.accepts("bonusChest");
parser.accepts("forceUpgrade");
parser.accepts("eraseCache");
parser.accepts("safeMode", "Loads level with vanilla datapack only");
parser.accepts("help").forHelp();
parser.accepts("singleplayer").withRequiredArg();
parser.accepts("port").withRequiredArg().ofType(Integer.class).defaultsTo(-1, (Integer[])new Integer[0]);
parser.accepts("serverId").withRequiredArg();
parser.accepts("jfrProfile");
parser.nonOptions();
OptionSet options = parser.parse(args);
Path settingPath = Paths.get("server.properties", new String[0]);

View file

@ -50,6 +50,11 @@ public class NoiseGeneratorSettingsMixin implements SurfaceRuleProvider {
}
private void bclib_setCustomRules(List<RuleSource> rules) {
if (rules.size()==0){
bclib_clearCustomRules();
return;
}
RuleSource org = getOriginalSurfaceRule();
if (org instanceof SurfaceRules.SequenceRuleSource sequenceRule){
List<RuleSource> currentSequence = sequenceRule.sequence();
@ -58,6 +63,7 @@ public class NoiseGeneratorSettingsMixin implements SurfaceRuleProvider {
} else {
rules.add(org);
}
setSurfaceRule(SurfaceRules.sequence(rules.toArray(new RuleSource[rules.size()])));
}

View file

@ -31,7 +31,8 @@ public class BlocksHelper {
public static final Direction[] HORIZONTAL = makeHorizontal();
public static final Direction[] DIRECTIONS = Direction.values();
private static final MutableBlockPos POS = new MutableBlockPos();
private static final ThreadLocal<MutableBlockPos> TL_POS = ThreadLocal.withInitial(() -> new MutableBlockPos());
protected static final BlockState AIR = Blocks.AIR.defaultBlockState();
protected static final BlockState WATER = Blocks.WATER.defaultBlockState();
@ -76,6 +77,7 @@ public class BlocksHelper {
}
public static int downRayRep(LevelAccessor world, BlockPos pos, int maxDist) {
final MutableBlockPos POS = TL_POS.get();
POS.set(pos);
for (int j = 1; j < maxDist && (world.getBlockState(POS)).getMaterial().isReplaceable(); j++) {
POS.setY(POS.getY() - 1);
@ -84,6 +86,7 @@ public class BlocksHelper {
}
public static int raycastSqr(LevelAccessor world, BlockPos pos, int dx, int dy, int dz, int maxDist) {
final MutableBlockPos POS = TL_POS.get();
POS.set(pos);
for (int j = 1; j < maxDist && (world.getBlockState(POS)).getMaterial().isReplaceable(); j++) {
POS.move(dx, dy, dz);

View file

@ -62,53 +62,31 @@ public class ModUtil {
mods = new HashMap<>();
org.apache.logging.log4j.Logger logger = LogManager.getFormatterLogger("BCLib|ModLoader");
PathUtil.fileWalker(PathUtil.MOD_FOLDER.toFile(), false, (file -> {
try {
URI uri = URI.create("jar:" + file.toUri());
FileSystem fs = FileSystems.getFileSystem(uri);
if (fs!=null) {
try {
Path modMetaFile = fs.getPath("fabric.mod.json");
if (modMetaFile != null) {
try (InputStream is = Files.newInputStream(modMetaFile)) {
//ModMetadata mc = ModMetadataParser.parseMetadata(is, uri.toString(), new LinkedList<String>());
ModMetadata mc = readJSON(is, uri.toString());
if (mc!=null){
mods.put(mc.getId(), new ModInfo(mc, file));
}
}
}
} catch (Exception e) {
BCLib.LOGGER.error(e.getMessage());
}
}
}
catch (Exception e) {
BCLib.LOGGER.error(e.getMessage());
}
}));
PathUtil.fileWalker(PathUtil.MOD_FOLDER.toFile(), false, (ModUtil::accept));
return mods;
}
private static ModMetadata readJSON(InputStream is, String sourceFile) throws IOException {
try (com.google.gson.stream.JsonReader reader = new JsonReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
JsonObject data = new JsonParser().parse(reader).getAsJsonObject();
JsonObject data = new JsonParser().parse(reader)
.getAsJsonObject();
Version ver;
try {
ver = new SemanticVersionImpl(data.get("version").getAsString(), false);
ver = new SemanticVersionImpl(data.get("version")
.getAsString(), false);
}
catch (VersionParsingException e) {
BCLib.LOGGER.error("Unable to parse Version in " + sourceFile);
return null;
}
if (data.get("id") == null){
if (data.get("id") == null) {
BCLib.LOGGER.error("Unable to read ID in " + sourceFile);
return null;
}
if (data.get("name") == null){
if (data.get("name") == null) {
BCLib.LOGGER.error("Unable to read name in " + sourceFile);
return null;
}
@ -118,6 +96,7 @@ public class ModUtil {
public Version getVersion() {
return ver;
}
@Override
public String getType() {
return "fabric";
@ -125,7 +104,8 @@ public class ModUtil {
@Override
public String getId() {
return data.get("id").getAsString();
return data.get("id")
.getAsString();
}
@Override
@ -136,35 +116,44 @@ public class ModUtil {
@Override
public ModEnvironment getEnvironment() {
JsonElement env = data.get("environment");
if (env==null) {
if (env == null) {
BCLib.LOGGER.warning("No environment specified in " + sourceFile);
//return ModEnvironment.UNIVERSAL;
}
final String environment = env==null?"":env.getAsString().toLowerCase(Locale.ROOT);
final String environment = env == null ? "" : env.getAsString()
.toLowerCase(Locale.ROOT);
if (environment.isEmpty() || environment.equals("*") || environment.equals("common")) {
if (environment.isEmpty() || environment.equals("*") || environment.equals("\"*\"") || environment.equals("common")) {
JsonElement entrypoints = data.get("entrypoints");
boolean hasClient = true;
//check if there is an actual client entrypoint
if (entrypoints!=null && entrypoints.isJsonObject()){
JsonElement client = entrypoints.getAsJsonObject().get("client");
if (client!=null && client.isJsonArray()){
hasClient = client.getAsJsonArray().size() > 0;
} else if (client==null || !client.isJsonPrimitive()){
if (entrypoints != null && entrypoints.isJsonObject()) {
JsonElement client = entrypoints.getAsJsonObject()
.get("client");
if (client != null && client.isJsonArray()) {
hasClient = client.getAsJsonArray()
.size() > 0;
}
else if (client == null || !client.isJsonPrimitive()) {
hasClient = false;
} else if (!client.getAsJsonPrimitive().isString()){
}
else if (!client.getAsJsonPrimitive()
.isString()) {
hasClient = false;
}
}
if (hasClient == false) return ModEnvironment.SERVER;
//if (hasClient == false) return ModEnvironment.SERVER;
return ModEnvironment.UNIVERSAL;
} else if (environment.equals("client")) {
}
else if (environment.equals("client")) {
return ModEnvironment.CLIENT;
} else if (environment.equals("server")) {
}
else if (environment.equals("server")) {
return ModEnvironment.SERVER;
} else {
}
else {
BCLib.LOGGER.error("Unable to read environment in " + sourceFile);
return ModEnvironment.UNIVERSAL;
}
@ -201,7 +190,8 @@ public class ModUtil {
@Override
public String getName() {
return data.get("name").getAsString();
return data.get("name")
.getAsString();
}
@Override
@ -325,13 +315,16 @@ public class ModUtil {
}
try {
int res = 0;
final String semanticVersionPattern = "(\\d+)\\.(\\d+)\\.(\\d+)\\D*";
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;
if (matcher.groupCount() > 0)
res = matcher.group(1) == null ? 0 : ((Integer.parseInt(matcher.group(1)) & 0xFF) << 22);
if (matcher.groupCount() > 1)
res |= matcher.group(2) == null ? 0 : ((Integer.parseInt(matcher.group(2)) & 0xFF) << 14);
if (matcher.groupCount() > 3)
res |= matcher.group(4) == null ? 0 : Integer.parseInt(matcher.group(4)) & 0x3FFF;
}
return res;
@ -376,6 +369,44 @@ public class ModUtil {
return convertModVersion(v1) >= convertModVersion(v2);
}
private static void accept(Path file) {
try {
URI uri = URI.create("jar:" + file.toUri());
FileSystem fs;
// boolean doClose = false;
try {
fs = FileSystems.getFileSystem(uri);
}
catch (Exception e) {
// doClose = true;
fs = FileSystems.newFileSystem(file);
}
if (fs != null) {
try {
Path modMetaFile = fs.getPath("fabric.mod.json");
if (modMetaFile != null) {
try (InputStream is = Files.newInputStream(modMetaFile)) {
//ModMetadata mc = ModMetadataParser.parseMetadata(is, uri.toString(), new LinkedList<String>());
ModMetadata mc = readJSON(is, uri.toString());
if (mc != null) {
mods.put(mc.getId(), new ModInfo(mc, file));
}
}
}
}
catch (Exception e) {
BCLib.LOGGER.error("Error for " + uri + ": " + e.toString());
}
//if (doClose) fs.close();
}
}
catch (Exception e) {
BCLib.LOGGER.error("Error for " + file.toUri() + ": " + e.toString());
e.printStackTrace();
}
}
public static class ModInfo {
public final ModMetadata metadata;
public final Path jarPath;
@ -416,7 +447,9 @@ public class ModUtil {
}
public String getVersion() {
if (metadata == null) return "0.0.0";
if (metadata == null) {
return "0.0.0";
}
return versionToString(metadata.getVersion());
}
}

View file

@ -191,4 +191,9 @@ public class BCLibEndBiomeSource extends BCLBiomeSource {
public static void register() {
Registry.register(Registry.BIOME_SOURCE, BCLib.makeID("end_biome_source"), CODEC);
}
@Override
public String toString() {
return "BCLib - The End BiomeSource";
}
}

View file

@ -153,4 +153,9 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource {
this.biomeMap = mapConstructor.apply(seed, GeneratorOptions.getBiomeSizeNether(), BiomeAPI.NETHER_BIOME_PICKER);
}
}
@Override
public String toString() {
return "BCLib - Nether BiomeSource";
}
}

View file

@ -0,0 +1,55 @@
{
"message.bclib.anvil_damage": "§c损坏",
"bclib.datafixer.backupWarning.title": "发现了一个不兼容的世界",
"bclib.datafixer.backupWarning.message": "自从上次加载这个世界以来一些安装的MOD的内部结构确实发生了变化。\n\n我们可以自动为您更改世界。如果继续而不应用更改则世界可能无法正确加载在着之前您应该创建一个备份.",
"bclib.datafixer.backupWarning.backup": "创建备份",
"bclib.datafixer.backupWarning.nofixes": "忽略错误",
"bclib.datafixer.backupWarning.fix": "应用修复",
"title.bclib.bclibmissmatch": "版本不匹配",
"message.bclib.bclibmissmatch": "服务器上的BCLib版本与此客户端不匹配。这将导致非常严重的问题。\n\n是否要从服务器自动下载BCLib版本\n\nBCLib将在安装新版本之前将旧版本移动到Mods文件夹的子目录中.",
"title.bclib.syncfiles": "不匹配数据",
"message.bclib.syncfiles": "服务器上的某些内容与客户端上的版本不匹配。\n是否要用服务器上的数据替换所选内容?",
"message.bclib.syncfiles.mods": "同步Mods",
"message.bclib.syncfiles.configs": "同步配置",
"message.bclib.syncfiles.folders": "同步文件夹和文件",
"message.bclib.syncfiles.delete": "删除不需要的文件",
"title.bclib.confirmrestart": "需要重新启动",
"message.bclib.confirmrestart": "请求的内容已同步。你现在需要重新启动Minecraft.",
"title.link.bclib.discord": "Discord",
"title.bclib.modmenu.main": "BCLib设置",
"title.bclib.progress": "改进",
"title.bclib.filesync.progress": "文件传输",
"message.bclib.filesync.progress": "正在将文件内容与服务器同步",
"message.bclib.filesync.progress.stage.empty": "",
"title.config.bclib.client.auto_sync.enabled": "启用自动同步",
"title.config.bclib.client.auto_sync.acceptConfigs": "接受下载的配置文件",
"title.config.bclib.client.auto_sync.acceptFiles": "接受下载的文件",
"title.config.bclib.client.auto_sync.acceptMods": "接受下载的MOD",
"title.config.bclib.client.auto_sync.displayModInfo": "当服务器端MOD与客户端不同时显示警告",
"title.config.bclib.client.auto_sync.debugHashes": "将自动同步调试哈希打印到日志",
"title.config.bclib.generator.options.useOldBiomeGenerator": "使用旧版1.17生物群落生成器",
"title.config.bclib.main.patches.applyPatches": "加载时自动应用修复补丁",
"title.config.bclib.main.patches.repairBiomesOnLoad": "将生物群系固定在水平荷载上",
"title.config.bclib.client.ui.suppressExperimentalDialogOnLoad": "禁用加载时的实验警告",
"title.bclib.syncfiles.modInfo": "模组信息",
"title.bclib.syncfiles.modlist": "模组列表",
"message.bclib.syncfiles.modlist": "以下显示已安装的MOD的状态 \n\n将同步本地不存在或服务器上版本不同的所有MOD",
"title.bclib.modmissmatch": "Mod版本冲突",
"message.bclib.modmissmatch": "此客户端上的某些MOD与服务器上的MOD版本不匹配。\n可能导致奇怪的游戏行为或崩溃。请确保您使用与服务器相同的mod版本",
"message.bclib.datafixer.progress.waitbackup": "正在等待备份完成。这可能需要一段时间!",
"message.bclib.datafixer.progress.reading": "读取数据",
"message.bclib.datafixer.progress.players": "固定玩家",
"message.bclib.datafixer.progress.level": "将修复补丁应用于level.dat",
"message.bclib.datafixer.progress.worlddata": "修补自定义世界数据",
"message.bclib.datafixer.progress.regions": "修复所有区域",
"message.bclib.datafixer.progress.saving": "保存补丁状态",
"title.bclib.datafixer.progress": "修复世界",
"message.bclib.datafixer.progress": "将所有修复补丁应用于您的世界",
"title.bclib.datafixer.error": "修复世界时的错误",
"message.bclib.datafixer.error": "修复世界时出现了错误,请还原备份并修复以下错误,然后重试",
"title.bclib.datafixer.error.continue": "继续并标记为已修复"
}