241 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			241 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.server.dedicated;
 | |
| 
 | |
| import com.google.common.base.Splitter;
 | |
| import com.google.common.base.Strings;
 | |
| import com.google.gson.JsonElement;
 | |
| import com.google.gson.JsonObject;
 | |
| import com.mojang.logging.LogUtils;
 | |
| import com.mojang.serialization.Dynamic;
 | |
| import com.mojang.serialization.JsonOps;
 | |
| import java.nio.charset.StandardCharsets;
 | |
| import java.nio.file.Path;
 | |
| import java.util.List;
 | |
| import java.util.Locale;
 | |
| import java.util.Map;
 | |
| import java.util.Optional;
 | |
| import java.util.Properties;
 | |
| import java.util.UUID;
 | |
| import java.util.concurrent.TimeUnit;
 | |
| import java.util.regex.Pattern;
 | |
| import net.minecraft.core.Holder;
 | |
| import net.minecraft.core.HolderLookup;
 | |
| import net.minecraft.core.RegistryAccess;
 | |
| import net.minecraft.core.registries.Registries;
 | |
| import net.minecraft.network.chat.Component;
 | |
| import net.minecraft.network.chat.ComponentSerialization;
 | |
| import net.minecraft.resources.RegistryOps;
 | |
| import net.minecraft.resources.ResourceKey;
 | |
| import net.minecraft.resources.ResourceLocation;
 | |
| import net.minecraft.server.MinecraftServer;
 | |
| import net.minecraft.util.GsonHelper;
 | |
| import net.minecraft.util.Mth;
 | |
| import net.minecraft.util.StrictJsonParser;
 | |
| import net.minecraft.world.Difficulty;
 | |
| import net.minecraft.world.level.DataPackConfig;
 | |
| import net.minecraft.world.level.GameType;
 | |
| import net.minecraft.world.level.WorldDataConfiguration;
 | |
| import net.minecraft.world.level.levelgen.FlatLevelSource;
 | |
| import net.minecraft.world.level.levelgen.WorldDimensions;
 | |
| import net.minecraft.world.level.levelgen.WorldOptions;
 | |
| import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
 | |
| import net.minecraft.world.level.levelgen.presets.WorldPreset;
 | |
| import net.minecraft.world.level.levelgen.presets.WorldPresets;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| import org.slf4j.Logger;
 | |
| 
 | |
