903 lines
		
	
	
	
		
			34 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			903 lines
		
	
	
	
		
			34 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.server.players;
 | |
| 
 | |
| import com.google.common.collect.Lists;
 | |
| import com.google.common.collect.Maps;
 | |
| import com.google.common.collect.Sets;
 | |
| import com.mojang.authlib.GameProfile;
 | |
| import com.mojang.logging.LogUtils;
 | |
| import java.io.File;
 | |
| import java.net.SocketAddress;
 | |
| import java.nio.file.Path;
 | |
| import java.text.SimpleDateFormat;
 | |
| import java.time.Instant;
 | |
| import java.util.EnumSet;
 | |
| import java.util.List;
 | |
| import java.util.Map;
 | |
| import java.util.Optional;
 | |
| import java.util.Set;
 | |
| import java.util.UUID;
 | |
| import java.util.function.Function;
 | |
| import java.util.function.Predicate;
 | |
| import net.minecraft.ChatFormatting;
 | |
| import net.minecraft.FileUtil;
 | |
| import net.minecraft.commands.CommandSourceStack;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.core.LayeredRegistryAccess;
 | |
| import net.minecraft.nbt.CompoundTag;
 | |
| import net.minecraft.network.Connection;
 | |
| import net.minecraft.network.RegistryFriendlyByteBuf;
 | |
| import net.minecraft.network.chat.ChatType;
 | |
| import net.minecraft.network.chat.Component;
 | |
| import net.minecraft.network.chat.MutableComponent;
 | |
| import net.minecraft.network.chat.OutgoingChatMessage;
 | |
| import net.minecraft.network.chat.PlayerChatMessage;
 | |
| import net.minecraft.network.protocol.Packet;
 | |
| import net.minecraft.network.protocol.common.ClientboundUpdateTagsPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundInitializeBorderPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundLoginPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundRespawnPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetBorderCenterPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetBorderLerpSizePacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetBorderSizePacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDelayPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDistancePacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetExperiencePacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetHeldSlotPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetSimulationDistancePacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetTimePacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundSoundPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundUpdateMobEffectPacket;
 | |
| import net.minecraft.network.protocol.game.ClientboundUpdateRecipesPacket;
 | |
| import net.minecraft.network.protocol.game.GameProtocols;
 | |
| import net.minecraft.network.protocol.status.ServerStatus;
 | |
| import net.minecraft.resources.ResourceKey;
 | |
| import net.minecraft.server.MinecraftServer;
 | |
| import net.minecraft.server.PlayerAdvancements;
 | |
| import net.minecraft.server.RegistryLayer;
 | |
| import net.minecraft.server.ServerScoreboard;
 | |
| import net.minecraft.server.level.ServerLevel;
 | |
| import net.minecraft.server.level.ServerPlayer;
 | |
| import net.minecraft.server.network.CommonListenerCookie;
 | |
| import net.minecraft.server.network.ServerGamePacketListenerImpl;
 | |
| import net.minecraft.sounds.SoundEvents;
 | |
| import net.minecraft.sounds.SoundSource;
 | |
| import net.minecraft.stats.ServerStatsCounter;
 | |
| import net.minecraft.stats.Stats;
 | |
| import net.minecraft.tags.TagNetworkSerialization;
 | |
| import net.minecraft.util.ProblemReporter;
 | |
| import net.minecraft.world.effect.MobEffectInstance;
 | |
| import net.minecraft.world.entity.Entity;
 | |
| import net.minecraft.world.entity.LivingEntity;
 | |
| import net.minecraft.world.entity.player.Player;
 | |
| import net.minecraft.world.entity.projectile.ThrownEnderpearl;
 | |
| import net.minecraft.world.item.crafting.RecipeManager;
 | |
| import net.minecraft.world.level.GameRules;
 | |
| import net.minecraft.world.level.Level;
 | |
| import net.minecraft.world.level.block.Blocks;
 | |
| import net.minecraft.world.level.block.state.BlockState;
 | |
| import net.minecraft.world.level.border.BorderChangeListener;
 | |
| import net.minecraft.world.level.border.WorldBorder;
 | |
| import net.minecraft.world.level.portal.TeleportTransition;
 | |
| import net.minecraft.world.level.storage.LevelData;
 | |
| import net.minecraft.world.level.storage.LevelResource;
 | |
| import net.minecraft.world.level.storage.PlayerDataStorage;
 | |
| import net.minecraft.world.level.storage.TagValueInput;
 | |
| import net.minecraft.world.level.storage.ValueInput;
 | |
| import net.minecraft.world.phys.Vec3;
 | |
| import net.minecraft.world.scores.DisplaySlot;
 | |
| import net.minecraft.world.scores.Objective;
 | |
| import net.minecraft.world.scores.PlayerTeam;
 | |
| import net.minecraft.world.scores.Team;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| import org.slf4j.Logger;
 | |
| 
 | |
