200 lines
10 KiB
Java
200 lines
10 KiB
Java
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.UUID;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.logging.Level;
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
import org.checkerframework.checker.nullness.compatqual.NonNullDecl;
|
|
|
|
public class HubPortalInteraction extends SimpleInstantInteraction {
|
|
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
|
|
public static final BuilderCodec<HubPortalInteraction> CODEC = BuilderCodec.builder(
|
|
HubPortalInteraction.class, HubPortalInteraction::new, SimpleInstantInteraction.CODEC
|
|
)
|
|
.documentation("Teleports the **Player** to a permanent world, creating it if required.")
|
|
.<String>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()
|
|
.<String>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()
|
|
.<String>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();
|
|
private String worldName;
|
|
private String worldGenType;
|
|
@Nullable
|
|
private String instanceTemplate;
|
|
|
|
@NonNullDecl
|
|
@Override
|
|
public WaitForDataFrom getWaitForDataFrom() {
|
|
return WaitForDataFrom.Server;
|
|
}
|
|
|
|
@Override
|
|
protected void firstRun(@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nonnull CooldownHandler cooldownHandler) {
|
|
CommandBuffer<EntityStore> commandBuffer = context.getCommandBuffer();
|
|
Ref<EntityStore> ref = context.getEntity();
|
|
Player playerComponent = 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 = 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, 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 = perWorldData.get(targetWorld.getName());
|
|
Transform spawnPoint;
|
|
if (worldData != null && worldData.getLastPosition() != null) {
|
|
spawnPoint = worldData.getLastPosition();
|
|
} else {
|
|
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();
|
|
}
|
|
|
|
componentAccessor.addComponent(playerRef, Teleport.getComponentType(), new Teleport(targetWorld, spawnPoint));
|
|
}
|
|
|
|
private void teleportToLoadingWorld(
|
|
@Nonnull Ref<EntityStore> playerRef,
|
|
@Nonnull ComponentAccessor<EntityStore> componentAccessor,
|
|
@Nonnull CompletableFuture<World> worldFuture,
|
|
@Nonnull World originalWorld,
|
|
@Nonnull Player playerComponent
|
|
) {
|
|
TransformComponent transformComponent = componentAccessor.getComponent(playerRef, TransformComponent.getComponentType());
|
|
|
|
assert transformComponent != null;
|
|
|
|
Transform originalPosition = transformComponent.getTransform().clone();
|
|
PlayerRef playerRefComponent = componentAccessor.getComponent(playerRef, PlayerRef.getComponentType());
|
|
|
|
assert playerRefComponent != null;
|
|
|
|
Map<String, PlayerWorldData> perWorldData = playerComponent.getPlayerConfigData().getPerWorldData();
|
|
UUIDComponent uuidComponent = componentAccessor.getComponent(playerRef, UUIDComponent.getComponentType());
|
|
|
|
assert uuidComponent != null;
|
|
|
|
UUID playerUUID = uuidComponent.getUuid();
|
|
CreativeHubEntityConfig hubEntityConfig = componentAccessor.getComponent(playerRef, CreativeHubEntityConfig.getComponentType());
|
|
originalWorld.execute(playerRefComponent::removeFromStore);
|
|
worldFuture.orTimeout(1L, TimeUnit.MINUTES).thenCompose(world -> {
|
|
PlayerWorldData worldData = 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, null, Boolean.TRUE, Boolean.FALSE);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
World defaultWorld = Universe.get().getDefaultWorld();
|
|
if (defaultWorld != null) {
|
|
defaultWorld.addPlayer(playerRefComponent, 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");
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|