| public class DedicatedServerProperties extends Settings<DedicatedServerProperties> {
 | |
| 	static final Logger LOGGER = LogUtils.getLogger();
 | |
| 	private static final Pattern SHA1 = Pattern.compile("^[a-fA-F0-9]{40}$");
 | |
| 	private static final Splitter COMMA_SPLITTER = Splitter.on(',').trimResults();
 | |
| 	public final boolean onlineMode = this.get("online-mode", true);
 | |
| 	public final boolean preventProxyConnections = this.get("prevent-proxy-connections", false);
 | |
| 	public final String serverIp = this.get("server-ip", "");
 | |
| 	public final boolean pvp = this.get("pvp", true);
 | |
| 	public final boolean allowFlight = this.get("allow-flight", false);
 | |
| 	public final String motd = this.get("motd", "A Minecraft Server");
 | |
| 	public final String bugReportLink = this.get("bug-report-link", "");
 | |
| 	public final boolean forceGameMode = this.get("force-gamemode", false);
 | |
| 	public final boolean enforceWhitelist = this.get("enforce-whitelist", false);
 | |
| 	public final Difficulty difficulty = this.get("difficulty", dispatchNumberOrString(Difficulty::byId, Difficulty::byName), Difficulty::getKey, Difficulty.EASY);
 | |
| 	public final GameType gamemode = this.get("gamemode", dispatchNumberOrString(GameType::byId, GameType::byName), GameType::getName, GameType.SURVIVAL);
 | |
| 	public final String levelName = this.get("level-name", "world");
 | |
| 	public final int serverPort = this.get("server-port", 25565);
 | |
| 	@Nullable
 | |
| 	public final Boolean announcePlayerAchievements = this.getLegacyBoolean("announce-player-achievements");
 | |
| 	public final boolean enableQuery = this.get("enable-query", false);
 | |
| 	public final int queryPort = this.get("query.port", 25565);
 | |
| 	public final boolean enableRcon = this.get("enable-rcon", false);
 | |
| 	public final int rconPort = this.get("rcon.port", 25575);
 | |
| 	public final String rconPassword = this.get("rcon.password", "");
 | |
| 	public final boolean hardcore = this.get("hardcore", false);
 | |
| 	public final boolean allowNether = this.get("allow-nether", true);
 | |
| 	public final boolean spawnMonsters = this.get("spawn-monsters", true);
 | |
| 	public final boolean useNativeTransport = this.get("use-native-transport", true);
 | |
| 	public final boolean enableCommandBlock = this.get("enable-command-block", false);
 | |
| 	public final int spawnProtection = this.get("spawn-protection", 16);
 | |
| 	public final int opPermissionLevel = this.get("op-permission-level", 4);
 | |
| 	public final int functionPermissionLevel = this.get("function-permission-level", 2);
 | |
| 	public final long maxTickTime = this.get("max-tick-time", TimeUnit.MINUTES.toMillis(1L));
 | |
| 	public final int maxChainedNeighborUpdates = this.get("max-chained-neighbor-updates", 1000000);
 | |
| 	public final int rateLimitPacketsPerSecond = this.get("rate-limit", 0);
 | |
| 	public final int viewDistance = this.get("view-distance", 10);
 | |
| 	public final int simulationDistance = this.get("simulation-distance", 10);
 | |
| 	public final int maxPlayers = this.get("max-players", 20);
 | |
| 	public final int networkCompressionThreshold = this.get("network-compression-threshold", 256);
 | |
| 	public final boolean broadcastRconToOps = this.get("broadcast-rcon-to-ops", true);
 | |
| 	public final boolean broadcastConsoleToOps = this.get("broadcast-console-to-ops", true);
 | |
| 	public final int maxWorldSize = this.get("max-world-size", integer -> Mth.clamp(integer, 1, 29999984), 29999984);
 | |
| 	public final boolean syncChunkWrites = this.get("sync-chunk-writes", true);
 | |
| 	public final String regionFileComression = this.get("region-file-compression", "deflate");
 | |
| 	public final boolean enableJmxMonitoring = this.get("enable-jmx-monitoring", false);
 | |
| 	public final boolean enableStatus = this.get("enable-status", true);
 | |
| 	public final boolean hideOnlinePlayers = this.get("hide-online-players", false);
 | |
| 	public final int entityBroadcastRangePercentage = this.get("entity-broadcast-range-percentage", integer -> Mth.clamp(integer, 10, 1000), 100);
 | |
| 	public final String textFilteringConfig = this.get("text-filtering-config", "");
 | |
| 	public final int textFilteringVersion = this.get("text-filtering-version", 0);
 | |
| 	public final Optional<MinecraftServer.ServerResourcePackInfo> serverResourcePackInfo;
 | |
| 	public final DataPackConfig initialDataPackConfiguration;
 | |
| 	public final Settings<DedicatedServerProperties>.MutableValue<Integer> playerIdleTimeout = this.getMutable("player-idle-timeout", 0);
 | |
| 	public final Settings<DedicatedServerProperties>.MutableValue<Boolean> whiteList = this.getMutable("white-list", false);
 | |
| 	public final boolean enforceSecureProfile = this.get("enforce-secure-profile", true);
 | |
| 	public final boolean logIPs = this.get("log-ips", true);
 | |
| 	public final int pauseWhenEmptySeconds = this.get("pause-when-empty-seconds", 60);
 | |
| 	private final DedicatedServerProperties.WorldDimensionData worldDimensionData;
 | |
| 	public final WorldOptions worldOptions;
 | |
| 	public boolean acceptsTransfers = this.get("accepts-transfers", false);
 | |
| 
 | |
| 	public DedicatedServerProperties(Properties properties) {
 | |
| 		super(properties);
 | |
| 		String string = this.get("level-seed", "");
 | |
| 		boolean bl = this.get("generate-structures", true);
 | |
| 		long l = WorldOptions.parseSeed(string).orElse(WorldOptions.randomSeed());
 | |
| 		this.worldOptions = new WorldOptions(l, bl, false);
 | |
| 		this.worldDimensionData = new DedicatedServerProperties.WorldDimensionData(
 | |
| 			this.get("generator-settings", stringx -> GsonHelper.parse(!stringx.isEmpty() ? stringx : "{}"), new JsonObject()),
 | |
| 			this.get("level-type", stringx -> stringx.toLowerCase(Locale.ROOT), WorldPresets.NORMAL.location().toString())
 | |
| 		);
 | |
| 		this.serverResourcePackInfo = getServerPackInfo(
 | |
| 			this.get("resource-pack-id", ""),
 | |
| 			this.get("resource-pack", ""),
 | |
| 			this.get("resource-pack-sha1", ""),
 | |
| 			this.getLegacyString("resource-pack-hash"),
 | |
| 			this.get("require-resource-pack", false),
 | |
| 			this.get("resource-pack-prompt", "")
 | |
| 		);
 | |
| 		this.initialDataPackConfiguration = getDatapackConfig(
 | |
| 			this.get("initial-enabled-packs", String.join(",", WorldDataConfiguration.DEFAULT.dataPacks().getEnabled())),
 | |
| 			this.get("initial-disabled-packs", String.join(",", WorldDataConfiguration.DEFAULT.dataPacks().getDisabled()))
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	public static DedicatedServerProperties fromFile(Path path) {
 | |
| 		return new DedicatedServerProperties(loadFromFile(path));
 | |
| 	}
 | |
| 
 | |
| 	protected DedicatedServerProperties reload(RegistryAccess registryAccess, Properties properties) {
 | |
| 		return new DedicatedServerProperties(properties);
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	private static Component parseResourcePackPrompt(String json) {
 | |
| 		if (!Strings.isNullOrEmpty(json)) {
 | |
| 			try {
 | |
| 				JsonElement jsonElement = StrictJsonParser.parse(json);
 | |
| 				return (Component)ComponentSerialization.CODEC
 | |
| 					.parse(RegistryAccess.EMPTY.createSerializationContext(JsonOps.INSTANCE), jsonElement)
 | |
| 					.resultOrPartial(string2 -> LOGGER.warn("Failed to parse resource pack prompt '{}': {}", json, string2))
 | |
| 					.orElse(null);
 | |
| 			} catch (Exception var2) {
 | |
| 				LOGGER.warn("Failed to parse resource pack prompt '{}'", json, var2);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return null;
 | |
| 	}
 | |
| 
 | |
| 	private static Optional<MinecraftServer.ServerResourcePackInfo> getServerPackInfo(
 | |
| 		String id, String url, String sha1, @Nullable String hash, boolean isRequired, String promptJson
 | |
| 	) {
 | |
| 		if (url.isEmpty()) {
 | |
| 			return Optional.empty();
 | |
| 		} else {
 | |
| 			String string;
 | |
| 			if (!sha1.isEmpty()) {
 | |
| 				string = sha1;
 | |
| 				if (!Strings.isNullOrEmpty(hash)) {
 | |
| 					LOGGER.warn("resource-pack-hash is deprecated and found along side resource-pack-sha1. resource-pack-hash will be ignored.");
 | |
| 				}
 | |
| 			} else if (!Strings.isNullOrEmpty(hash)) {
 | |
| 				LOGGER.warn("resource-pack-hash is deprecated. Please use resource-pack-sha1 instead.");
 | |
| 				string = hash;
 | |
| 			} else {
 | |
| 				string = "";
 | |
| 			}
 | |
| 
 | |
| 			if (string.isEmpty()) {
 | |
| 				LOGGER.warn("You specified a resource pack without providing a sha1 hash. Pack will be updated on the client only if you change the name of the pack.");
 | |
| 			} else if (!SHA1.matcher(string).matches()) {
 | |
| 				LOGGER.warn("Invalid sha1 for resource-pack-sha1");
 | |
| 			}
 | |
| 
 | |
| 			Component component = parseResourcePackPrompt(promptJson);
 | |
| 			UUID uUID;
 | |
| 			if (id.isEmpty()) {
 | |
| 				uUID = UUID.nameUUIDFromBytes(url.getBytes(StandardCharsets.UTF_8));
 | |
| 				LOGGER.warn("resource-pack-id missing, using default of {}", uUID);
 | |
| 			} else {
 | |
| 				try {
 | |
| 					uUID = UUID.fromString(id);
 | |
| 				} catch (IllegalArgumentException var10) {
 | |
| 					LOGGER.warn("Failed to parse '{}' into UUID", id);
 | |
| 					return Optional.empty();
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return Optional.of(new MinecraftServer.ServerResourcePackInfo(uUID, url, string, isRequired, component));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private static DataPackConfig getDatapackConfig(String initialEnabledPacks, String initialDisabledPacks) {
 | |
| 		List<String> list = COMMA_SPLITTER.splitToList(initialEnabledPacks);
 | |
| 		List<String> list2 = COMMA_SPLITTER.splitToList(initialDisabledPacks);
 | |
| 		return new DataPackConfig(list, list2);
 | |
| 	}
 | |
| 
 | |
| 	public WorldDimensions createDimensions(HolderLookup.Provider registries) {
 | |
| 		return this.worldDimensionData.create(registries);
 | |
| 	}
 | |
| 
 | |
| 	record WorldDimensionData(JsonObject generatorSettings, String levelType) {
 | |
| 		private static final Map<String, ResourceKey<WorldPreset>> LEGACY_PRESET_NAMES = Map.of(
 | |
| 			"default", WorldPresets.NORMAL, "largebiomes", WorldPresets.LARGE_BIOMES
 | |
| 		);
 | |
| 
 | |
| 		public WorldDimensions create(HolderLookup.Provider registries) {
 | |
| 			HolderLookup<WorldPreset> holderLookup = registries.lookupOrThrow(Registries.WORLD_PRESET);
 | |
| 			Holder.Reference<WorldPreset> reference = (Holder.Reference<WorldPreset>)holderLookup.get(WorldPresets.NORMAL)
 | |
| 				.or(() -> holderLookup.listElements().findAny())
 | |
| 				.orElseThrow(() -> new IllegalStateException("Invalid datapack contents: can't find default preset"));
 | |
| 			Holder<WorldPreset> holder = (Holder<WorldPreset>)Optional.ofNullable(ResourceLocation.tryParse(this.levelType))
 | |
| 				.map(resourceLocation -> ResourceKey.create(Registries.WORLD_PRESET, resourceLocation))
 | |
| 				.or(() -> Optional.ofNullable((ResourceKey)LEGACY_PRESET_NAMES.get(this.levelType)))
 | |
| 				.flatMap(holderLookup::get)
 | |
| 				.orElseGet(() -> {
 | |
| 					DedicatedServerProperties.LOGGER.warn("Failed to parse level-type {}, defaulting to {}", this.levelType, reference.key().location());
 | |
| 					return reference;
 | |
| 				});
 | |
| 			WorldDimensions worldDimensions = holder.value().createWorldDimensions();
 | |
| 			if (holder.is(WorldPresets.FLAT)) {
 | |
| 				RegistryOps<JsonElement> registryOps = registries.createSerializationContext(JsonOps.INSTANCE);
 | |
| 				Optional<FlatLevelGeneratorSettings> optional = FlatLevelGeneratorSettings.CODEC
 | |
| 					.parse(new Dynamic<>(registryOps, this.generatorSettings()))
 | |
| 					.resultOrPartial(DedicatedServerProperties.LOGGER::error);
 | |
| 				if (optional.isPresent()) {
 | |
| 					return worldDimensions.replaceOverworldGenerator(registries, new FlatLevelSource((FlatLevelGeneratorSettings)optional.get()));
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return worldDimensions;
 | |
| 		}
 | |
| 	}
 | |
| }
 |