HyCodeYourTale

Spawn System

Spawn System

Dokumentace spawn systému pro nastavení výchozích bodů hráčů.

---

Přehled

Spawn systém v Hytale používá ISpawnProvider interface pro definování spawn bodů. Každý svět má svůj spawn provider v WorldConfig.

---

ISpawnProvider Interface

Z Dekompilovaného Kódu

public interface ISpawnProvider {
@Nonnull
BuilderCodecMapCodec CODEC;

// Hlavní metoda - získej spawn pro konkrétního hráče
Transform getSpawnPoint(@Nonnull World world, @Nonnull UUID playerUuid);

// Všechny spawn pointy (deprecated)
@Deprecated
Transform[] getSpawnPoints();

// Kontrola vzdálenosti od spawnu
boolean isWithinSpawnDistance(@Nonnull Vector3d position, double distance);

// Pomocné metody s komponentami
default Transform getSpawnPoint(@Nonnull Ref ref, @Nonnull ComponentAccessor accessor) {
UUIDComponent uuidComponent = accessor.getComponent(ref, UUIDComponent.getComponentType());
World world = accessor.getExternalData().getWorld();
return this.getSpawnPoint(world, uuidComponent.getUuid());
}
}

---

Získání Spawn Bodu

Z WorldConfig

// Získání spawn provideru
World world = Universe.get().getWorld("overworld");
WorldConfig worldConfig = world.getWorldConfig();
ISpawnProvider spawnProvider = worldConfig.getSpawnProvider();

// Spawn pro konkrétního hráče
UUID playerUuid = playerRef.getUuid();
Transform spawnPoint = spawnProvider.getSpawnPoint(world, playerUuid);

Vector3d position = spawnPoint.getPosition();
Vector3f rotation = spawnPoint.getRotation();

Všechny Spawn Pointy

// Získej pole všech spawn pointů
Transform[] spawnPoints = spawnProvider.getSpawnPoints();

// Iterace přes spawny
for (int i = 0; i < spawnPoints.length; i++) {
Transform spawn = spawnPoints[i];
getLogger().atInfo().log("Spawn %d: %s", i, spawn.getPosition());
}

Spawn podle Indexu (z SpawnCommand)

private static Transform resolveSpawn(
@Nonnull CommandContext context,
@Nonnull World world,
@Nonnull PlayerRef playerRef,
@Nonnull OptionalArg spawnIndexArg
) {
ISpawnProvider spawnProvider = world.getWorldConfig().getSpawnProvider();

if (spawnIndexArg.provided(context)) {
int spawnIndex = spawnIndexArg.get(context);
Transform[] spawnPoints = spawnProvider.getSpawnPoints();

if (spawnIndex >= 0 && spawnIndex < spawnPoints.length) {
return spawnPoints[spawnIndex];
} else {
int maxIndex = spawnPoints.length - 1;
context.sendMessage(
Message.translation("server.commands.spawn.indexNotFound")
.param("maxIndex", maxIndex)
);
throw new GeneralCommandException(
Message.translation("server.commands.errors.spawnIndexOutOfRange")
.param("index", spawnIndex)
.param("maxIndex", maxIndex)
);
}
} else {
// Výchozí spawn na základě UUID hráče
return spawnProvider.getSpawnPoint(world, playerRef.getUuid());
}
}

---

SpawnUtil

Utility třída pro práci se spawn body.

Z Dekompilovaného Kódu

public final class SpawnUtil {

// Aplikace spawn transformu na nového hráče
@Nullable
public static TransformComponent applyFirstSpawnTransform(
@Nonnull Holder holder,
@Nonnull World world,
@Nonnull WorldConfig worldConfig,
@Nonnull UUID playerUuid
) {
ISpawnProvider spawnProvider = worldConfig.getSpawnProvider();
if (spawnProvider == null) {
return null;
}

Transform spawnPoint = spawnProvider.getSpawnPoint(world, playerUuid);

// Body rotation má jen yaw
Vector3f bodyRotation = new Vector3f(0.0F, spawnPoint.getRotation().getYaw(), 0.0F);

TransformComponent transformComponent = new TransformComponent(
spawnPoint.getPosition(),
bodyRotation
);
holder.addComponent(TransformComponent.getComponentType(), transformComponent);

// Head rotation má plnou rotaci
HeadRotation headRotationComponent = holder.ensureAndGetComponent(HeadRotation.getComponentType());
headRotationComponent.teleportRotation(spawnPoint.getRotation());

return transformComponent;
}

// Aplikace transformu na existující holder
public static void applyTransform(@Nonnull Holder holder, @Nonnull Transform transform) {
TransformComponent transformComponent = holder.getComponent(TransformComponent.getComponentType());

transformComponent.setPosition(transform.getPosition());
transformComponent.getRotation().setYaw(transform.getRotation().getYaw());

HeadRotation headRotationComponent = holder.ensureAndGetComponent(HeadRotation.getComponentType());
headRotationComponent.teleportRotation(transform.getRotation());
}
}