| public abstract class PlayerList {
 | |
| 	public static final File USERBANLIST_FILE = new File("banned-players.json");
 | |
| 	public static final File IPBANLIST_FILE = new File("banned-ips.json");
 | |
| 	public static final File OPLIST_FILE = new File("ops.json");
 | |
| 	public static final File WHITELIST_FILE = new File("whitelist.json");
 | |
| 	public static final Component CHAT_FILTERED_FULL = Component.translatable("chat.filtered_full");
 | |
| 	public static final Component DUPLICATE_LOGIN_DISCONNECT_MESSAGE = Component.translatable("multiplayer.disconnect.duplicate_login");
 | |
| 	private static final Logger LOGGER = LogUtils.getLogger();
 | |
| 	private static final int SEND_PLAYER_INFO_INTERVAL = 600;
 | |
| 	private static final SimpleDateFormat BAN_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z");
 | |
| 	private final MinecraftServer server;
 | |
| 	private final List<ServerPlayer> players = Lists.<ServerPlayer>newArrayList();
 | |
| 	/**
 | |
| 	 * A map containing the key-value pairs for UUIDs and their EntityPlayerMP objects.
 | |
| 	 */
 | |
| 	private final Map<UUID, ServerPlayer> playersByUUID = Maps.<UUID, ServerPlayer>newHashMap();
 | |
| 	private final UserBanList bans = new UserBanList(USERBANLIST_FILE);
 | |
| 	private final IpBanList ipBans = new IpBanList(IPBANLIST_FILE);
 | |
| 	private final ServerOpList ops = new ServerOpList(OPLIST_FILE);
 | |
| 	private final UserWhiteList whitelist = new UserWhiteList(WHITELIST_FILE);
 | |
| 	private final Map<UUID, ServerStatsCounter> stats = Maps.<UUID, ServerStatsCounter>newHashMap();
 | |
| 	private final Map<UUID, PlayerAdvancements> advancements = Maps.<UUID, PlayerAdvancements>newHashMap();
 | |
| 	private final PlayerDataStorage playerIo;
 | |
| 	private boolean doWhiteList;
 | |
| 	private final LayeredRegistryAccess<RegistryLayer> registries;
 | |
| 	protected final int maxPlayers;
 | |
| 	private int viewDistance;
 | |
| 	private int simulationDistance;
 | |
| 	private boolean allowCommandsForAllPlayers;
 | |
| 	private static final boolean ALLOW_LOGOUTIVATOR = false;
 | |
| 	private int sendAllPlayerInfoIn;
 | |
| 
 | |
| 	public PlayerList(MinecraftServer server, LayeredRegistryAccess<RegistryLayer> registries, PlayerDataStorage playerIo, int maxPlayers) {
 | |
| 		this.server = server;
 | |
| 		this.registries = registries;
 | |
| 		this.maxPlayers = maxPlayers;
 | |
| 		this.playerIo = playerIo;
 | |
| 	}
 | |
| 
 | |
| 	public void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie cookie) {
 | |
| 		GameProfile gameProfile = player.getGameProfile();
 | |
| 		GameProfileCache gameProfileCache = this.server.getProfileCache();
 | |
| 		String string;
 | |
| 		if (gameProfileCache != null) {
 | |
| 			Optional<GameProfile> optional = gameProfileCache.get(gameProfile.getId());
 | |
| 			string = (String)optional.map(GameProfile::getName).orElse(gameProfile.getName());
 | |
| 			gameProfileCache.add(gameProfile);
 | |
| 		} else {
 | |
| 			string = gameProfile.getName();
 | |
| 		}
 | |
| 
 | |
| 		try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), LOGGER)) {
 | |
| 			Optional<ValueInput> optional2 = this.load(player, scopedCollector);
 | |
| 			ResourceKey<Level> resourceKey = (ResourceKey<Level>)optional2.flatMap(valueInput -> valueInput.read("Dimension", Level.RESOURCE_KEY_CODEC))
 | |
| 				.orElse(Level.OVERWORLD);
 | |
| 			ServerLevel serverLevel = this.server.getLevel(resourceKey);
 | |
| 			ServerLevel serverLevel2;
 | |
| 			if (serverLevel == null) {
 | |
| 				LOGGER.warn("Unknown respawn dimension {}, defaulting to overworld", resourceKey);
 | |
| 				serverLevel2 = this.server.overworld();
 | |
| 			} else {
 | |
| 				serverLevel2 = serverLevel;
 | |
| 			}
 | |
| 
 | |
| 			player.setServerLevel(serverLevel2);
 | |
| 			if (optional2.isEmpty()) {
 | |
| 				player.snapTo(player.adjustSpawnLocation(serverLevel2, serverLevel2.getSharedSpawnPos()).getBottomCenter(), serverLevel2.getSharedSpawnAngle(), 0.0F);
 | |
| 			}
 | |
| 
 | |
| 			serverLevel2.waitForChunkAndEntities(player.chunkPosition(), 1);
 | |
| 			String string2 = connection.getLoggableAddress(this.server.logIPs());
 | |
| 			LOGGER.info(
 | |
| 				"{}[{}] logged in with entity id {} at ({}, {}, {})", player.getName().getString(), string2, player.getId(), player.getX(), player.getY(), player.getZ()
 | |
| 			);
 | |
| 			LevelData levelData = serverLevel2.getLevelData();
 | |
| 			player.loadGameTypes((ValueInput)optional2.orElse(null));
 | |
| 			ServerGamePacketListenerImpl serverGamePacketListenerImpl = new ServerGamePacketListenerImpl(this.server, connection, player, cookie);
 | |
| 			connection.setupInboundProtocol(
 | |
| 				GameProtocols.SERVERBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()), serverGamePacketListenerImpl),
 | |
| 				serverGamePacketListenerImpl
 | |
| 			);
 | |
| 			GameRules gameRules = serverLevel2.getGameRules();
 | |
| 			boolean bl = gameRules.getBoolean(GameRules.RULE_DO_IMMEDIATE_RESPAWN);
 | |
| 			boolean bl2 = gameRules.getBoolean(GameRules.RULE_REDUCEDDEBUGINFO);
 | |
| 			boolean bl3 = gameRules.getBoolean(GameRules.RULE_LIMITED_CRAFTING);
 | |
