232 lines
13 KiB
Java
232 lines
13 KiB
Java
package com.hypixel.hytale.builtin.instances.interactions;
|
|
|
|
import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap;
|
|
import com.hypixel.hytale.builtin.instances.InstancesPlugin;
|
|
import com.hypixel.hytale.builtin.instances.blocks.ConfigurableInstanceBlock;
|
|
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
|
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.component.Store;
|
|
import com.hypixel.hytale.math.Axis;
|
|
import com.hypixel.hytale.math.shape.Box;
|
|
import com.hypixel.hytale.math.util.ChunkUtil;
|
|
import com.hypixel.hytale.math.vector.Transform;
|
|
import com.hypixel.hytale.math.vector.Vector3d;
|
|
import com.hypixel.hytale.math.vector.Vector3f;
|
|
import com.hypixel.hytale.math.vector.Vector3i;
|
|
import com.hypixel.hytale.protocol.BlockPosition;
|
|
import com.hypixel.hytale.protocol.InteractionType;
|
|
import com.hypixel.hytale.protocol.WaitForDataFrom;
|
|
import com.hypixel.hytale.server.core.Message;
|
|
import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes;
|
|
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
|
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
|
|
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
|
import com.hypixel.hytale.server.core.entity.entities.Player;
|
|
import com.hypixel.hytale.server.core.inventory.ItemStack;
|
|
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.client.SimpleBlockInteraction;
|
|
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.chunk.BlockChunk;
|
|
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
|
|
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
|
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
|
|
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
|
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.TimeUnit;
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
|
|
public class TeleportConfigInstanceInteraction extends SimpleBlockInteraction {
|
|
@Nonnull
|
|
private static final Message MESSAGE_GENERAL_INTERACTION_CONFIGURE_INSTANCE_NO_INSTANCE_NAME = Message.translation(
|
|
"server.general.interaction.configureInstance.noInstanceName"
|
|
);
|
|
@Nonnull
|
|
public static final BuilderCodec<TeleportConfigInstanceInteraction> CODEC = BuilderCodec.builder(
|
|
TeleportConfigInstanceInteraction.class, TeleportConfigInstanceInteraction::new, SimpleBlockInteraction.CODEC
|
|
)
|
|
.documentation(
|
|
"Teleports the **Player** to the named instance, creating it if required.\n\nThis is configured via a UI instead of inside the interaction. This interaction just executes that set configuration."
|
|
)
|
|
.build();
|
|
private static final int SET_BLOCK_SETTINGS = 256;
|
|
|
|
@Nonnull
|
|
@Override
|
|
public WaitForDataFrom getWaitForDataFrom() {
|
|
return WaitForDataFrom.Server;
|
|
}
|
|
|
|
@Override
|
|
protected void interactWithBlock(
|
|
@Nonnull World world,
|
|
@Nonnull CommandBuffer<EntityStore> commandBuffer,
|
|
@Nonnull InteractionType type,
|
|
@Nonnull InteractionContext context,
|
|
@Nullable ItemStack itemInHand,
|
|
@Nonnull Vector3i targetBlock,
|
|
@Nonnull CooldownHandler cooldownHandler
|
|
) {
|
|
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())) {
|
|
InstancesPlugin module = InstancesPlugin.get();
|
|
Universe universe = Universe.get();
|
|
ChunkStore chunkStore = world.getChunkStore();
|
|
Ref<ChunkStore> chunkRef = chunkStore.getChunkReference(ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z));
|
|
if (chunkRef != null && chunkRef.isValid()) {
|
|
BlockComponentChunk blockComponentChunk = chunkStore.getStore().getComponent(chunkRef, BlockComponentChunk.getComponentType());
|
|
|
|
assert blockComponentChunk != null;
|
|
|
|
Ref<ChunkStore> blockRef = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(targetBlock.x, targetBlock.y, targetBlock.z));
|
|
if (blockRef != null && blockRef.isValid()) {
|
|
ConfigurableInstanceBlock configurableInstanceBlock = chunkStore.getStore()
|
|
.getComponent(blockRef, ConfigurableInstanceBlock.getComponentType());
|
|
if (configurableInstanceBlock != null) {
|
|
if (configurableInstanceBlock.getInstanceName() == null) {
|
|
playerComponent.sendMessage(MESSAGE_GENERAL_INTERACTION_CONFIGURE_INSTANCE_NO_INSTANCE_NAME);
|
|
} else {
|
|
CompletableFuture<World> targetWorldFuture = null;
|
|
Transform returnPoint = null;
|
|
World targetWorld;
|
|
if (configurableInstanceBlock.getInstanceKey() != null) {
|
|
targetWorld = universe.getWorld(configurableInstanceBlock.getInstanceKey());
|
|
if (targetWorld == null) {
|
|
returnPoint = makeReturnPoint(configurableInstanceBlock, context, commandBuffer);
|
|
targetWorldFuture = module.spawnInstance(
|
|
configurableInstanceBlock.getInstanceName(), configurableInstanceBlock.getInstanceKey(), world, returnPoint
|
|
);
|
|
}
|
|
} else {
|
|
UUID worldUuid = configurableInstanceBlock.getWorldUUID();
|
|
targetWorldFuture = configurableInstanceBlock.getWorldFuture();
|
|
targetWorld = worldUuid != null ? universe.getWorld(worldUuid) : null;
|
|
if (targetWorld == null && targetWorldFuture == null) {
|
|
returnPoint = makeReturnPoint(configurableInstanceBlock, context, commandBuffer);
|
|
targetWorldFuture = module.spawnInstance(configurableInstanceBlock.getInstanceName(), world, returnPoint);
|
|
configurableInstanceBlock.setWorldFuture(targetWorldFuture);
|
|
targetWorldFuture.thenAccept(instanceWorld -> {
|
|
if (blockRef.isValid()) {
|
|
configurableInstanceBlock.setWorldFuture(null);
|
|
configurableInstanceBlock.setWorldUUID(instanceWorld.getWorldConfig().getUuid());
|
|
blockComponentChunk.markNeedsSaving();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
if (targetWorldFuture != null) {
|
|
Transform personalReturnPoint = getPersonalReturnPoint(configurableInstanceBlock, context, returnPoint, commandBuffer);
|
|
InstancesPlugin.teleportPlayerToLoadingInstance(ref, commandBuffer, targetWorldFuture, personalReturnPoint);
|
|
} else if (targetWorld != null) {
|
|
Transform personalReturnPoint = getPersonalReturnPoint(configurableInstanceBlock, context, returnPoint, commandBuffer);
|
|
InstancesPlugin.teleportPlayerToInstance(ref, commandBuffer, targetWorld, personalReturnPoint);
|
|
}
|
|
|
|
double removeBlockAfter = configurableInstanceBlock.getRemoveBlockAfter();
|
|
if (removeBlockAfter >= 0.0) {
|
|
if (removeBlockAfter == 0.0) {
|
|
long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z);
|
|
WorldChunk worldChunk = world.getChunk(chunkIndex);
|
|
worldChunk.setBlock(targetBlock.x, targetBlock.y, targetBlock.z, 0, 256);
|
|
} else {
|
|
int block = world.getBlock(targetBlock);
|
|
new CompletableFuture().completeOnTimeout(null, (long)(removeBlockAfter * 1.0E9), TimeUnit.NANOSECONDS).thenRunAsync(() -> {
|
|
if (world.getBlock(targetBlock) == block) {
|
|
long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z);
|
|
WorldChunk worldChunkx = world.getChunk(chunkIndex);
|
|
worldChunkx.setBlock(targetBlock.x, targetBlock.y, targetBlock.z, 0, 256);
|
|
}
|
|
}, world);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void simulateInteractWithBlock(
|
|
@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nullable ItemStack itemInHand, @Nonnull World world, @Nonnull Vector3i targetBlock
|
|
) {
|
|
}
|
|
|
|
@Nullable
|
|
private static Transform getPersonalReturnPoint(
|
|
@Nonnull ConfigurableInstanceBlock state,
|
|
@Nonnull InteractionContext context,
|
|
@Nullable Transform returnPoint,
|
|
@Nonnull ComponentAccessor<EntityStore> componentAccessor
|
|
) {
|
|
if (!state.isPersonalReturnPoint()) {
|
|
return null;
|
|
} else {
|
|
return returnPoint == null ? makeReturnPoint(state, context, componentAccessor) : returnPoint;
|
|
}
|
|
}
|
|
|
|
@Nonnull
|
|
private static Transform makeReturnPoint(
|
|
@Nonnull ConfigurableInstanceBlock state, @Nonnull InteractionContext context, @Nonnull ComponentAccessor<EntityStore> componentAccessor
|
|
) {
|
|
BlockPosition targetBlock = context.getTargetBlock();
|
|
if (targetBlock == null) {
|
|
throw new IllegalArgumentException("Can't use OriginSource.BLOCK without a target block");
|
|
} else {
|
|
World world = componentAccessor.getExternalData().getWorld();
|
|
ChunkStore chunkStore = world.getChunkStore();
|
|
Store<ChunkStore> chunkComponentStore = chunkStore.getStore();
|
|
long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z);
|
|
Ref<ChunkStore> chunkRef = chunkStore.getChunkReference(chunkIndex);
|
|
if (chunkRef != null && chunkRef.isValid()) {
|
|
BlockChunk blockChunkComponent = chunkComponentStore.getComponent(chunkRef, BlockChunk.getComponentType());
|
|
|
|
assert blockChunkComponent != null;
|
|
|
|
WorldChunk worldChunkComponent = chunkComponentStore.getComponent(chunkRef, WorldChunk.getComponentType());
|
|
|
|
assert worldChunkComponent != null;
|
|
|
|
BlockType blockType = worldChunkComponent.getBlockType(targetBlock.x, targetBlock.y, targetBlock.z);
|
|
if (blockType == null) {
|
|
throw new IllegalArgumentException("Block type not found");
|
|
} else {
|
|
IndexedLookupTableAssetMap<String, BlockBoundingBoxes> hitboxAssetMap = BlockBoundingBoxes.getAssetMap();
|
|
BlockSection section = blockChunkComponent.getSectionAtBlockY(targetBlock.y);
|
|
int rotationIndex = section.getRotationIndex(targetBlock.x, targetBlock.y, targetBlock.z);
|
|
RotationTuple rotation = RotationTuple.get(rotationIndex);
|
|
Box hitbox = hitboxAssetMap.getAsset(blockType.getHitboxTypeIndex()).get(rotationIndex).getBoundingBox();
|
|
Vector3d position = state.getPositionOffset() != null ? rotation.rotate(state.getPositionOffset()) : new Vector3d();
|
|
position.x = position.x + (hitbox.middleX() + targetBlock.x);
|
|
position.y = position.y + (hitbox.middleY() + targetBlock.y);
|
|
position.z = position.z + (hitbox.middleZ() + targetBlock.z);
|
|
Vector3f rotationOutput = Vector3f.NaN;
|
|
if (state.getRotation() != null) {
|
|
rotationOutput = state.getRotation().clone();
|
|
rotationOutput.addRotationOnAxis(Axis.Y, rotation.yaw().getDegrees());
|
|
rotationOutput.addRotationOnAxis(Axis.X, rotation.pitch().getDegrees());
|
|
}
|
|
|
|
return new Transform(position, rotationOutput);
|
|
}
|
|
} else {
|
|
throw new IllegalArgumentException("Chunk not loaded");
|
|
}
|
|
}
|
|
}
|
|
}
|