---

Implementace Spawn Providerů

GlobalSpawnProvider

Jeden globální spawn pro všechny hráče.

public class GlobalSpawnProvider implements ISpawnProvider {
private final Transform spawnPoint;

public GlobalSpawnProvider(Transform spawnPoint) {
this.spawnPoint = spawnPoint;
}

@Override
public Transform getSpawnPoint(@Nonnull World world, @Nonnull UUID playerUuid) {
return this.spawnPoint;
}

@Override
public Transform[] getSpawnPoints() {
return new Transform[] { this.spawnPoint };
}

@Override
public boolean isWithinSpawnDistance(@Nonnull Vector3d position, double distance) {
return this.spawnPoint.getPosition().distance(position) <= distance;
}
}

IndividualSpawnProvider

Různé spawn body pro různé hráče (např. round-robin).

public class IndividualSpawnProvider implements ISpawnProvider {
private final Transform[] spawnPoints;

public IndividualSpawnProvider(Transform[] spawnPoints) {
this.spawnPoints = spawnPoints;
}

@Override
public Transform getSpawnPoint(@Nonnull World world, @Nonnull UUID playerUuid) {
// Hash UUID pro konzistentní přidělení
int index = Math.abs(playerUuid.hashCode()) % this.spawnPoints.length;
return this.spawnPoints[index];
}

@Override
public Transform[] getSpawnPoints() {
return this.spawnPoints;
}

@Override
public boolean isWithinSpawnDistance(@Nonnull Vector3d position, double distance) {
for (Transform spawn : this.spawnPoints) {
if (spawn.getPosition().distance(position) <= distance) {
return true;
}
}
return false;
}
}

FitToHeightMapSpawnProvider

Spawn s automatickým přizpůsobením výšce terénu.

public class FitToHeightMapSpawnProvider implements ISpawnProvider {
private final ISpawnProvider delegate;

public FitToHeightMapSpawnProvider(ISpawnProvider delegate) {
this.delegate = delegate;
}

@Override
public Transform getSpawnPoint(@Nonnull World world, @Nonnull UUID playerUuid) {
Transform baseSpawn = this.delegate.getSpawnPoint(world, playerUuid);
Vector3d position = baseSpawn.getPosition();

// Získej výšku terénu na dané pozici
int groundY = world.getHighestBlockY((int)position.x, (int)position.z);

// Přizpůsob Y souřadnici
Vector3d adjustedPosition = new Vector3d(position.x, groundY + 1, position.z);

return new Transform(adjustedPosition, baseSpawn.getRotation());
}
}

---

WorldSpawnPoint Asset

Spawn body z asset systému.

public class WorldSpawnPoint {
public static final Codec CODEC = ...;

private Transform transform;
private String name;
private boolean isDefault;

public Transform getTransform() { return this.transform; }
public String getName() { return this.name; }
public boolean isDefault() { return this.isDefault; }
}

---

Spawn Command

Základní /spawn Příkaz

public class SpawnCommand extends AbstractPlayerCommand {
@Nonnull
private final OptionalArg spawnIndexArg = this.withOptionalArg(
"spawnIndex",
"server.commands.spawn.index.desc",
ArgTypes.INTEGER
);

public SpawnCommand() {
super("spawn", "server.commands.spawn.desc");
this.requirePermission(HytalePermissions.fromCommand("spawn.self"));
this.addUsageVariant(new SpawnOtherCommand());
this.addSubCommand(new SpawnSetCommand());
this.addSubCommand(new SpawnSetDefaultCommand());
}

@Override
protected void execute(
@Nonnull CommandContext context,
@Nonnull Store store,
@Nonnull Ref ref,
@Nonnull PlayerRef playerRef,
@Nonnull World world
) {
Transform spawnTransform = resolveSpawn(context, world, playerRef, this.spawnIndexArg);

// Uložení historie
TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType());
HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType());

Vector3d previousPos = transformComponent.getPosition().clone();
Vector3f previousRotation = headRotationComponent.getRotation().clone();

TeleportHistory teleportHistoryComponent = store.ensureAndGetComponent(ref, TeleportHistory.getComponentType());
teleportHistoryComponent.append(world, previousPos, previousRotation, "World " + world.getName() + "'s spawn");

// Teleportace
Teleport teleportComponent = Teleport.createForPlayer(world, spawnTransform);
store.addComponent(ref, Teleport.getComponentType(), teleportComponent);

// Zpráva
Vector3d position = spawnTransform.getPosition();
context.sendMessage(
Message.translation("server.commands.spawn.teleported")
.param("x", position.getX())
.param("y", position.getY())
.param("z", position.getZ())
);
}
}