| 			serverGamePacketListenerImpl.send(
 | |
| 				new ClientboundLoginPacket(
 | |
| 					player.getId(),
 | |
| 					levelData.isHardcore(),
 | |
| 					this.server.levelKeys(),
 | |
| 					this.getMaxPlayers(),
 | |
| 					this.viewDistance,
 | |
| 					this.simulationDistance,
 | |
| 					bl2,
 | |
| 					!bl,
 | |
| 					bl3,
 | |
| 					player.createCommonSpawnInfo(serverLevel2),
 | |
| 					this.server.enforceSecureProfile()
 | |
| 				)
 | |
| 			);
 | |
| 			serverGamePacketListenerImpl.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked()));
 | |
| 			serverGamePacketListenerImpl.send(new ClientboundPlayerAbilitiesPacket(player.getAbilities()));
 | |
| 			serverGamePacketListenerImpl.send(new ClientboundSetHeldSlotPacket(player.getInventory().getSelectedSlot()));
 | |
| 			RecipeManager recipeManager = this.server.getRecipeManager();
 | |
| 			serverGamePacketListenerImpl.send(
 | |
| 				new ClientboundUpdateRecipesPacket(recipeManager.getSynchronizedItemProperties(), recipeManager.getSynchronizedStonecutterRecipes())
 | |
| 			);
 | |
| 			this.sendPlayerPermissionLevel(player);
 | |
| 			player.getStats().markAllDirty();
 | |
| 			player.getRecipeBook().sendInitialRecipeBook(player);
 | |
| 			this.updateEntireScoreboard(serverLevel2.getScoreboard(), player);
 | |
| 			this.server.invalidateStatus();
 | |
| 			MutableComponent mutableComponent;
 | |
| 			if (player.getGameProfile().getName().equalsIgnoreCase(string)) {
 | |
| 				mutableComponent = Component.translatable("multiplayer.player.joined", player.getDisplayName());
 | |
| 			} else {
 | |
| 				mutableComponent = Component.translatable("multiplayer.player.joined.renamed", player.getDisplayName(), string);
 | |
| 			}
 | |
| 
 | |
| 			this.broadcastSystemMessage(mutableComponent.withStyle(ChatFormatting.YELLOW), false);
 | |
| 			serverGamePacketListenerImpl.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot());
 | |
| 			ServerStatus serverStatus = this.server.getStatus();
 | |
| 			if (serverStatus != null && !cookie.transferred()) {
 | |
| 				player.sendServerStatus(serverStatus);
 | |
| 			}
 | |
| 
 | |
| 			player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players));
 | |
| 			this.players.add(player);
 | |
| 			this.playersByUUID.put(player.getUUID(), player);
 | |
| 			this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player)));
 | |
| 			this.sendLevelInfo(player, serverLevel2);
 | |
| 			serverLevel2.addNewPlayer(player);
 | |
| 			this.server.getCustomBossEvents().onPlayerConnect(player);
 | |
| 			this.sendActivePlayerEffects(player);
 | |
| 			optional2.ifPresent(valueInput -> {
 | |
| 				player.loadAndSpawnEnderPearls(valueInput);
 | |
| 				player.loadAndSpawnParentVehicle(valueInput);
 | |
| 			});
 | |
