package com.hypixel.hytale.builtin.creativehub.interactions;
import com.hypixel.hytale.builtin.creativehub.CreativeHubPlugin;
import com.hypixel.hytale.builtin.creativehub.config.CreativeHubEntityConfig;
import com.hypixel.hytale.builtin.creativehub.config.CreativeHubWorldConfig;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.protocol.InteractionType;
import com.hypixel.hytale.protocol.WaitForDataFrom;
import com.hypixel.hytale.server.core.entity.InteractionContext;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.entity.entities.player.data.PlayerWorldData;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.teleport.PendingTeleport;
import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport;
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.WorldConfig;
import com.hypixel.hytale.server.core.universe.world.spawn.ISpawnProvider;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class HubPortalInteraction extends SimpleInstantInteraction {
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
public static final BuilderCodec<HubPortalInteraction> CODEC;
private String worldName;
private String worldGenType;
@Nullable
private String instanceTemplate;
public HubPortalInteraction() {
}
@Nonnull
public WaitForDataFrom getWaitForDataFrom() {
return WaitForDataFrom.Server;
}
protected void firstRun(@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nonnull CooldownHandler cooldownHandler) {
CommandBuffer<EntityStore> commandBuffer = context.getCommandBuffer();
Ref<EntityStore> ref = context.getEntity();
Player playerComponent = (Player)commandBuffer.getComponent(ref, Player.getComponentType());
if (playerComponent != null && !playerComponent.isWaitingForClientReady()) {
Archetype<EntityStore> archetype = commandBuffer.getArchetype(ref);
if (!archetype.contains(Teleport.getComponentType()) && !archetype.contains(PendingTeleport.getComponentType())) {
World currentWorld = ((EntityStore)commandBuffer.getExternalData()).getWorld();
Universe universe = Universe.get();
World targetWorld = universe.getWorld(this.worldName);
if (targetWorld != null) {
this.teleportToLoadedWorld(ref, commandBuffer, targetWorld, playerComponent);
} else {
CompletableFuture<World> worldFuture;
if (this.instanceTemplate != null) {
worldFuture = CreativeHubPlugin.get().spawnPermanentWorldFromTemplate(this.instanceTemplate, this.worldName);
} else if (universe.isWorldLoadable(this.worldName)) {
worldFuture = universe.loadWorld(this.worldName);
} else {
worldFuture = universe.addWorld(this.worldName, this.worldGenType, (String)null);
worldFuture.thenAccept((world) -> {
if (world.getWorldConfig().getDisplayName() == null) {
world.getWorldConfig().setDisplayName(WorldConfig.formatDisplayName(this.worldName));
}
});
}
this.teleportToLoadingWorld(ref, commandBuffer, worldFuture, currentWorld, playerComponent);
}
}
}
}
private void teleportToLoadedWorld(@Nonnull Ref<EntityStore> playerRef, @Nonnull ComponentAccessor<EntityStore> componentAccessor, @Nonnull World targetWorld, @Nonnull Player playerComponent) {
Map<String, PlayerWorldData> perWorldData = playerComponent.getPlayerConfigData().getPerWorldData();
PlayerWorldData worldData = (PlayerWorldData)perWorldData.get(targetWorld.getName());
Transform spawnPoint;
if (worldData != null && worldData.getLastPosition() != null) {
spawnPoint = worldData.getLastPosition();
} else {
UUIDComponent uuidComponent = (UUIDComponent)componentAccessor.getComponent(playerRef, UUIDComponent.getComponentType());
assert uuidComponent != null;
ISpawnProvider spawnProvider = targetWorld.getWorldConfig().getSpawnProvider();
spawnPoint = spawnProvider != null ? spawnProvider.getSpawnPoint(targetWorld, uuidComponent.getUuid()) : new Transform();
}
Teleport teleportComponent = Teleport.createForPlayer(targetWorld, spawnPoint);
componentAccessor.addComponent(playerRef, Teleport.getComponentType(), teleportComponent);
}
private void teleportToLoadingWorld(@Nonnull Ref<EntityStore> playerRef, @Nonnull ComponentAccessor<EntityStore> componentAccessor, @Nonnull CompletableFuture<World> worldFuture, @Nonnull World originalWorld, @Nonnull Player playerComponent) {
TransformComponent transformComponent = (TransformComponent)componentAccessor.getComponent(playerRef, TransformComponent.getComponentType());
assert transformComponent != null;
Transform originalPosition = transformComponent.getTransform().clone();
PlayerRef playerRefComponent = (PlayerRef)componentAccessor.getComponent(playerRef, PlayerRef.getComponentType());
assert playerRefComponent != null;
Map<String, PlayerWorldData> perWorldData = playerComponent.getPlayerConfigData().getPerWorldData();
UUIDComponent uuidComponent = (UUIDComponent)componentAccessor.getComponent(playerRef, UUIDComponent.getComponentType());
assert uuidComponent != null;
UUID playerUUID = uuidComponent.getUuid();
CreativeHubEntityConfig hubEntityConfig = (CreativeHubEntityConfig)componentAccessor.getComponent(playerRef, CreativeHubEntityConfig.getComponentType());
Objects.requireNonNull(playerRefComponent);
originalWorld.execute(playerRefComponent::removeFromStore);
worldFuture.orTimeout(1L, TimeUnit.MINUTES).thenCompose((world) -> {
PlayerWorldData worldData = (PlayerWorldData)perWorldData.get(world.getName());
if (worldData != null && worldData.getLastPosition() != null) {
return world.addPlayer(playerRefComponent, worldData.getLastPosition(), Boolean.TRUE, Boolean.FALSE);
} else {
ISpawnProvider spawnProvider = world.getWorldConfig().getSpawnProvider();
Transform spawnPoint = spawnProvider != null ? spawnProvider.getSpawnPoint(world, playerUUID) : null;
return world.addPlayer(playerRefComponent, spawnPoint, Boolean.TRUE, Boolean.FALSE);
}
}).whenComplete((ret, ex) -> {
if (ex != null) {
((HytaleLogger.Api)LOGGER.at(Level.SEVERE).withCause(ex)).log("Failed to teleport %s to permanent world", playerRefComponent.getUsername());
}
if (ret == null) {
if (originalWorld.isAlive()) {
originalWorld.addPlayer(playerRefComponent, originalPosition, Boolean.TRUE, Boolean.FALSE);
} else {
if (hubEntityConfig != null && hubEntityConfig.getParentHubWorldUuid() != null) {
World parentWorld = Universe.get().getWorld(hubEntityConfig.getParentHubWorldUuid());
if (parentWorld != null) {
CreativeHubWorldConfig parentHubConfig = CreativeHubWorldConfig.get(parentWorld.getWorldConfig());
if (parentHubConfig != null && parentHubConfig.getStartupInstance() != null) {
World hubInstance = CreativeHubPlugin.get().getOrSpawnHubInstance(parentWorld, parentHubConfig, new Transform());
hubInstance.addPlayer(playerRefComponent, (Transform)null, Boolean.TRUE, Boolean.FALSE);
return;
}
}
}
World defaultWorld = Universe.get().getDefaultWorld();
if (defaultWorld != null) {
defaultWorld.addPlayer(playerRefComponent, (Transform)null, Boolean.TRUE, Boolean.FALSE);
} else {
LOGGER.at(Level.SEVERE).log("No fallback world available for %s, disconnecting", playerRefComponent.getUsername());
playerRefComponent.getPacketHandler().disconnect("Failed to teleport - no world available");
}
}
}
});
}
static {
CODEC = ((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)BuilderCodec.builder(HubPortalInteraction.class, HubPortalInteraction::new, SimpleInstantInteraction.CODEC).documentation("Teleports the **Player** to a permanent world, creating it if required.")).appendInherited(new KeyedCodec("WorldName", Codec.STRING), (o, i) -> o.worldName = i, (o) -> o.worldName, (o, p) -> o.worldName = p.worldName).documentation("The name of the permanent world to teleport to.").addValidator(Validators.nonNull()).add()).appendInherited(new KeyedCodec("WorldGenType", Codec.STRING), (o, i) -> o.worldGenType = i, (o) -> o.worldGenType, (o, p) -> o.worldGenType = p.worldGenType).documentation("The world generator type to use when creating the world (e.g., 'Flat', 'Hytale'). Mutually exclusive with InstanceTemplate.").add()).appendInherited(new KeyedCodec("InstanceTemplate", Codec.STRING), (o, i) -> o.instanceTemplate = i, (o) -> o.instanceTemplate, (o, p) -> o.instanceTemplate = p.instanceTemplate).documentation("Instance asset to use as template for creating the permanent world. Mutually exclusive with WorldGenType.").add()).build();
}
}