Teleportace Jiného Hráče na Spawn

private static class SpawnOtherCommand extends CommandBase {
private static final Message MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_IN_WORLD =
Message.translation("server.commands.errors.playerNotInWorld");

@Nonnull
private final RequiredArg playerArg = this.withRequiredArg(
"player",
"server.commands.argtype.player.desc",
ArgTypes.PLAYER_REF
);

@Nonnull
private final OptionalArg spawnIndexArg = this.withOptionalArg(
"spawnIndex",
"server.commands.spawn.index.desc",
ArgTypes.INTEGER
);

SpawnOtherCommand() {
super("server.commands.spawn.other.desc");
this.requirePermission(HytalePermissions.fromCommand("spawn.other"));
}

@Override
protected void executeSync(@Nonnull CommandContext context) {
PlayerRef targetPlayerRef = this.playerArg.get(context);
Ref ref = targetPlayerRef.getReference();

if (ref == null || !ref.isValid()) {
context.sendMessage(MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_IN_WORLD);
return;
}

Store store = ref.getStore();
World world = store.getExternalData().getWorld();

world.execute(() -> {
Player playerComponent = store.getComponent(ref, Player.getComponentType());
if (playerComponent == null) {
context.sendMessage(MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_IN_WORLD);
return;
}

Transform spawn = SpawnCommand.resolveSpawn(context, world, targetPlayerRef, this.spawnIndexArg);

// Uložení historie
TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType());
HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType());

Vector3d previousPos = transformComponent.getPosition().clone();
Vector3f previousRotation = headRotationComponent.getRotation().clone();

TeleportHistory history = store.ensureAndGetComponent(ref, TeleportHistory.getComponentType());
history.append(world, previousPos, previousRotation, "World " + world.getName() + "'s spawn");

// Teleportace
Teleport teleport = Teleport.createForPlayer(world, spawn);
store.addComponent(ref, Teleport.getComponentType(), teleport);

// Zpráva
Vector3d position = spawn.getPosition();
context.sendMessage(
Message.translation("server.commands.spawn.teleportedOther")
.param("username", targetPlayerRef.getUsername())
.param("x", position.getX())
.param("y", position.getY())
.param("z", position.getZ())
);
});
}
}

---

Respawn Systém

HomeOrSpawnPoint

Respawn na domov nebo výchozí spawn.

public class HomeOrSpawnPoint {
public static final Codec CODEC = ...;

private boolean useHome;
private ISpawnProvider fallbackSpawn;

public Transform getRespawnPoint(World world, UUID playerUuid, @Nullable Transform homePoint) {
if (this.useHome && homePoint != null) {
return homePoint;
}
return this.fallbackSpawn.getSpawnPoint(world, playerUuid);
}
}

---

Vlastní Spawn Provider

Příklad: Team-Based Spawn

public class TeamSpawnProvider implements ISpawnProvider {
private final Map teamSpawns;
private final Transform defaultSpawn;
private final Function teamResolver;

public TeamSpawnProvider(
Map teamSpawns,
Transform defaultSpawn,
Function teamResolver
) {
this.teamSpawns = teamSpawns;
this.defaultSpawn = defaultSpawn;
this.teamResolver = teamResolver;
}

@Override
public Transform getSpawnPoint(@Nonnull World world, @Nonnull UUID playerUuid) {
String team = teamResolver.apply(playerUuid);
if (team != null && teamSpawns.containsKey(team)) {
return teamSpawns.get(team);
}
return defaultSpawn;
}

@Override
public Transform[] getSpawnPoints() {
return teamSpawns.values().toArray(new Transform[0]);
}

@Override
public boolean isWithinSpawnDistance(@Nonnull Vector3d position, double distance) {
for (Transform spawn : teamSpawns.values()) {
if (spawn.getPosition().distance(position) <= distance) {
return true;
}
}
return defaultSpawn.getPosition().distance(position) <= distance;
}
}

---

Shrnutí

| Operace | Metoda |
|---------|--------|
| Získání spawn provideru | world.getWorldConfig().getSpawnProvider() |
| Spawn pro hráče | spawnProvider.getSpawnPoint(world, uuid) |
| Všechny spawn body | spawnProvider.getSpawnPoints() |
| Kontrola vzdálenosti | spawnProvider.isWithinSpawnDistance(pos, distance) |
| První spawn hráče | SpawnUtil.applyFirstSpawnTransform(holder, world, config, uuid) |
| Aplikace transformu | SpawnUtil.applyTransform(holder, transform) |

| Spawn Provider | Popis |
|----------------|-------|
| GlobalSpawnProvider | Jeden spawn pro všechny |
| IndividualSpawnProvider | Různé spawny pro různé hráče |
| FitToHeightMapSpawnProvider | Přizpůsobení výšce terénu |

Last updated: 20. ledna 2026