| 			player.initInventoryMenu();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected void updateEntireScoreboard(ServerScoreboard scoreboard, ServerPlayer player) {
 | |
| 		Set<Objective> set = Sets.<Objective>newHashSet();
 | |
| 
 | |
| 		for (PlayerTeam playerTeam : scoreboard.getPlayerTeams()) {
 | |
| 			player.connection.send(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(playerTeam, true));
 | |
| 		}
 | |
| 
 | |
| 		for (DisplaySlot displaySlot : DisplaySlot.values()) {
 | |
| 			Objective objective = scoreboard.getDisplayObjective(displaySlot);
 | |
| 			if (objective != null && !set.contains(objective)) {
 | |
| 				for (Packet<?> packet : scoreboard.getStartTrackingPackets(objective)) {
 | |
| 					player.connection.send(packet);
 | |
| 				}
 | |
| 
 | |
| 				set.add(objective);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void addWorldborderListener(ServerLevel level) {
 | |
| 		level.getWorldBorder().addListener(new BorderChangeListener() {
 | |
| 			@Override
 | |
| 			public void onBorderSizeSet(WorldBorder border, double size) {
 | |
| 				PlayerList.this.broadcastAll(new ClientboundSetBorderSizePacket(border));
 | |
| 			}
 | |
| 
 | |
| 			@Override
 | |
| 			public void onBorderSizeLerping(WorldBorder border, double oldSize, double newSize, long time) {
 | |
| 				PlayerList.this.broadcastAll(new ClientboundSetBorderLerpSizePacket(border));
 | |
| 			}
 | |
| 
 | |
| 			@Override
 | |
| 			public void onBorderCenterSet(WorldBorder border, double x, double z) {
 | |
| 				PlayerList.this.broadcastAll(new ClientboundSetBorderCenterPacket(border));
 | |
| 			}
 | |
| 
 | |
| 			@Override
 | |
| 			public void onBorderSetWarningTime(WorldBorder border, int warningTime) {
 | |
| 				PlayerList.this.broadcastAll(new ClientboundSetBorderWarningDelayPacket(border));
 | |
| 			}
 | |
| 
 | |
| 			@Override
 | |
| 			public void onBorderSetWarningBlocks(WorldBorder border, int warningBlocks) {
 | |
| 				PlayerList.this.broadcastAll(new ClientboundSetBorderWarningDistancePacket(border));
 | |
| 			}
 | |
| 
 | |
| 			@Override
 | |
| 			public void onBorderSetDamagePerBlock(WorldBorder border, double damagePerBlock) {
 | |
| 			}
 | |
| 
 | |
| 			@Override
 | |
| 			public void onBorderSetDamageSafeZOne(WorldBorder border, double damageSafeZone) {
 | |
| 			}
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	public Optional<ValueInput> load(ServerPlayer player, ProblemReporter problemReporter) {
 | |
| 		CompoundTag compoundTag = this.server.getWorldData().getLoadedPlayerTag();
 | |
| 		Optional<ValueInput> optional;
 | |
| 		if (this.server.isSingleplayerOwner(player.getGameProfile()) && compoundTag != null) {
 | |
| 			ValueInput valueInput = TagValueInput.create(problemReporter, player.registryAccess(), compoundTag);
 | |
| 			optional = Optional.of(valueInput);
 | |
| 			player.load(valueInput);
 | |
| 			LOGGER.debug("loading single player");
 | |
| 		} else {
 | |
| 			optional = this.playerIo.load(player, problemReporter);
 | |
| 		}
 | |
| 
 | |
| 		return optional;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Also stores the NBTTags if this is an IntegratedPlayerList.
 | |
| 	 */
 | |
| 	protected void save(ServerPlayer player) {
 | |
| 		this.playerIo.save(player);
 | |
| 		ServerStatsCounter serverStatsCounter = (ServerStatsCounter)this.stats.get(player.getUUID());
 | |
| 		if (serverStatsCounter != null) {
 | |
| 			serverStatsCounter.save();
 | |
| 		}
 | |
| 
 | |
| 		PlayerAdvancements playerAdvancements = (PlayerAdvancements)this.advancements.get(player.getUUID());
 | |
| 		if (playerAdvancements != null) {
 | |
| 			playerAdvancements.save();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Called when a player disconnects from the game. Writes player data to disk and removes them from the world.
 | |
| 	 */
 | |
| 	public void remove(ServerPlayer player) {
 | |
| 		ServerLevel serverLevel = player.level();
 | |
| 		player.awardStat(Stats.LEAVE_GAME);
 | |
| 		this.save(player);
 | |
| 		if (player.isPassenger()) {
 | |
| 			Entity entity = player.getRootVehicle();
 | |
| 			if (entity.hasExactlyOnePlayerPassenger()) {
 | |
| 				LOGGER.debug("Removing player mount");
 | |
| 				player.stopRiding();
 | |
| 				entity.getPassengersAndSelf().forEach(entityx -> entityx.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER));
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		player.unRide();
 | |
| 
 | |
| 		for (ThrownEnderpearl thrownEnderpearl : player.getEnderPearls()) {
 | |
| 			thrownEnderpearl.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER);
 | |
| 		}
 | |
| 
 | |
| 		serverLevel.removePlayerImmediately(player, Entity.RemovalReason.UNLOADED_WITH_PLAYER);
 | |
| 		player.getAdvancements().stopListening();
 | |
| 		this.players.remove(player);
 | |
| 		this.server.getCustomBossEvents().onPlayerDisconnect(player);
 | |
| 		UUID uUID = player.getUUID();
 | |
| 		ServerPlayer serverPlayer = (ServerPlayer)this.playersByUUID.get(uUID);
 | |
| 		if (serverPlayer == player) {
 | |
| 			this.playersByUUID.remove(uUID);
 | |
| 			this.stats.remove(uUID);
 | |
| 			this.advancements.remove(uUID);
 | |
| 		}
 | |
| 
 | |
| 		this.broadcastAll(new ClientboundPlayerInfoRemovePacket(List.of(player.getUUID())));
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	public Component canPlayerLogin(SocketAddress socketAddress, GameProfile gameProfile) {
 | |
| 		if (this.bans.isBanned(gameProfile)) {
 | |
| 			UserBanListEntry userBanListEntry = this.bans.get(gameProfile);
 | |
| 			MutableComponent mutableComponent = Component.translatable("multiplayer.disconnect.banned.reason", userBanListEntry.getReason());
 | |
| 			if (userBanListEntry.getExpires() != null) {
 | |
| 				mutableComponent.append(Component.translatable("multiplayer.disconnect.banned.expiration", BAN_DATE_FORMAT.format(userBanListEntry.getExpires())));
 | |
| 			}
 | |
| 
 | |
| 			return mutableComponent;
 | |
| 		} else if (!this.isWhiteListed(gameProfile)) {
 | |
| 			return Component.translatable("multiplayer.disconnect.not_whitelisted");
 | |
| 		} else if (this.ipBans.isBanned(socketAddress)) {
 | |
| 			IpBanListEntry ipBanListEntry = this.ipBans.get(socketAddress);
 | |
| 			MutableComponent mutableComponent = Component.translatable("multiplayer.disconnect.banned_ip.reason", ipBanListEntry.getReason());
 | |
| 			if (ipBanListEntry.getExpires() != null) {
 | |
| 				mutableComponent.append(Component.translatable("multiplayer.disconnect.banned_ip.expiration", BAN_DATE_FORMAT.format(ipBanListEntry.getExpires())));
 | |
| 			}
 | |
| 
 | |
| 			return mutableComponent;
 | |
| 		} else {
 | |
| 			return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameProfile)
 | |
| 				? Component.translatable("multiplayer.disconnect.server_full")
 | |
| 				: null;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public boolean disconnectAllPlayersWithProfile(GameProfile gameProfile) {
 | |
| 		UUID uUID = gameProfile.getId();
 | |
| 		Set<ServerPlayer> set = Sets.newIdentityHashSet();
 | |
| 
 | |
| 		for (ServerPlayer serverPlayer : this.players) {
 | |
| 			if (serverPlayer.getUUID().equals(uUID)) {
 | |
| 				set.add(serverPlayer);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		ServerPlayer serverPlayer2 = (ServerPlayer)this.playersByUUID.get(gameProfile.getId());
 | |
| 		if (serverPlayer2 != null) {
 | |
| 			set.add(serverPlayer2);
 | |
| 		}
 | |
| 
 | |
| 		for (ServerPlayer serverPlayer3 : set) {
 | |
| 			serverPlayer3.connection.disconnect(DUPLICATE_LOGIN_DISCONNECT_MESSAGE);
 | |
| 		}
 | |
| 
 | |
| 		return !set.isEmpty();
 | |
| 	}
 | |
| 
 | |
| 	public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason) {
 | |
| 		this.players.remove(player);
 | |
| 		player.level().removePlayerImmediately(player, reason);
 | |
| 		TeleportTransition teleportTransition = player.findRespawnPositionAndUseSpawnBlock(!keepInventory, TeleportTransition.DO_NOTHING);
 | |
| 		ServerLevel serverLevel = teleportTransition.newLevel();
 | |
| 		ServerPlayer serverPlayer = new ServerPlayer(this.server, serverLevel, player.getGameProfile(), player.clientInformation());
 | |
| 		serverPlayer.connection = player.connection;
 | |
| 		serverPlayer.restoreFrom(player, keepInventory);
 | |
| 		serverPlayer.setId(player.getId());
 | |
| 		serverPlayer.setMainArm(player.getMainArm());
 | |
| 		if (!teleportTransition.missingRespawnBlock()) {
 | |
| 			serverPlayer.copyRespawnPosition(player);
 | |
| 		}
 | |
| 
 | |
| 		for (String string : player.getTags()) {
 | |
| 			serverPlayer.addTag(string);
 | |
| 		}
 | |
| 
 | |
| 		Vec3 vec3 = teleportTransition.position();
 | |
| 		serverPlayer.snapTo(vec3.x, vec3.y, vec3.z, teleportTransition.yRot(), teleportTransition.xRot());
 | |
| 		if (teleportTransition.missingRespawnBlock()) {
 | |
| 			serverPlayer.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.NO_RESPAWN_BLOCK_AVAILABLE, 0.0F));
 | |
| 		}
 | |
| 
 | |
| 		byte b = (byte)(keepInventory ? 1 : 0);
 | |
| 		ServerLevel serverLevel2 = serverPlayer.level();
 | |
| 		LevelData levelData = serverLevel2.getLevelData();
 | |
| 		serverPlayer.connection.send(new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(serverLevel2), b));
 | |
| 		serverPlayer.connection.teleport(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ(), serverPlayer.getYRot(), serverPlayer.getXRot());
 | |
| 		serverPlayer.connection.send(new ClientboundSetDefaultSpawnPositionPacket(serverLevel.getSharedSpawnPos(), serverLevel.getSharedSpawnAngle()));
 | |
| 		serverPlayer.connection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked()));
 | |
| 		serverPlayer.connection.send(new ClientboundSetExperiencePacket(serverPlayer.experienceProgress, serverPlayer.totalExperience, serverPlayer.experienceLevel));
 | |
| 		this.sendActivePlayerEffects(serverPlayer);
 | |
| 		this.sendLevelInfo(serverPlayer, serverLevel);
 | |
| 		this.sendPlayerPermissionLevel(serverPlayer);
 | |
| 		serverLevel.addRespawnedPlayer(serverPlayer);
 | |
| 		this.players.add(serverPlayer);
 | |
| 		this.playersByUUID.put(serverPlayer.getUUID(), serverPlayer);
 | |
| 		serverPlayer.initInventoryMenu();
 | |
| 		serverPlayer.setHealth(serverPlayer.getHealth());
 | |
| 		ServerPlayer.RespawnConfig respawnConfig = serverPlayer.getRespawnConfig();
 | |
| 		if (!keepInventory && respawnConfig != null) {
 | |
| 			ServerLevel serverLevel3 = this.server.getLevel(respawnConfig.dimension());
 | |
| 			if (serverLevel3 != null) {
 | |
| 				BlockPos blockPos = respawnConfig.pos();
 | |
| 				BlockState blockState = serverLevel3.getBlockState(blockPos);
 | |
| 				if (blockState.is(Blocks.RESPAWN_ANCHOR)) {
 | |
| 					serverPlayer.connection
 | |
| 						.send(
 | |
| 							new ClientboundSoundPacket(
 | |
| 								SoundEvents.RESPAWN_ANCHOR_DEPLETE,
 | |
| 								SoundSource.BLOCKS,
 | |
| 								blockPos.getX(),
 | |
| 								blockPos.getY(),
 | |
| 								blockPos.getZ(),
 | |
| 								1.0F,
 | |
| 								1.0F,
 | |
| 								serverLevel.getRandom().nextLong()
 | |
| 							)
 | |
| 						);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return serverPlayer;
 | |
| 	}
 | |
| 
 | |
| 	public void sendActivePlayerEffects(ServerPlayer player) {
 | |
| 		this.sendActiveEffects(player, player.connection);
 | |
| 	}
 | |
| 
 | |
| 	public void sendActiveEffects(LivingEntity entity, ServerGamePacketListenerImpl connection) {
 | |
| 		for (MobEffectInstance mobEffectInstance : entity.getActiveEffects()) {
 | |
| 			connection.send(new ClientboundUpdateMobEffectPacket(entity.getId(), mobEffectInstance, false));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void sendPlayerPermissionLevel(ServerPlayer player) {
 | |
| 		GameProfile gameProfile = player.getGameProfile();
 | |
| 		int i = this.server.getProfilePermissions(gameProfile);
 | |
| 		this.sendPlayerPermissionLevel(player, i);
 | |
| 	}
 | |
| 
 | |
| 	public void tick() {
 | |
| 		if (++this.sendAllPlayerInfoIn > 600) {
 | |
| 			this.broadcastAll(new ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY), this.players));
 | |
| 			this.sendAllPlayerInfoIn = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void broadcastAll(Packet<?> packet) {
 | |
| 		for (ServerPlayer serverPlayer : this.players) {
 | |
| 			serverPlayer.connection.send(packet);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void broadcastAll(Packet<?> packet, ResourceKey<Level> dimension) {
 | |
| 		for (ServerPlayer serverPlayer : this.players) {
 | |
| 			if (serverPlayer.level().dimension() == dimension) {
 | |
| 				serverPlayer.connection.send(packet);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void broadcastSystemToTeam(Player player, Component message) {
 | |
| 		Team team = player.getTeam();
 | |
| 		if (team != null) {
 | |
| 			for (String string : team.getPlayers()) {
 | |
| 				ServerPlayer serverPlayer = this.getPlayerByName(string);
 | |
| 				if (serverPlayer != null && serverPlayer != player) {
 | |
| 					serverPlayer.sendSystemMessage(message);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void broadcastSystemToAllExceptTeam(Player player, Component message) {
 | |
| 		Team team = player.getTeam();
 | |
| 		if (team == null) {
 | |
| 			this.broadcastSystemMessage(message, false);
 | |
| 		} else {
 | |
| 			for (int i = 0; i < this.players.size(); i++) {
 | |
| 				ServerPlayer serverPlayer = (ServerPlayer)this.players.get(i);
 | |
| 				if (serverPlayer.getTeam() != team) {
 | |
| 					serverPlayer.sendSystemMessage(message);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns an array of the usernames of all the connected players.
 | |
| 	 */
 | |
| 	public String[] getPlayerNamesArray() {
 | |
| 		String[] strings = new String[this.players.size()];
 | |
| 
 | |
| 		for (int i = 0; i < this.players.size(); i++) {
 | |
| 			strings[i] = ((ServerPlayer)this.players.get(i)).getGameProfile().getName();
 | |
| 		}
 | |
| 
 | |
| 		return strings;
 | |
| 	}
 | |
| 
 | |
| 	public UserBanList getBans() {
 | |
| 		return this.bans;
 | |
| 	}
 | |
| 
 | |
| 	public IpBanList getIpBans() {
 | |
| 		return this.ipBans;
 | |
| 	}
 | |
| 
 | |
| 	public void op(GameProfile profile) {
 | |
| 		this.ops.add(new ServerOpListEntry(profile, this.server.getOperatorUserPermissionLevel(), this.ops.canBypassPlayerLimit(profile)));
 | |
| 		ServerPlayer serverPlayer = this.getPlayer(profile.getId());
 | |
| 		if (serverPlayer != null) {
 | |
| 			this.sendPlayerPermissionLevel(serverPlayer);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void deop(GameProfile profile) {
 | |
| 		this.ops.remove(profile);
 | |
| 		ServerPlayer serverPlayer = this.getPlayer(profile.getId());
 | |
| 		if (serverPlayer != null) {
 | |
| 			this.sendPlayerPermissionLevel(serverPlayer);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void sendPlayerPermissionLevel(ServerPlayer player, int permLevel) {
 | |
| 		if (player.connection != null) {
 | |
| 			byte b;
 | |
| 			if (permLevel <= 0) {
 | |
| 				b = 24;
 | |
| 			} else if (permLevel >= 4) {
 | |
| 				b = 28;
 | |
| 			} else {
 | |
| 				b = (byte)(24 + permLevel);
 | |
| 			}
 | |
| 
 | |
| 			player.connection.send(new ClientboundEntityEventPacket(player, b));
 | |
| 		}
 | |
| 
 | |
| 		this.server.getCommands().sendCommands(player);
 | |
| 	}
 | |
| 
 | |
| 	public boolean isWhiteListed(GameProfile profile) {
 | |
| 		return !this.doWhiteList || this.ops.contains(profile) || this.whitelist.contains(profile);
 | |
| 	}
 | |
| 
 | |
| 	public boolean isOp(GameProfile profile) {
 | |
| 		return this.ops.contains(profile)
 | |
| 			|| this.server.isSingleplayerOwner(profile) && this.server.getWorldData().isAllowCommands()
 | |
| 			|| this.allowCommandsForAllPlayers;
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	public ServerPlayer getPlayerByName(String username) {
 | |
| 		int i = this.players.size();
 | |
| 
 | |
| 		for (int j = 0; j < i; j++) {
 | |
| 			ServerPlayer serverPlayer = (ServerPlayer)this.players.get(j);
 | |
| 			if (serverPlayer.getGameProfile().getName().equalsIgnoreCase(username)) {
 | |
| 				return serverPlayer;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return null;
 | |
| 	}
 | |
| 
 | |
| 	public void broadcast(@Nullable Player except, double x, double y, double z, double radius, ResourceKey<Level> dimension, Packet<?> packet) {
 | |
| 		for (int i = 0; i < this.players.size(); i++) {
 | |
| 			ServerPlayer serverPlayer = (ServerPlayer)this.players.get(i);
 | |
| 			if (serverPlayer != except && serverPlayer.level().dimension() == dimension) {
 | |
| 				double d = x - serverPlayer.getX();
 | |
| 				double e = y - serverPlayer.getY();
 | |
| 				double f = z - serverPlayer.getZ();
 | |
| 				if (d * d + e * e + f * f < radius * radius) {
 | |
| 					serverPlayer.connection.send(packet);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Saves all of the players' current states.
 | |
| 	 */
 | |
| 	public void saveAll() {
 | |
| 		for (int i = 0; i < this.players.size(); i++) {
 | |
| 			this.save((ServerPlayer)this.players.get(i));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public UserWhiteList getWhiteList() {
 | |
| 		return this.whitelist;
 | |
| 	}
 | |
| 
 | |
| 	public String[] getWhiteListNames() {
 | |
| 		return this.whitelist.getUserList();
 | |
| 	}
 | |
| 
 | |
| 	public ServerOpList getOps() {
 | |
| 		return this.ops;
 | |
| 	}
 | |
| 
 | |
| 	public String[] getOpNames() {
 | |
| 		return this.ops.getUserList();
 | |
| 	}
 | |
| 
 | |
| 	public void reloadWhiteList() {
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Updates the time and weather for the given player to those of the given world
 | |
| 	 */
 | |
| 	public void sendLevelInfo(ServerPlayer player, ServerLevel level) {
 | |
| 		WorldBorder worldBorder = this.server.overworld().getWorldBorder();
 | |
| 		player.connection.send(new ClientboundInitializeBorderPacket(worldBorder));
 | |
| 		player.connection.send(new ClientboundSetTimePacket(level.getGameTime(), level.getDayTime(), level.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)));
 | |
| 		player.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getSharedSpawnPos(), level.getSharedSpawnAngle()));
 | |
| 		if (level.isRaining()) {
 | |
| 			player.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.START_RAINING, 0.0F));
 | |
| 			player.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, level.getRainLevel(1.0F)));
 | |
| 			player.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, level.getThunderLevel(1.0F)));
 | |
| 		}
 | |
| 
 | |
| 		player.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.LEVEL_CHUNKS_LOAD_START, 0.0F));
 | |
| 		this.server.tickRateManager().updateJoiningPlayer(player);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sends the players inventory to himself.
 | |
| 	 */
 | |
| 	public void sendAllPlayerInfo(ServerPlayer player) {
 | |
| 		player.inventoryMenu.sendAllDataToRemote();
 | |
| 		player.resetSentInfo();
 | |
| 		player.connection.send(new ClientboundSetHeldSlotPacket(player.getInventory().getSelectedSlot()));
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the number of players currently on the server.
 | |
| 	 */
 | |
| 	public int getPlayerCount() {
 | |
| 		return this.players.size();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the maximum number of players allowed on the server.
 | |
| 	 */
 | |
| 	public int getMaxPlayers() {
 | |
| 		return this.maxPlayers;
 | |
| 	}
 | |
| 
 | |
| 	public boolean isUsingWhitelist() {
 | |
| 		return this.doWhiteList;
 | |
| 	}
 | |
| 
 | |
| 	public void setUsingWhiteList(boolean whitelistEnabled) {
 | |
| 		this.doWhiteList = whitelistEnabled;
 | |
| 	}
 | |
| 
 | |
| 	public List<ServerPlayer> getPlayersWithAddress(String address) {
 | |
| 		List<ServerPlayer> list = Lists.<ServerPlayer>newArrayList();
 | |
| 
 | |
| 		for (ServerPlayer serverPlayer : this.players) {
 | |
| 			if (serverPlayer.getIpAddress().equals(address)) {
 | |
| 				list.add(serverPlayer);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return list;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets the view distance, in chunks.
 | |
| 	 */
 | |
| 	public int getViewDistance() {
 | |
| 		return this.viewDistance;
 | |
| 	}
 | |
| 
 | |
| 	public int getSimulationDistance() {
 | |
| 		return this.simulationDistance;
 | |
| 	}
 | |
| 
 | |
| 	public MinecraftServer getServer() {
 | |
| 		return this.server;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * On integrated servers, returns the host's player data to be written to level.dat.
 | |
| 	 */
 | |
| 	@Nullable
 | |
| 	public CompoundTag getSingleplayerData() {
 | |
| 		return null;
 | |
| 	}
 | |
| 
 | |
| 	public void setAllowCommandsForAllPlayers(boolean allowCommandsForAllPlayers) {
 | |
| 		this.allowCommandsForAllPlayers = allowCommandsForAllPlayers;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Kicks everyone with "Server closed" as reason.
 | |
| 	 */
 | |
| 	public void removeAll() {
 | |
| 		for (int i = 0; i < this.players.size(); i++) {
 | |
| 			((ServerPlayer)this.players.get(i)).connection.disconnect(Component.translatable("multiplayer.disconnect.server_shutdown"));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void broadcastSystemMessage(Component message, boolean bypassHiddenChat) {
 | |
| 		this.broadcastSystemMessage(message, serverPlayer -> message, bypassHiddenChat);
 | |
| 	}
 | |
| 
 | |
| 	public void broadcastSystemMessage(Component serverMessage, Function<ServerPlayer, Component> playerMessageFactory, boolean bypassHiddenChat) {
 | |
| 		this.server.sendSystemMessage(serverMessage);
 | |
| 
 | |
| 		for (ServerPlayer serverPlayer : this.players) {
 | |
| 			Component component = (Component)playerMessageFactory.apply(serverPlayer);
 | |
| 			if (component != null) {
 | |
| 				serverPlayer.sendSystemMessage(component, bypassHiddenChat);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void broadcastChatMessage(PlayerChatMessage message, CommandSourceStack sender, ChatType.Bound boundChatType) {
 | |
| 		this.broadcastChatMessage(message, sender::shouldFilterMessageTo, sender.getPlayer(), boundChatType);
 | |
| 	}
 | |
| 
 | |
| 	public void broadcastChatMessage(PlayerChatMessage message, ServerPlayer sender, ChatType.Bound boundChatType) {
 | |
| 		this.broadcastChatMessage(message, sender::shouldFilterMessageTo, sender, boundChatType);
 | |
| 	}
 | |
| 
 | |
| 	private void broadcastChatMessage(
 | |
| 		PlayerChatMessage message, Predicate<ServerPlayer> shouldFilterMessageTo, @Nullable ServerPlayer sender, ChatType.Bound boundChatType
 | |
| 	) {
 | |
| 		boolean bl = this.verifyChatTrusted(message);
 | |
| 		this.server.logChatMessage(message.decoratedContent(), boundChatType, bl ? null : "Not Secure");
 | |
| 		OutgoingChatMessage outgoingChatMessage = OutgoingChatMessage.create(message);
 | |
| 		boolean bl2 = false;
 | |
| 
 | |
| 		for (ServerPlayer serverPlayer : this.players) {
 | |
| 			boolean bl3 = shouldFilterMessageTo.test(serverPlayer);
 | |
| 			serverPlayer.sendChatMessage(outgoingChatMessage, bl3, boundChatType);
 | |
| 			bl2 |= bl3 && message.isFullyFiltered();
 | |
| 		}
 | |
| 
 | |
| 		if (bl2 && sender != null) {
 | |
| 			sender.sendSystemMessage(CHAT_FILTERED_FULL);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private boolean verifyChatTrusted(PlayerChatMessage message) {
 | |
| 		return message.hasSignature() && !message.hasExpiredServer(Instant.now());
 | |
| 	}
 | |
| 
 | |
| 	public ServerStatsCounter getPlayerStats(Player player) {
 | |
| 		UUID uUID = player.getUUID();
 | |
| 		ServerStatsCounter serverStatsCounter = (ServerStatsCounter)this.stats.get(uUID);
 | |
| 		if (serverStatsCounter == null) {
 | |
| 			File file = this.server.getWorldPath(LevelResource.PLAYER_STATS_DIR).toFile();
 | |
| 			File file2 = new File(file, uUID + ".json");
 | |
| 			if (!file2.exists()) {
 | |
| 				File file3 = new File(file, player.getName().getString() + ".json");
 | |
| 				Path path = file3.toPath();
 | |
| 				if (FileUtil.isPathNormalized(path) && FileUtil.isPathPortable(path) && path.startsWith(file.getPath()) && file3.isFile()) {
 | |
| 					file3.renameTo(file2);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			serverStatsCounter = new ServerStatsCounter(this.server, file2);
 | |
| 			this.stats.put(uUID, serverStatsCounter);
 | |
| 		}
 | |
| 
 | |
| 		return serverStatsCounter;
 | |
| 	}
 | |
| 
 | |
| 	public PlayerAdvancements getPlayerAdvancements(ServerPlayer player) {
 | |
| 		UUID uUID = player.getUUID();
 | |
| 		PlayerAdvancements playerAdvancements = (PlayerAdvancements)this.advancements.get(uUID);
 | |
| 		if (playerAdvancements == null) {
 | |
| 			Path path = this.server.getWorldPath(LevelResource.PLAYER_ADVANCEMENTS_DIR).resolve(uUID + ".json");
 | |
| 			playerAdvancements = new PlayerAdvancements(this.server.getFixerUpper(), this, this.server.getAdvancements(), path, player);
 | |
| 			this.advancements.put(uUID, playerAdvancements);
 | |
| 		}
 | |
| 
 | |
| 		playerAdvancements.setPlayer(player);
 | |
| 		return playerAdvancements;
 | |
| 	}
 | |
| 
 | |
| 	public void setViewDistance(int viewDistance) {
 | |
| 		this.viewDistance = viewDistance;
 | |
| 		this.broadcastAll(new ClientboundSetChunkCacheRadiusPacket(viewDistance));
 | |
| 
 | |
| 		for (ServerLevel serverLevel : this.server.getAllLevels()) {
 | |
| 			if (serverLevel != null) {
 | |
| 				serverLevel.getChunkSource().setViewDistance(viewDistance);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void setSimulationDistance(int simulationDistance) {
 | |
| 		this.simulationDistance = simulationDistance;
 | |
| 		this.broadcastAll(new ClientboundSetSimulationDistancePacket(simulationDistance));
 | |
| 
 | |
| 		for (ServerLevel serverLevel : this.server.getAllLevels()) {
 | |
| 			if (serverLevel != null) {
 | |
| 				serverLevel.getChunkSource().setSimulationDistance(simulationDistance);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public List<ServerPlayer> getPlayers() {
 | |
| 		return this.players;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets the ServerPlayer object representing the player with the UUID.
 | |
| 	 */
 | |
| 	@Nullable
 | |
| 	public ServerPlayer getPlayer(UUID playerUUID) {
 | |
| 		return (ServerPlayer)this.playersByUUID.get(playerUUID);
 | |
| 	}
 | |
| 
 | |
| 	public boolean canBypassPlayerLimit(GameProfile profile) {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	public void reloadResources() {
 | |
| 		for (PlayerAdvancements playerAdvancements : this.advancements.values()) {
 | |
| 			playerAdvancements.reload(this.server.getAdvancements());
 | |
| 		}
 | |
| 
 | |
| 		this.broadcastAll(new ClientboundUpdateTagsPacket(TagNetworkSerialization.serializeTagsToNetwork(this.registries)));
 | |
| 		RecipeManager recipeManager = this.server.getRecipeManager();
 | |
| 		ClientboundUpdateRecipesPacket clientboundUpdateRecipesPacket = new ClientboundUpdateRecipesPacket(
 | |
| 			recipeManager.getSynchronizedItemProperties(), recipeManager.getSynchronizedStonecutterRecipes()
 | |
| 		);
 | |
| 
 | |
| 		for (ServerPlayer serverPlayer : this.players) {
 | |
| 			serverPlayer.connection.send(clientboundUpdateRecipesPacket);
 | |
| 			serverPlayer.getRecipeBook().sendInitialRecipeBook(serverPlayer);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public boolean isAllowCommandsForAllPlayers() {
 | |
| 		return this.allowCommandsForAllPlayers;
 | |
| 	}
 | |
| }
 |