SpreadPlayersCommand.java
net.minecraft.server.commands.SpreadPlayersCommand
信息
- 全限定名:net.minecraft.server.commands.SpreadPlayersCommand
- 类型:public class
- 包:net.minecraft.server.commands
- 源码路径:src/main/java/net/minecraft/server/commands/SpreadPlayersCommand.java
- 起始行号:L34
- 职责:
TODO
字段/常量
-
MAX_ITERATION_COUNT- 类型:
int - 修饰符:
private static final - 源码定位:
L35 - 说明:
TODO
- 类型:
-
ERROR_FAILED_TO_SPREAD_TEAMS- 类型:
Dynamic4CommandExceptionType - 修饰符:
private static final - 源码定位:
L36 - 说明:
TODO
- 类型:
-
ERROR_FAILED_TO_SPREAD_ENTITIES- 类型:
Dynamic4CommandExceptionType - 修饰符:
private static final - 源码定位:
L39 - 说明:
TODO
- 类型:
-
ERROR_INVALID_MAX_HEIGHT- 类型:
Dynamic2CommandExceptionType - 修饰符:
private static final - 源码定位:
L42 - 说明:
TODO
- 类型:
内部类/嵌套类型
net.minecraft.server.commands.SpreadPlayersCommand.Position- 类型:
class - 修饰符:
private static - 源码定位:
L292 - 说明:
TODO
- 类型:
构造器
- 无
方法
下面的方法块按源码顺序生成。
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) @ L46
- 方法名:register
- 源码定位:L46
- 返回类型:void
- 修饰符:public static
参数:
- dispatcher: CommandDispatcher
说明:
TODO
private static int spreadPlayers(CommandSourceStack source, Vec2 center, float spreadDistance, float maxRange, int maxHeight, boolean respectTeams, Collection<?extends Entity> entities) @ L102
- 方法名:spreadPlayers
- 源码定位:L102
- 返回类型:int
- 修饰符:private static
参数:
- source: CommandSourceStack
- center: Vec2
- spreadDistance: float
- maxRange: float
- maxHeight: int
- respectTeams: boolean
- entities: Collection<?extends Entity>
说明:
TODO
private static int getNumberOfTeams(Collection<?extends Entity> players) @ L140
- 方法名:getNumberOfTeams
- 源码定位:L140
- 返回类型:int
- 修饰符:private static
参数:
- players: Collection<?extends Entity>
说明:
TODO
private static void spreadPositions(Vec2 center, double spreadDist, ServerLevel level, RandomSource random, double minX, double minZ, double maxX, double maxZ, int maxHeight, SpreadPlayersCommand.Position[] positions, boolean respectTeams) @ L154
- 方法名:spreadPositions
- 源码定位:L154
- 返回类型:void
- 修饰符:private static
参数:
- center: Vec2
- spreadDist: double
- level: ServerLevel
- random: RandomSource
- minX: double
- minZ: double
- maxX: double
- maxZ: double
- maxHeight: int
- positions: SpreadPlayersCommand.Position[]
- respectTeams: boolean
说明:
TODO
private static double setPlayerPositions(Collection<?extends Entity> entities, ServerLevel level, SpreadPlayersCommand.Position[] positions, int maxHeight, boolean respectTeams) @ L235
- 方法名:setPlayerPositions
- 源码定位:L235
- 返回类型:double
- 修饰符:private static
参数:
- entities: Collection<?extends Entity>
- level: ServerLevel
- positions: SpreadPlayersCommand.Position[]
- maxHeight: int
- respectTeams: boolean
说明:
TODO
private static SpreadPlayersCommand.Position[] createInitialPositions(RandomSource random, int count, double minX, double minZ, double maxX, double maxZ) @ L280
- 方法名:createInitialPositions
- 源码定位:L280
- 返回类型:SpreadPlayersCommand.Position[]
- 修饰符:private static
参数:
- random: RandomSource
- count: int
- minX: double
- minZ: double
- maxX: double
- maxZ: double
说明:
TODO
代码
public class SpreadPlayersCommand {
private static final int MAX_ITERATION_COUNT = 10000;
private static final Dynamic4CommandExceptionType ERROR_FAILED_TO_SPREAD_TEAMS = new Dynamic4CommandExceptionType(
(count, x, z, recommended) -> Component.translatableEscape("commands.spreadplayers.failed.teams", count, x, z, recommended)
);
private static final Dynamic4CommandExceptionType ERROR_FAILED_TO_SPREAD_ENTITIES = new Dynamic4CommandExceptionType(
(count, x, z, recommended) -> Component.translatableEscape("commands.spreadplayers.failed.entities", count, x, z, recommended)
);
private static final Dynamic2CommandExceptionType ERROR_INVALID_MAX_HEIGHT = new Dynamic2CommandExceptionType(
(suppliedMaxHeight, worldMinHeight) -> Component.translatableEscape("commands.spreadplayers.failed.invalid.height", suppliedMaxHeight, worldMinHeight)
);
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(
Commands.literal("spreadplayers")
.requires(Commands.hasPermission(Commands.LEVEL_GAMEMASTERS))
.then(
Commands.argument("center", Vec2Argument.vec2())
.then(
Commands.argument("spreadDistance", FloatArgumentType.floatArg(0.0F))
.then(
Commands.argument("maxRange", FloatArgumentType.floatArg(1.0F))
.then(
Commands.argument("respectTeams", BoolArgumentType.bool())
.then(
Commands.argument("targets", EntityArgument.entities())
.executes(
c -> spreadPlayers(
c.getSource(),
Vec2Argument.getVec2(c, "center"),
FloatArgumentType.getFloat(c, "spreadDistance"),
FloatArgumentType.getFloat(c, "maxRange"),
c.getSource().getLevel().getMaxY() + 1,
BoolArgumentType.getBool(c, "respectTeams"),
EntityArgument.getEntities(c, "targets")
)
)
)
)
.then(
Commands.literal("under")
.then(
Commands.argument("maxHeight", IntegerArgumentType.integer())
.then(
Commands.argument("respectTeams", BoolArgumentType.bool())
.then(
Commands.argument("targets", EntityArgument.entities())
.executes(
c -> spreadPlayers(
c.getSource(),
Vec2Argument.getVec2(c, "center"),
FloatArgumentType.getFloat(c, "spreadDistance"),
FloatArgumentType.getFloat(c, "maxRange"),
IntegerArgumentType.getInteger(c, "maxHeight"),
BoolArgumentType.getBool(c, "respectTeams"),
EntityArgument.getEntities(c, "targets")
)
)
)
)
)
)
)
)
)
);
}
private static int spreadPlayers(
CommandSourceStack source,
Vec2 center,
float spreadDistance,
float maxRange,
int maxHeight,
boolean respectTeams,
Collection<? extends Entity> entities
) throws CommandSyntaxException {
ServerLevel level = source.getLevel();
int minY = level.getMinY();
if (maxHeight < minY) {
throw ERROR_INVALID_MAX_HEIGHT.create(maxHeight, minY);
} else {
RandomSource random = RandomSource.createThreadLocalInstance();
double minX = center.x - maxRange;
double minZ = center.y - maxRange;
double maxX = center.x + maxRange;
double maxZ = center.y + maxRange;
SpreadPlayersCommand.Position[] positions = createInitialPositions(
random, respectTeams ? getNumberOfTeams(entities) : entities.size(), minX, minZ, maxX, maxZ
);
spreadPositions(center, spreadDistance, level, random, minX, minZ, maxX, maxZ, maxHeight, positions, respectTeams);
double distance = setPlayerPositions(entities, level, positions, maxHeight, respectTeams);
source.sendSuccess(
() -> Component.translatable(
"commands.spreadplayers.success." + (respectTeams ? "teams" : "entities"),
positions.length,
center.x,
center.y,
String.format(Locale.ROOT, "%.2f", distance)
),
true
);
return positions.length;
}
}
private static int getNumberOfTeams(Collection<? extends Entity> players) {
Set<Team> teams = Sets.newHashSet();
for (Entity player : players) {
if (player instanceof Player) {
teams.add(player.getTeam());
} else {
teams.add(null);
}
}
return teams.size();
}
private static void spreadPositions(
Vec2 center,
double spreadDist,
ServerLevel level,
RandomSource random,
double minX,
double minZ,
double maxX,
double maxZ,
int maxHeight,
SpreadPlayersCommand.Position[] positions,
boolean respectTeams
) throws CommandSyntaxException {
boolean hasCollisions = true;
double minDistance = Float.MAX_VALUE;
int iteration;
for (iteration = 0; iteration < 10000 && hasCollisions; iteration++) {
hasCollisions = false;
minDistance = Float.MAX_VALUE;
for (int i = 0; i < positions.length; i++) {
SpreadPlayersCommand.Position position = positions[i];
int neighbourCount = 0;
SpreadPlayersCommand.Position averageNeighbourPos = new SpreadPlayersCommand.Position();
for (int j = 0; j < positions.length; j++) {
if (i != j) {
SpreadPlayersCommand.Position neighbour = positions[j];
double dist = position.dist(neighbour);
minDistance = Math.min(dist, minDistance);
if (dist < spreadDist) {
neighbourCount++;
averageNeighbourPos.x = averageNeighbourPos.x + (neighbour.x - position.x);
averageNeighbourPos.z = averageNeighbourPos.z + (neighbour.z - position.z);
}
}
}
if (neighbourCount > 0) {
averageNeighbourPos.x /= neighbourCount;
averageNeighbourPos.z /= neighbourCount;
double length = averageNeighbourPos.getLength();
if (length > 0.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 (SpreadPlayersCommand.Position position : positions) {
if (!position.isSafe(level, maxHeight)) {
position.randomize(random, minX, minZ, maxX, maxZ);
hasCollisions = true;
}
}
}
}
if (minDistance == Float.MAX_VALUE) {
minDistance = 0.0;
}
if (iteration >= 10000) {
if (respectTeams) {
throw ERROR_FAILED_TO_SPREAD_TEAMS.create(positions.length, center.x, center.y, String.format(Locale.ROOT, "%.2f", minDistance));
} else {
throw ERROR_FAILED_TO_SPREAD_ENTITIES.create(positions.length, center.x, center.y, String.format(Locale.ROOT, "%.2f", minDistance));
}
}
}
private static double setPlayerPositions(
Collection<? extends Entity> entities, ServerLevel level, SpreadPlayersCommand.Position[] positions, int maxHeight, boolean respectTeams
) {
double avgDistance = 0.0;
int positionIndex = 0;
Map<Team, SpreadPlayersCommand.Position> teamPositions = Maps.newHashMap();
for (Entity entity : entities) {
SpreadPlayersCommand.Position position;
if (respectTeams) {
Team team = entity instanceof Player ? entity.getTeam() : null;
if (!teamPositions.containsKey(team)) {
teamPositions.put(team, positions[positionIndex++]);
}
position = teamPositions.get(team);
} else {
position = positions[positionIndex++];
}
entity.teleportTo(
level,
Mth.floor(position.x) + 0.5,
position.getSpawnY(level, maxHeight),
Mth.floor(position.z) + 0.5,
Set.of(),
entity.getYRot(),
entity.getXRot(),
true
);
double closest = Double.MAX_VALUE;
for (SpreadPlayersCommand.Position testPosition : positions) {
if (position != testPosition) {
double dist = position.dist(testPosition);
closest = Math.min(dist, closest);
}
}
avgDistance += closest;
}
return entities.size() < 2 ? 0.0 : avgDistance / entities.size();
}
private static SpreadPlayersCommand.Position[] createInitialPositions(RandomSource random, int count, double minX, double minZ, double maxX, double maxZ) {
SpreadPlayersCommand.Position[] result = new SpreadPlayersCommand.Position[count];
for (int i = 0; i < result.length; i++) {
SpreadPlayersCommand.Position position = new SpreadPlayersCommand.Position();
position.randomize(random, minX, minZ, maxX, maxZ);
result[i] = position;
}
return result;
}
private static class Position {
private double x;
private double z;
double dist(SpreadPlayersCommand.Position target) {
double dx = this.x - target.x;
double dz = this.z - target.z;
return Math.sqrt(dx * dx + dz * dz);
}
void normalize() {
double dist = this.getLength();
this.x /= dist;
this.z /= dist;
}
double getLength() {
return Math.sqrt(this.x * this.x + this.z * this.z);
}
public void moveAway(SpreadPlayersCommand.Position pos) {
this.x = this.x - pos.x;
this.z = this.z - pos.z;
}
public boolean clamp(double minX, double minZ, double maxX, double maxZ) {
boolean changed = false;
if (this.x < minX) {
this.x = minX;
changed = true;
} else if (this.x > maxX) {
this.x = maxX;
changed = true;
}
if (this.z < minZ) {
this.z = minZ;
changed = true;
} else if (this.z > maxZ) {
this.z = maxZ;
changed = true;
}
return changed;
}
public int getSpawnY(BlockGetter level, int maxHeight) {
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(this.x, (double)(maxHeight + 1), this.z);
boolean air2Above = level.getBlockState(pos).isAir();
pos.move(Direction.DOWN);
boolean air1Above = level.getBlockState(pos).isAir();
while (pos.getY() > level.getMinY()) {
pos.move(Direction.DOWN);
boolean currentIsAir = level.getBlockState(pos).isAir();
if (!currentIsAir && air1Above && air2Above) {
return pos.getY() + 1;
}
air2Above = air1Above;
air1Above = currentIsAir;
}
return maxHeight + 1;
}
public boolean isSafe(BlockGetter level, int maxHeight) {
BlockPos pos = BlockPos.containing(this.x, this.getSpawnY(level, maxHeight) - 1, this.z);
BlockState state = level.getBlockState(pos);
return pos.getY() < maxHeight && !state.liquid() && !state.is(BlockTags.FIRE);
}
public void randomize(RandomSource random, double minX, double minZ, double maxX, double maxZ) {
this.x = Mth.nextDouble(random, minX, maxX);
this.z = Mth.nextDouble(random, minZ, maxZ);
}
}
}引用的其他类
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Commands.argument(), Commands.hasPermission(), Commands.literal()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
EntityArgument.entities(), EntityArgument.getEntities()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Vec2Argument.getVec2(), Vec2Argument.vec2()
- 引用位置:
-
- 引用位置:
方法调用/构造调用 - 关联成员:
BlockPos.MutableBlockPos(), BlockPos.containing(), MutableBlockPos()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Component.translatable(), Component.translatableEscape()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Mth.floor(), Mth.nextDouble()
- 引用位置:
-
- 引用位置:
参数/方法调用 - 关联成员:
RandomSource.createThreadLocalInstance()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置: