aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.World/SpreadPlayersCommand.h
diff options
context:
space:
mode:
authordaoge <3523206925@qq.com>2026-03-03 03:04:10 +0800
committerGitHub <noreply@github.com>2026-03-03 03:04:10 +0800
commitb3feddfef372618c8a9d7a0abcaf18cfad866c18 (patch)
tree267761c3bb39241ba5c347bfbe2254d06686e287 /Minecraft.World/SpreadPlayersCommand.h
parent84c31a2331f7a0ec85b9d438992e244f60e5020f (diff)
feat: TU19 (Dec 2014) Features & Content (#155)
* try to resolve merge conflict * feat: TU19 (Dec 2014) Features & Content (#32) * December 2014 files * Working release build * Fix compilation issues * Add sound to Windows64Media * Add DLC content and force Tutorial DLC * Revert "Add DLC content and force Tutorial DLC" This reverts commit 97a43994725008e35fceb984d5549df9c8cea470. * Disable broken light packing * Disable breakpoint during DLC texture map load Allows DLC loading but the DLC textures are still broken * Fix post build not working * ... * fix vs2022 build * fix cmake build --------- Co-authored-by: Loki <lokirautio@gmail.com>
Diffstat (limited to 'Minecraft.World/SpreadPlayersCommand.h')
-rw-r--r--Minecraft.World/SpreadPlayersCommand.h328
1 files changed, 328 insertions, 0 deletions
diff --git a/Minecraft.World/SpreadPlayersCommand.h b/Minecraft.World/SpreadPlayersCommand.h
new file mode 100644
index 00000000..b8b451d6
--- /dev/null
+++ b/Minecraft.World/SpreadPlayersCommand.h
@@ -0,0 +1,328 @@
+/*
+package net.minecraft.commands.common;
+
+import java.util.*;
+
+import net.minecraft.commands.*;
+import net.minecraft.commands.exceptions.*;
+import net.minecraft.network.chat.ChatMessageComponent;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.util.Mth;
+import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.material.Material;
+import net.minecraft.world.level.tile.Tile;
+import net.minecraft.world.scores.Team;
+
+import com.google.common.collect.*;
+
+public class SpreadPlayersCommand extends BaseCommand {
+ private static final int MAX_ITERATION_COUNT = 10000;
+
+ @Override
+ public String getName() {
+ return "spreadplayers";
+ }
+
+ @Override
+ public int getPermissionLevel() {
+ return LEVEL_GAMEMASTERS;
+ }
+
+ @Override
+ public String getUsage(CommandSender source) {
+ return "commands.spreadplayers.usage";
+ }
+
+ @Override
+ public void execute(CommandSender source, String[] args) {
+ if (args.length < 6) throw new UsageException("commands.spreadplayers.usage");
+ int index = 0;
+ double x = convertArgToCoordinate(source, Double.NaN, args[index++]);
+ double z = convertArgToCoordinate(source, Double.NaN, args[index++]);
+ double minDist = convertArgToDouble(source, args[index++], 0);
+ double maxDist = convertArgToDouble(source, args[index++], minDist + 1);
+ boolean respectTeams = convertArgToBoolean(source, args[index++]);
+
+ List<LivingEntity> players = Lists.newArrayList();
+
+ while (index < args.length) {
+ String arg = args[index++];
+
+ if (PlayerSelector.isPattern(arg)) {
+ ServerPlayer[] result = PlayerSelector.getPlayers(source, arg);
+
+ if (result != null && result.length != 0) {
+ Collections.addAll(players, result);
+ } else {
+ throw new PlayerNotFoundException();
+ }
+ } else {
+ Player player = MinecraftServer.getInstance().getPlayers().getPlayer(arg);
+
+ if (player != null) {
+ players.add(player);
+ } else {
+ throw new PlayerNotFoundException();
+ }
+ }
+ }
+
+ if (players.isEmpty()) {
+ throw new PlayerNotFoundException();
+ }
+
+ source.sendMessage(ChatMessageComponent.forTranslation("commands.spreadplayers.spreading." + (respectTeams ? "teams" : "players"), joinPlayerNames(players), x, z, minDist, maxDist));
+
+ spreadPlayers(source, players, new Position(x, z), minDist, maxDist, players.get(0).level, respectTeams);
+ }
+
+ private void spreadPlayers(CommandSender source, List<LivingEntity> players, Position center, double spreadDist, double maxDistFromCenter, Level level, boolean respectTeams) {
+ Random random = new Random();
+ double minX = center.x - maxDistFromCenter;
+ double minZ = center.z - maxDistFromCenter;
+ double maxX = center.x + maxDistFromCenter;
+ double maxZ = center.z + maxDistFromCenter;
+
+ Position[] positions = createInitialPositions(random, respectTeams ? getNumberOfTeams(players) : players.size(), minX, minZ, maxX, maxZ);
+ int iterations = spreadPositions(center, spreadDist, level, random, minX, minZ, maxX, maxZ, positions, respectTeams);
+ double avgDistance = setPlayerPositions(players, level, positions, respectTeams);
+
+ logAdminAction(source, "commands.spreadplayers.success." + (respectTeams ? "teams" : "players"), positions.length, center.x, center.z);
+ if (positions.length > 1) source.sendMessage(ChatMessageComponent.forTranslation("commands.spreadplayers.info." + (respectTeams ? "teams" : "players"), String.format("%.2f", avgDistance),
+ iterations));
+ }
+
+ private int getNumberOfTeams(List<LivingEntity> players) {
+ Set<Team> teams = Sets.newHashSet();
+
+ for (LivingEntity player : players) {
+ if (player instanceof Player) {
+ teams.add(((Player) player).getTeam());
+ } else {
+ teams.add(null);
+ }
+ }
+
+ return teams.size();
+ }
+
+ private int spreadPositions(Position center, double spreadDist, Level level, Random random, double minX, double minZ, double maxX, double maxZ, Position[] positions, boolean respectTeams) {
+ boolean hasCollisions = true;
+ int iteration;
+ double minDistance = Float.MAX_VALUE;
+
+ for (iteration = 0; iteration < MAX_ITERATION_COUNT && hasCollisions; iteration++) {
+ hasCollisions = false;
+ minDistance = Float.MAX_VALUE;
+
+ for (int i = 0; i < positions.length; i++) {
+ Position position = positions[i];
+ int neighbourCount = 0;
+ Position averageNeighbourPos = new Position();
+
+ for (int j = 0; j < positions.length; j++) {
+ if (i == j) continue;
+ Position neighbour = positions[j];
+
+ double dist = position.dist(neighbour);
+ minDistance = Math.min(dist, minDistance);
+ if (dist < spreadDist) {
+ neighbourCount++;
+ averageNeighbourPos.x += neighbour.x - position.x;
+ averageNeighbourPos.z += neighbour.z - position.z;
+ }
+ }
+
+ if (neighbourCount > 0) {
+ averageNeighbourPos.x /= neighbourCount;
+ averageNeighbourPos.z /= neighbourCount;
+ double length = averageNeighbourPos.getLength();
+
+ if (length > 0) {
+ averageNeighbourPos.normalize();
+
+ position.moveAway(averageNeighbourPos);
+ } else {
+ position.randomize(random, minX, minZ, maxX, maxZ);
+ }
+
+ hasCollisions = true;
+ }
+
+ if (position.clamp(minX, minZ, maxX, maxZ)) {
+ hasCollisions = true;
+ }
+ }
+
+ if (!hasCollisions) {
+ for (Position position : positions) {
+ if (!position.isSafe(level)) {
+ position.randomize(random, minX, minZ, maxX, maxZ);
+ hasCollisions = true;
+ }
+ }
+ }
+ }
+
+ if (iteration >= MAX_ITERATION_COUNT) {
+ throw new CommandException("commands.spreadplayers.failure." + (respectTeams ? "teams" : "players"), positions.length, center.x, center.z, String.format("%.2f", minDistance));
+ }
+
+ return iteration;
+ }
+
+ private double setPlayerPositions(List<LivingEntity> players, Level level, Position[] positions, boolean respectTeams) {
+ double avgDistance = 0;
+ int positionIndex = 0;
+ Map<Team, Position> teamPositions = Maps.newHashMap();
+
+ for (int i = 0; i < players.size(); i++) {
+ LivingEntity player = players.get(i);
+ Position position;
+
+ if (respectTeams) {
+ Team team = player instanceof Player ? ((Player) player).getTeam() : null;
+
+ if (!teamPositions.containsKey(team)) {
+ teamPositions.put(team, positions[positionIndex++]);
+ }
+
+ position = teamPositions.get(team);
+ } else {
+ position = positions[positionIndex++];
+ }
+
+ player.teleportTo(Mth.floor(position.x) + 0.5f, position.getSpawnY(level), Mth.floor(position.z) + 0.5);
+
+ double closest = Double.MAX_VALUE;
+ for (int j = 0; j < positions.length; j++) {
+ if (position == positions[j]) continue;
+
+ double dist = position.dist(positions[j]);
+ closest = Math.min(dist, closest);
+ }
+ avgDistance += closest;
+ }
+
+ avgDistance /= players.size();
+ return avgDistance;
+ }
+
+ private Position[] createInitialPositions(Random random, int count, double minX, double minZ, double maxX, double maxZ) {
+ Position[] result = new Position[count];
+
+ for (int i = 0; i < result.length; i++) {
+ Position position = new Position();
+
+ position.randomize(random, minX, minZ, maxX, maxZ);
+
+ result[i] = position;
+ }
+
+ return result;
+ }
+
+ private static class Position {
+ double x;
+ double z;
+
+ Position() {
+ }
+
+ Position(double x, double z) {
+ this.x = x;
+ this.z = z;
+ }
+
+ void set(double x, double z) {
+ this.x = x;
+ this.z = z;
+ }
+
+ double dist(Position target) {
+ double dx = x - target.x;
+ double dz = z - target.z;
+
+ return Math.sqrt(dx * dx + dz * dz);
+ }
+
+ void normalize() {
+ double dist = (double) getLength();
+ x /= dist;
+ z /= dist;
+ }
+
+ float getLength() {
+ return Mth.sqrt(x * x + z * z);
+ }
+
+ public void moveAway(Position pos) {
+ x -= pos.x;
+ z -= pos.z;
+ }
+
+ public boolean clamp(double minX, double minZ, double maxX, double maxZ) {
+ boolean changed = false;
+
+ if (x < minX) {
+ x = minX;
+ changed = true;
+ } else if (x > maxX) {
+ x = maxX;
+ changed = true;
+ }
+
+ if (z < minZ) {
+ z = minZ;
+ changed = true;
+ } else if (z > maxZ) {
+ z = maxZ;
+ changed = true;
+ }
+
+ return changed;
+ }
+
+ public int getSpawnY(Level level) {
+ int xt = Mth.floor(x);
+ int zt = Mth.floor(z);
+
+ for (int y = Level.maxBuildHeight; y > 0; y--) {
+ int tile = level.getTile(xt, y, zt);
+
+ if (tile != 0) {
+ return y + 1;
+ }
+ }
+
+ return Level.maxBuildHeight + 1;
+ }
+
+ public boolean isSafe(Level level) {
+ int xt = Mth.floor(x);
+ int zt = Mth.floor(z);
+
+ for (int y = Level.maxBuildHeight; y > 0; y--) {
+ int tile = level.getTile(xt, y, zt);
+
+ if (tile != 0) {
+ Material material = Tile.tiles[tile].material;
+
+ return !material.isLiquid() && material != Material.fire;
+ }
+ }
+
+ return false;
+ }
+
+ public void randomize(Random random, double minX, double minZ, double maxX, double maxZ) {
+ x = Mth.nextDouble(random, minX, maxX);
+ z = Mth.nextDouble(random, minZ, maxZ);
+ }
+ }
+}
+
+*/ \ No newline at end of file