/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.google.common.collect.ImmutableMap * com.google.common.collect.ImmutableMap$Builder * com.google.common.collect.Lists * com.google.common.net.HostAndPort * com.mojang.datafixers.DataFixer * com.mojang.logging.LogUtils * io.netty.handler.ssl.SslContext * org.jspecify.annotations.Nullable * org.slf4j.Logger */ package net.minecraft.server.dedicated; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.net.HostAndPort; import com.mojang.datafixers.DataFixer; import com.mojang.logging.LogUtils; import io.netty.handler.ssl.SslContext; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetAddress; import java.net.Proxy; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.OpenOption; import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.function.BooleanSupplier; import java.util.stream.Stream; import net.minecraft.DefaultUncaughtExceptionHandler; import net.minecraft.DefaultUncaughtExceptionHandlerWithName; import net.minecraft.SharedConstants; import net.minecraft.SystemReport; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.BlockPos; import net.minecraft.server.ConsoleInput; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerInterface; import net.minecraft.server.ServerLinks; import net.minecraft.server.Services; import net.minecraft.server.WorldStem; import net.minecraft.server.dedicated.DedicatedPlayerList; import net.minecraft.server.dedicated.DedicatedServerProperties; import net.minecraft.server.dedicated.DedicatedServerSettings; import net.minecraft.server.dedicated.ServerWatchdog; import net.minecraft.server.gui.MinecraftServerGui; import net.minecraft.server.jsonrpc.JsonRpcNotificationService; import net.minecraft.server.jsonrpc.ManagementServer; import net.minecraft.server.jsonrpc.internalapi.MinecraftApi; import net.minecraft.server.jsonrpc.security.AuthenticationHandler; import net.minecraft.server.jsonrpc.security.JsonRpcSslContextProvider; import net.minecraft.server.jsonrpc.security.SecurityConfig; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.progress.LoggingLevelLoadListener; import net.minecraft.server.network.ServerTextFilter; import net.minecraft.server.network.TextFilter; import net.minecraft.server.packs.repository.PackRepository; import net.minecraft.server.permissions.LevelBasedPermissionSet; import net.minecraft.server.permissions.PermissionSet; import net.minecraft.server.players.NameAndId; import net.minecraft.server.players.OldUsersConverter; import net.minecraft.server.rcon.RconConsoleSource; import net.minecraft.server.rcon.thread.QueryThreadGs4; import net.minecraft.server.rcon.thread.RconThread; import net.minecraft.util.Mth; import net.minecraft.util.StringUtil; import net.minecraft.util.TimeUtil; import net.minecraft.util.Util; import net.minecraft.util.debug.DebugSubscriptions; import net.minecraft.util.debugchart.RemoteDebugSampleType; import net.minecraft.util.debugchart.RemoteSampleLogger; import net.minecraft.util.debugchart.SampleLogger; import net.minecraft.util.debugchart.TpsDebugDimensions; import net.minecraft.util.monitoring.jmx.MinecraftServerStatistics; import net.minecraft.world.Difficulty; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.GameType; import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.storage.LevelData; import net.minecraft.world.level.storage.LevelStorageSource; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; public class DedicatedServer extends MinecraftServer implements ServerInterface { private static final Logger LOGGER = LogUtils.getLogger(); private static final int CONVERSION_RETRY_DELAY_MS = 5000; private static final int CONVERSION_RETRIES = 2; private final List consoleInput = Collections.synchronizedList(Lists.newArrayList()); private @Nullable QueryThreadGs4 queryThreadGs4; private final RconConsoleSource rconConsoleSource; private @Nullable RconThread rconThread; private final DedicatedServerSettings settings; private @Nullable MinecraftServerGui gui; private final @Nullable ServerTextFilter serverTextFilter; private @Nullable RemoteSampleLogger tickTimeLogger; private boolean isTickTimeLoggingEnabled; private final ServerLinks serverLinks; private final Map codeOfConductTexts; private @Nullable ManagementServer jsonRpcServer; private long lastHeartbeat; public DedicatedServer(Thread serverThread, LevelStorageSource.LevelStorageAccess levelStorageSource, PackRepository packRepository, WorldStem worldStem, DedicatedServerSettings settings, DataFixer fixerUpper, Services services) { super(serverThread, levelStorageSource, packRepository, worldStem, Proxy.NO_PROXY, fixerUpper, services, LoggingLevelLoadListener.forDedicatedServer()); this.settings = settings; this.rconConsoleSource = new RconConsoleSource(this); this.serverTextFilter = ServerTextFilter.createFromConfig(settings.getProperties()); this.serverLinks = DedicatedServer.createServerLinks(settings); this.codeOfConductTexts = settings.getProperties().codeOfConduct ? DedicatedServer.readCodeOfConducts() : Map.of(); } /* * Enabled force condition propagation * Lifted jumps to return sites */ private static Map readCodeOfConducts() { Path path = Path.of("codeofconduct", new String[0]); if (!Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS)) { throw new IllegalArgumentException("Code of Conduct folder does not exist: " + String.valueOf(path)); } try { ImmutableMap.Builder builder = ImmutableMap.builder(); try (Stream files = Files.list(path);){ for (Path file : files.toList()) { String filename = file.getFileName().toString(); if (!filename.endsWith(".txt")) continue; String language = filename.substring(0, filename.length() - 4).toLowerCase(Locale.ROOT); if (!file.toRealPath(new LinkOption[0]).getParent().equals(path.toAbsolutePath())) { throw new IllegalArgumentException("Failed to read Code of Conduct file \"" + filename + "\" because it links to a file outside the allowed directory"); } try { String codeOfConduct = String.join((CharSequence)"\n", Files.readAllLines(file, StandardCharsets.UTF_8)); builder.put((Object)language, (Object)StringUtil.stripColor(codeOfConduct)); } catch (IOException e) { throw new IllegalArgumentException("Failed to read Code of Conduct file " + filename, e); return builder.build(); } } } } catch (IOException e) { throw new IllegalArgumentException("Failed to read Code of Conduct folder", e); } } private SslContext createSslContext() { try { return JsonRpcSslContextProvider.createFrom(this.getProperties().managementServerTlsKeystore, this.getProperties().managementServerTlsKeystorePassword); } catch (Exception e) { JsonRpcSslContextProvider.printInstructions(); throw new IllegalStateException("Failed to configure TLS for the server management protocol", e); } } @Override protected boolean initServer() throws IOException { int managementPort = this.getProperties().managementServerPort; if (this.getProperties().managementServerEnabled) { String managementServerSecret = this.settings.getProperties().managementServerSecret; if (!SecurityConfig.isValid(managementServerSecret)) { throw new IllegalStateException("Invalid management server secret, must be 40 alphanumeric characters"); } String managementHost = this.getProperties().managementServerHost; HostAndPort hostAndPort = HostAndPort.fromParts((String)managementHost, (int)managementPort); SecurityConfig securityConfig = new SecurityConfig(managementServerSecret); String allowedOrigins = this.getProperties().managementServerAllowedOrigins; AuthenticationHandler authenticationHandler = new AuthenticationHandler(securityConfig, allowedOrigins); LOGGER.info("Starting json RPC server on {}", (Object)hostAndPort); this.jsonRpcServer = new ManagementServer(hostAndPort, authenticationHandler); MinecraftApi minecraftApi = MinecraftApi.of(this); minecraftApi.notificationManager().registerService(new JsonRpcNotificationService(minecraftApi, this.jsonRpcServer)); if (this.getProperties().managementServerTlsEnabled) { SslContext sslContext = this.createSslContext(); this.jsonRpcServer.startWithTls(minecraftApi, sslContext); } else { this.jsonRpcServer.startWithoutTls(minecraftApi); } } Thread consoleThread = new Thread("Server console handler"){ @Override public void run() { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); try { String line; while (!DedicatedServer.this.isStopped() && DedicatedServer.this.isRunning() && (line = reader.readLine()) != null) { DedicatedServer.this.handleConsoleInput(line, DedicatedServer.this.createCommandSourceStack()); } } catch (IOException e) { LOGGER.error("Exception handling console input", (Throwable)e); } } }; consoleThread.setDaemon(true); consoleThread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)); consoleThread.start(); LOGGER.info("Starting minecraft server version {}", (Object)SharedConstants.getCurrentVersion().name()); if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L) { LOGGER.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\""); } LOGGER.info("Loading properties"); DedicatedServerProperties properties = this.settings.getProperties(); if (this.isSingleplayer()) { this.setLocalIp("127.0.0.1"); } else { this.setUsesAuthentication(properties.onlineMode); this.setPreventProxyConnections(properties.preventProxyConnections); this.setLocalIp(properties.serverIp); } this.worldData.setGameType(properties.gameMode.get()); LOGGER.info("Default game type: {}", (Object)properties.gameMode.get()); InetAddress localAddress = null; if (!this.getLocalIp().isEmpty()) { localAddress = InetAddress.getByName(this.getLocalIp()); } if (this.getPort() < 0) { this.setPort(properties.serverPort); } this.initializeKeyPair(); LOGGER.info("Starting Minecraft server on {}:{}", (Object)(this.getLocalIp().isEmpty() ? "*" : this.getLocalIp()), (Object)this.getPort()); try { this.getConnection().startTcpServerListener(localAddress, this.getPort()); } catch (IOException e) { LOGGER.warn("**** FAILED TO BIND TO PORT!"); LOGGER.warn("The exception was: {}", (Object)e.toString()); LOGGER.warn("Perhaps a server is already running on that port?"); return false; } if (!this.usesAuthentication()) { LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); LOGGER.warn("The server will make no attempt to authenticate usernames. Beware."); LOGGER.warn("While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose."); LOGGER.warn("To change this, set \"online-mode\" to \"true\" in the server.properties file."); } if (this.convertOldUsers()) { this.services.nameToIdCache().save(); } if (!OldUsersConverter.serverReadyAfterUserconversion(this)) { return false; } this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); this.tickTimeLogger = new RemoteSampleLogger(TpsDebugDimensions.values().length, this.debugSubscribers(), RemoteDebugSampleType.TICK_TIME); long levelNanoTime = Util.getNanos(); this.services.nameToIdCache().resolveOfflineUsers(!this.usesAuthentication()); LOGGER.info("Preparing level \"{}\"", (Object)this.getLevelIdName()); this.loadLevel(); long elapsed = Util.getNanos() - levelNanoTime; String time = String.format(Locale.ROOT, "%.3fs", (double)elapsed / 1.0E9); LOGGER.info("Done ({})! For help, type \"help\"", (Object)time); if (properties.announcePlayerAchievements != null) { this.worldData.getGameRules().set(GameRules.SHOW_ADVANCEMENT_MESSAGES, properties.announcePlayerAchievements, this); } if (properties.enableQuery) { LOGGER.info("Starting GS4 status listener"); this.queryThreadGs4 = QueryThreadGs4.create(this); } if (properties.enableRcon) { LOGGER.info("Starting remote control listener"); this.rconThread = RconThread.create(this); } if (this.getMaxTickLength() > 0L) { Thread watchdog = new Thread(new ServerWatchdog(this)); watchdog.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(LOGGER)); watchdog.setName("Server Watchdog"); watchdog.setDaemon(true); watchdog.start(); } if (properties.enableJmxMonitoring) { MinecraftServerStatistics.registerJmxMonitoring(this); LOGGER.info("JMX monitoring enabled"); } this.notificationManager().serverStarted(); return true; } @Override public boolean isEnforceWhitelist() { return this.settings.getProperties().enforceWhitelist.get(); } @Override public void setEnforceWhitelist(boolean enforceWhitelist) { this.settings.update(p -> (DedicatedServerProperties)p.enforceWhitelist.update(this.registryAccess(), enforceWhitelist)); } @Override public boolean isUsingWhitelist() { return this.settings.getProperties().whiteList.get(); } @Override public void setUsingWhitelist(boolean usingWhitelist) { this.settings.update(p -> (DedicatedServerProperties)p.whiteList.update(this.registryAccess(), usingWhitelist)); } @Override protected void tickServer(BooleanSupplier haveTime) { long intervalMillis; super.tickServer(haveTime); if (this.jsonRpcServer != null) { this.jsonRpcServer.tick(); } long millis = Util.getMillis(); int heartbeatInterval = this.statusHeartbeatInterval(); if (heartbeatInterval > 0 && millis - this.lastHeartbeat >= (intervalMillis = (long)heartbeatInterval * TimeUtil.MILLISECONDS_PER_SECOND)) { this.lastHeartbeat = millis; this.notificationManager().statusHeartbeat(); } } @Override public boolean saveAllChunks(boolean silent, boolean flush, boolean force) { this.notificationManager().serverSaveStarted(); boolean savedChunks = super.saveAllChunks(silent, flush, force); this.notificationManager().serverSaveCompleted(); return savedChunks; } @Override public boolean allowFlight() { return this.settings.getProperties().allowFlight.get(); } public void setAllowFlight(boolean allowed) { this.settings.update(p -> (DedicatedServerProperties)p.allowFlight.update(this.registryAccess(), allowed)); } @Override public DedicatedServerProperties getProperties() { return this.settings.getProperties(); } public void setDifficulty(Difficulty difficulty) { this.settings.update(p -> (DedicatedServerProperties)p.difficulty.update(this.registryAccess(), difficulty)); this.forceDifficulty(); } @Override protected void forceDifficulty() { this.setDifficulty(this.getProperties().difficulty.get(), true); } public int viewDistance() { return this.settings.getProperties().viewDistance.get(); } public void setViewDistance(int viewDistance) { this.settings.update(p -> (DedicatedServerProperties)p.viewDistance.update(this.registryAccess(), viewDistance)); this.getPlayerList().setViewDistance(viewDistance); } public int simulationDistance() { return this.settings.getProperties().simulationDistance.get(); } public void setSimulationDistance(int simulationDistance) { this.settings.update(p -> (DedicatedServerProperties)p.simulationDistance.update(this.registryAccess(), simulationDistance)); this.getPlayerList().setSimulationDistance(simulationDistance); } @Override public SystemReport fillServerSystemReport(SystemReport systemReport) { systemReport.setDetail("Is Modded", () -> this.getModdedStatus().fullDescription()); systemReport.setDetail("Type", () -> "Dedicated Server"); return systemReport; } @Override public void dumpServerProperties(Path path) throws IOException { DedicatedServerProperties serverProperties = this.getProperties(); try (BufferedWriter output = Files.newBufferedWriter(path, new OpenOption[0]);){ output.write(String.format(Locale.ROOT, "sync-chunk-writes=%s%n", serverProperties.syncChunkWrites)); output.write(String.format(Locale.ROOT, "gamemode=%s%n", serverProperties.gameMode.get())); output.write(String.format(Locale.ROOT, "entity-broadcast-range-percentage=%d%n", serverProperties.entityBroadcastRangePercentage.get())); output.write(String.format(Locale.ROOT, "max-world-size=%d%n", serverProperties.maxWorldSize)); output.write(String.format(Locale.ROOT, "view-distance=%d%n", serverProperties.viewDistance.get())); output.write(String.format(Locale.ROOT, "simulation-distance=%d%n", serverProperties.simulationDistance.get())); output.write(String.format(Locale.ROOT, "generate-structures=%s%n", serverProperties.worldOptions.generateStructures())); output.write(String.format(Locale.ROOT, "use-native=%s%n", serverProperties.useNativeTransport)); output.write(String.format(Locale.ROOT, "rate-limit=%d%n", serverProperties.rateLimitPacketsPerSecond)); } } @Override protected void onServerExit() { if (this.serverTextFilter != null) { this.serverTextFilter.close(); } if (this.gui != null) { this.gui.close(); } if (this.rconThread != null) { this.rconThread.stop(); } if (this.queryThreadGs4 != null) { this.queryThreadGs4.stop(); } if (this.jsonRpcServer != null) { try { this.jsonRpcServer.stop(true); } catch (InterruptedException e) { LOGGER.error("Interrupted while stopping the management server", (Throwable)e); } } } @Override protected void tickConnection() { super.tickConnection(); this.handleConsoleInputs(); } public void handleConsoleInput(String msg, CommandSourceStack source) { this.consoleInput.add(new ConsoleInput(msg, source)); } public void handleConsoleInputs() { while (!this.consoleInput.isEmpty()) { ConsoleInput input = this.consoleInput.remove(0); this.getCommands().performPrefixedCommand(input.source, input.msg); } } @Override public boolean isDedicatedServer() { return true; } @Override public int getRateLimitPacketsPerSecond() { return this.getProperties().rateLimitPacketsPerSecond; } @Override public boolean useNativeTransport() { return this.getProperties().useNativeTransport; } @Override public DedicatedPlayerList getPlayerList() { return (DedicatedPlayerList)super.getPlayerList(); } @Override public int getMaxPlayers() { return this.settings.getProperties().maxPlayers.get(); } public void setMaxPlayers(int maxPlayers) { this.settings.update(p -> (DedicatedServerProperties)p.maxPlayers.update(this.registryAccess(), maxPlayers)); } @Override public boolean isPublished() { return true; } @Override public String getServerIp() { return this.getLocalIp(); } @Override public int getServerPort() { return this.getPort(); } @Override public String getServerName() { return this.getMotd(); } public void showGui() { if (this.gui == null) { this.gui = MinecraftServerGui.showFrameFor(this); } } @Override public boolean hasGui() { return this.gui != null; } public int spawnProtectionRadius() { return this.getProperties().spawnProtection.get(); } public void setSpawnProtectionRadius(int spawnProtectionRadius) { this.settings.update(p -> (DedicatedServerProperties)p.spawnProtection.update(this.registryAccess(), spawnProtectionRadius)); } @Override public boolean isUnderSpawnProtection(ServerLevel level, BlockPos pos, Player player) { int zd; LevelData.RespawnData respawnData = level.getRespawnData(); if (level.dimension() != respawnData.dimension()) { return false; } if (this.getPlayerList().getOps().isEmpty()) { return false; } if (this.getPlayerList().isOp(player.nameAndId())) { return false; } if (this.spawnProtectionRadius() <= 0) { return false; } BlockPos spawnPos = respawnData.pos(); int xd = Mth.abs(pos.getX() - spawnPos.getX()); int dist = Math.max(xd, zd = Mth.abs(pos.getZ() - spawnPos.getZ())); return dist <= this.spawnProtectionRadius(); } @Override public boolean repliesToStatus() { return this.getProperties().enableStatus.get(); } public void setRepliesToStatus(boolean enable) { this.settings.update(p -> (DedicatedServerProperties)p.enableStatus.update(this.registryAccess(), enable)); } @Override public boolean hidesOnlinePlayers() { return this.getProperties().hideOnlinePlayers.get(); } public void setHidesOnlinePlayers(boolean hide) { this.settings.update(p -> (DedicatedServerProperties)p.hideOnlinePlayers.update(this.registryAccess(), hide)); } @Override public LevelBasedPermissionSet operatorUserPermissions() { return this.getProperties().opPermissions.get(); } public void setOperatorUserPermissions(LevelBasedPermissionSet permissions) { this.settings.update(p -> (DedicatedServerProperties)p.opPermissions.update(this.registryAccess(), permissions)); } @Override public PermissionSet getFunctionCompilationPermissions() { return this.getProperties().functionPermissions; } @Override public int playerIdleTimeout() { return this.settings.getProperties().playerIdleTimeout.get(); } @Override public void setPlayerIdleTimeout(int playerIdleTimeout) { this.settings.update(p -> (DedicatedServerProperties)p.playerIdleTimeout.update(this.registryAccess(), playerIdleTimeout)); } public int statusHeartbeatInterval() { return this.settings.getProperties().statusHeartbeatInterval.get(); } public void setStatusHeartbeatInterval(int statusHeartbeatInterval) { this.settings.update(p -> (DedicatedServerProperties)p.statusHeartbeatInterval.update(this.registryAccess(), statusHeartbeatInterval)); } @Override public String getMotd() { return this.settings.getProperties().motd.get(); } @Override public void setMotd(String motd) { this.settings.update(p -> (DedicatedServerProperties)p.motd.update(this.registryAccess(), motd)); } @Override public boolean shouldRconBroadcast() { return this.getProperties().broadcastRconToOps; } @Override public boolean shouldInformAdmins() { return this.getProperties().broadcastConsoleToOps; } @Override public int getAbsoluteMaxWorldSize() { return this.getProperties().maxWorldSize; } @Override public int getCompressionThreshold() { return this.getProperties().networkCompressionThreshold; } @Override public boolean enforceSecureProfile() { DedicatedServerProperties properties = this.getProperties(); return properties.enforceSecureProfile && properties.onlineMode && this.services.canValidateProfileKeys(); } @Override public boolean logIPs() { return this.getProperties().logIPs; } protected boolean convertOldUsers() { int retries; boolean userBanlistConverted = false; for (retries = 0; !userBanlistConverted && retries <= 2; ++retries) { if (retries > 0) { LOGGER.warn("Encountered a problem while converting the user banlist, retrying in a few seconds"); this.waitForRetry(); } userBanlistConverted = OldUsersConverter.convertUserBanlist(this); } boolean ipBanlistConverted = false; for (retries = 0; !ipBanlistConverted && retries <= 2; ++retries) { if (retries > 0) { LOGGER.warn("Encountered a problem while converting the ip banlist, retrying in a few seconds"); this.waitForRetry(); } ipBanlistConverted = OldUsersConverter.convertIpBanlist(this); } boolean opListConverted = false; for (retries = 0; !opListConverted && retries <= 2; ++retries) { if (retries > 0) { LOGGER.warn("Encountered a problem while converting the op list, retrying in a few seconds"); this.waitForRetry(); } opListConverted = OldUsersConverter.convertOpsList(this); } boolean whitelistConverted = false; for (retries = 0; !whitelistConverted && retries <= 2; ++retries) { if (retries > 0) { LOGGER.warn("Encountered a problem while converting the whitelist, retrying in a few seconds"); this.waitForRetry(); } whitelistConverted = OldUsersConverter.convertWhiteList(this); } boolean playersConverted = false; for (retries = 0; !playersConverted && retries <= 2; ++retries) { if (retries > 0) { LOGGER.warn("Encountered a problem while converting the player save files, retrying in a few seconds"); this.waitForRetry(); } playersConverted = OldUsersConverter.convertPlayers(this); } return userBanlistConverted || ipBanlistConverted || opListConverted || whitelistConverted || playersConverted; } private void waitForRetry() { try { Thread.sleep(5000L); } catch (InterruptedException ignored) { return; } } public long getMaxTickLength() { return this.getProperties().maxTickTime; } @Override public int getMaxChainedNeighborUpdates() { return this.getProperties().maxChainedNeighborUpdates; } @Override public String getPluginNames() { return ""; } @Override public String runCommand(String command) { this.rconConsoleSource.prepareForCommand(); this.executeBlocking(() -> this.getCommands().performPrefixedCommand(this.rconConsoleSource.createCommandSourceStack(), command)); return this.rconConsoleSource.getCommandResponse(); } @Override protected void stopServer() { this.notificationManager().serverShuttingDown(); super.stopServer(); Util.shutdownExecutors(); } @Override public boolean isSingleplayerOwner(NameAndId nameAndId) { return false; } @Override public int getScaledTrackingDistance(int range) { return this.entityBroadcastRangePercentage() * range / 100; } public int entityBroadcastRangePercentage() { return this.getProperties().entityBroadcastRangePercentage.get(); } public void setEntityBroadcastRangePercentage(int range) { this.settings.update(p -> (DedicatedServerProperties)p.entityBroadcastRangePercentage.update(this.registryAccess(), range)); } @Override public String getLevelIdName() { return this.storageSource.getLevelId(); } @Override public boolean forceSynchronousWrites() { return this.settings.getProperties().syncChunkWrites; } @Override public TextFilter createTextFilterForPlayer(ServerPlayer player) { if (this.serverTextFilter != null) { return this.serverTextFilter.createContext(player.getGameProfile()); } return TextFilter.DUMMY; } @Override public @Nullable GameType getForcedGameType() { return this.forceGameMode() ? this.worldData.getGameType() : null; } public boolean forceGameMode() { return this.settings.getProperties().forceGameMode.get(); } public void setForceGameMode(boolean forceGameMode) { this.settings.update(p -> (DedicatedServerProperties)p.forceGameMode.update(this.registryAccess(), forceGameMode)); this.enforceGameTypeForPlayers(this.getForcedGameType()); } public GameType gameMode() { return this.getProperties().gameMode.get(); } public void setGameMode(GameType gameMode) { this.settings.update(p -> (DedicatedServerProperties)p.gameMode.update(this.registryAccess(), gameMode)); this.worldData.setGameType(this.gameMode()); this.enforceGameTypeForPlayers(this.getForcedGameType()); } @Override public Optional getServerResourcePack() { return this.settings.getProperties().serverResourcePackInfo; } @Override protected void endMetricsRecordingTick() { super.endMetricsRecordingTick(); this.isTickTimeLoggingEnabled = this.debugSubscribers().hasAnySubscriberFor(DebugSubscriptions.DEDICATED_SERVER_TICK_TIME); } @Override protected SampleLogger getTickTimeLogger() { return this.tickTimeLogger; } @Override public boolean isTickTimeLoggingEnabled() { return this.isTickTimeLoggingEnabled; } @Override public boolean acceptsTransfers() { return this.settings.getProperties().acceptsTransfers.get(); } public void setAcceptsTransfers(boolean acceptTransfers) { this.settings.update(p -> (DedicatedServerProperties)p.acceptsTransfers.update(this.registryAccess(), acceptTransfers)); } @Override public ServerLinks serverLinks() { return this.serverLinks; } @Override public int pauseWhenEmptySeconds() { return this.settings.getProperties().pauseWhenEmptySeconds.get(); } public void setPauseWhenEmptySeconds(int seconds) { this.settings.update(p -> (DedicatedServerProperties)p.pauseWhenEmptySeconds.update(this.registryAccess(), seconds)); } private static ServerLinks createServerLinks(DedicatedServerSettings settings) { Optional bugReportLink = DedicatedServer.parseBugReportLink(settings.getProperties()); return bugReportLink.map(bugLink -> new ServerLinks(List.of(ServerLinks.KnownLinkType.BUG_REPORT.create((URI)bugLink)))).orElse(ServerLinks.EMPTY); } private static Optional parseBugReportLink(DedicatedServerProperties properties) { String bugReportLink = properties.bugReportLink; if (bugReportLink.isEmpty()) { return Optional.empty(); } try { return Optional.of(Util.parseAndValidateUntrustedUri(bugReportLink)); } catch (Exception e) { LOGGER.warn("Failed to parse bug link {}", (Object)bugReportLink, (Object)e); return Optional.empty(); } } @Override public Map getCodeOfConducts() { return this.codeOfConductTexts; } }