package net.minecraft.client.server; import com.google.common.base.MoreObjects; import com.google.common.collect.Lists; import com.mojang.authlib.GameProfile; import com.mojang.logging.LogUtils; import java.io.IOException; import java.nio.file.Path; import java.util.UUID; import java.util.function.BooleanSupplier; import java.util.function.Supplier; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.CrashReport; import net.minecraft.SharedConstants; import net.minecraft.SystemReport; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.toasts.SystemToast; import net.minecraft.server.MinecraftServer; import net.minecraft.server.Services; import net.minecraft.server.WorldStem; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.progress.ChunkProgressListenerFactory; import net.minecraft.server.packs.repository.PackRepository; import net.minecraft.stats.Stats; import net.minecraft.util.ModCheck; import net.minecraft.util.debugchart.LocalSampleLogger; import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.GameType; import net.minecraft.world.level.chunk.storage.RegionStorageInfo; import net.minecraft.world.level.storage.LevelStorageSource; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @Environment(EnvType.CLIENT) public class IntegratedServer extends MinecraftServer { private static final Logger LOGGER = LogUtils.getLogger(); private static final int MIN_SIM_DISTANCE = 2; private final Minecraft minecraft; private boolean paused = true; private int publishedPort = -1; @Nullable private GameType publishedGameType; @Nullable private LanServerPinger lanPinger; @Nullable private UUID uuid; private int previousSimulationDistance = 0; public IntegratedServer( Thread serverThread, Minecraft minecraft, LevelStorageSource.LevelStorageAccess storageSource, PackRepository packRepository, WorldStem worldStem, Services services, ChunkProgressListenerFactory progressListenerFactory ) { super(serverThread, storageSource, packRepository, worldStem, minecraft.getProxy(), minecraft.getFixerUpper(), services, progressListenerFactory); this.setSingleplayerProfile(minecraft.getGameProfile()); this.setDemo(minecraft.isDemo()); this.setPlayerList(new IntegratedPlayerList(this, this.registries(), this.playerDataStorage)); this.minecraft = minecraft; } @Override public boolean initServer() { LOGGER.info("Starting integrated minecraft server version {}", SharedConstants.getCurrentVersion().getName()); this.setUsesAuthentication(true); this.setPvpAllowed(true); this.setFlightAllowed(true); this.initializeKeyPair(); this.loadLevel(); GameProfile gameProfile = this.getSingleplayerProfile(); String string = this.getWorldData().getLevelName(); this.setMotd(gameProfile != null ? gameProfile.getName() + " - " + string : string); return true; } @Override public boolean isPaused() { return this.paused; } @Override public void tickServer(BooleanSupplier hasTimeLeft) { boolean bl = this.paused; this.paused = Minecraft.getInstance().isPaused(); ProfilerFiller profilerFiller = Profiler.get(); if (!bl && this.paused) { profilerFiller.push("autoSave"); LOGGER.info("Saving and pausing game..."); this.saveEverything(false, false, false); profilerFiller.pop(); } boolean bl2 = Minecraft.getInstance().getConnection() != null; if (bl2 && this.paused) { this.tickPaused(); } else { if (bl && !this.paused) { this.forceTimeSynchronization(); } super.tickServer(hasTimeLeft); int i = Math.max(2, this.minecraft.options.renderDistance().get()); if (i != this.getPlayerList().getViewDistance()) { LOGGER.info("Changing view distance to {}, from {}", i, this.getPlayerList().getViewDistance()); this.getPlayerList().setViewDistance(i); } int j = Math.max(2, this.minecraft.options.simulationDistance().get()); if (j != this.previousSimulationDistance) { LOGGER.info("Changing simulation distance to {}, from {}", j, this.previousSimulationDistance); this.getPlayerList().setSimulationDistance(j); this.previousSimulationDistance = j; } } } protected LocalSampleLogger getTickTimeLogger() { return this.minecraft.getDebugOverlay().getTickTimeLogger(); } @Override public boolean isTickTimeLoggingEnabled() { return true; } private void tickPaused() { for (ServerPlayer serverPlayer : this.getPlayerList().getPlayers()) { serverPlayer.awardStat(Stats.TOTAL_WORLD_TIME); } } @Override public boolean shouldRconBroadcast() { return true; } @Override public boolean shouldInformAdmins() { return true; } @Override public Path getServerDirectory() { return this.minecraft.gameDirectory.toPath(); } @Override public boolean isDedicatedServer() { return false; } @Override public int getRateLimitPacketsPerSecond() { return 0; } @Override public boolean isEpollEnabled() { return false; } @Override public void onServerCrash(CrashReport report) { this.minecraft.delayCrashRaw(report); } @Override public SystemReport fillServerSystemReport(SystemReport report) { report.setDetail("Type", "Integrated Server (map_client.txt)"); report.setDetail("Is Modded", (Supplier)(() -> this.getModdedStatus().fullDescription())); report.setDetail("Launched Version", this.minecraft::getLaunchedVersion); return report; } @Override public ModCheck getModdedStatus() { return Minecraft.checkModStatus().merge(super.getModdedStatus()); } @Override public boolean publishServer(@Nullable GameType gameMode, boolean commands, int port) { try { this.minecraft.prepareForMultiplayer(); this.minecraft.getConnection().prepareKeyPair(); this.getConnection().startTcpServerListener(null, port); LOGGER.info("Started serving on {}", port); this.publishedPort = port; this.lanPinger = new LanServerPinger(this.getMotd(), port + ""); this.lanPinger.start(); this.publishedGameType = gameMode; this.getPlayerList().setAllowCommandsForAllPlayers(commands); int i = this.getProfilePermissions(this.minecraft.player.getGameProfile()); this.minecraft.player.setPermissionLevel(i); for (ServerPlayer serverPlayer : this.getPlayerList().getPlayers()) { this.getCommands().sendCommands(serverPlayer); } return true; } catch (IOException var7) { return false; } } @Override public void stopServer() { super.stopServer(); if (this.lanPinger != null) { this.lanPinger.interrupt(); this.lanPinger = null; } } @Override public void halt(boolean waitForServer) { this.executeBlocking(() -> { for (ServerPlayer serverPlayer : Lists.newArrayList(this.getPlayerList().getPlayers())) { if (!serverPlayer.getUUID().equals(this.uuid)) { this.getPlayerList().remove(serverPlayer); } } }); super.halt(waitForServer); if (this.lanPinger != null) { this.lanPinger.interrupt(); this.lanPinger = null; } } @Override public boolean isPublished() { return this.publishedPort > -1; } @Override public int getPort() { return this.publishedPort; } @Override public void setDefaultGameType(GameType gameMode) { super.setDefaultGameType(gameMode); this.publishedGameType = null; } @Override public boolean isCommandBlockEnabled() { return true; } @Override public int getOperatorUserPermissionLevel() { return 2; } @Override public int getFunctionCompilationLevel() { return 2; } public void setUUID(UUID uuid) { this.uuid = uuid; } @Override public boolean isSingleplayerOwner(GameProfile profile) { return this.getSingleplayerProfile() != null && profile.getName().equalsIgnoreCase(this.getSingleplayerProfile().getName()); } @Override public int getScaledTrackingDistance(int trackingDistance) { return (int)(this.minecraft.options.entityDistanceScaling().get() * trackingDistance); } @Override public boolean forceSynchronousWrites() { return this.minecraft.options.syncWrites; } @Nullable @Override public GameType getForcedGameType() { return this.isPublished() && !this.isHardcore() ? MoreObjects.firstNonNull(this.publishedGameType, this.worldData.getGameType()) : null; } @Override public boolean saveEverything(boolean suppressLog, boolean flush, boolean forced) { boolean bl = super.saveEverything(suppressLog, flush, forced); this.warnOnLowDiskSpace(); return bl; } private void warnOnLowDiskSpace() { if (this.storageSource.checkForLowDiskSpace()) { this.minecraft.execute(() -> SystemToast.onLowDiskSpace(this.minecraft)); } } @Override public void reportChunkLoadFailure(Throwable throwable, RegionStorageInfo regionStorageInfo, ChunkPos chunkPos) { super.reportChunkLoadFailure(throwable, regionStorageInfo, chunkPos); this.warnOnLowDiskSpace(); this.minecraft.execute(() -> SystemToast.onChunkLoadFailure(this.minecraft, chunkPos)); } @Override public void reportChunkSaveFailure(Throwable throwable, RegionStorageInfo regionStorageInfo, ChunkPos chunkPos) { super.reportChunkSaveFailure(throwable, regionStorageInfo, chunkPos); this.warnOnLowDiskSpace(); this.minecraft.execute(() -> SystemToast.onChunkSaveFailure(this.minecraft, chunkPos)); } }