Skip to content

Commit

Permalink
Fire teleport events in other places we teleport entities (mekanism/M…
Browse files Browse the repository at this point in the history
  • Loading branch information
pupnewfster committed Mar 1, 2024
1 parent 5392c69 commit 59f9b2d
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/api/java/mekanism/api/MekanismAPI.java
Expand Up @@ -29,7 +29,7 @@ private MekanismAPI() {
/**
* The version of the api classes - may not always match the mod's version
*/
public static final String API_VERSION = "10.5.1";
public static final String API_VERSION = "10.5.2";
/**
* Mekanism's Mod ID
*/
Expand Down
155 changes: 155 additions & 0 deletions src/api/java/mekanism/api/event/MekanismTeleportEvent.java
@@ -1,9 +1,16 @@
package mekanism.api.event;

import java.util.Objects;
import mekanism.api.annotations.NothingNullByDefault;
import mekanism.api.math.FloatingLong;
import mekanism.api.robit.IRobit;
import net.minecraft.core.BlockPos;
import net.minecraft.core.GlobalPos;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.neoforged.fml.LogicalSide;
import net.neoforged.neoforge.common.NeoForge;
Expand Down Expand Up @@ -82,4 +89,152 @@ public BlockHitResult getTargetBlock() {
return targetBlock;
}
}

/**
* @since 10.5.2
*/
public static class GlobalTeleport extends MekanismTeleportEvent {

private final ResourceKey<Level> targetDimension;

/**
* @param entity The entity that is teleporting.
* @param targetX Destination x position.
* @param targetY Destination y position.
* @param targetZ Destination z position.
* @param targetDimension Destination dimension.
*/
public GlobalTeleport(Entity entity, double targetX, double targetY, double targetZ, ResourceKey<Level> targetDimension) {
super(entity, targetX, targetY, targetZ);
this.targetDimension = targetDimension;
}

/**
* @return If the teleport is going across dimensions
*/
public boolean isTransDimensional() {
return getEntity().level().dimension() != targetDimension;
}

/**
* Gets the dimension the entity is teleporting to.
*/
public ResourceKey<Level> getTargetDimension() {
return targetDimension;
}
}

/**
* This event is fired before a robit teleports back home.
* <br>
* This event is Cancelable.
* <br>
* If the event is not canceled, the entity will be teleported.
* <br>
* This event <strong>does not</strong> allow changing the target position.
* <br>
* This event is fired on the {@link NeoForge#EVENT_BUS}.
* <br>
* This event is only fired on the {@link LogicalSide#SERVER} side.
*
* @since 10.5.2
*/
public static class Robit extends GlobalTeleport {

/**
* @param robit The robit that is teleporting home.
*/
public <ROBIT extends Entity & IRobit> Robit(ROBIT robit) {
this(robit, Objects.requireNonNull(robit.getHome(), "Robit teleport event cannot be fired for invalid Robits"));
}

private <ROBIT extends Entity & IRobit> Robit(ROBIT robit, GlobalPos homeLocation) {
super(robit, homeLocation.pos().getX() + 0.5, homeLocation.pos().getY() + 0.3, homeLocation.pos().getZ() + 0.5, homeLocation.dimension());
}
}

/**
* This event is fired before a teleporter or portable teleporter is used.
* <br>
* This event is Cancelable.
* <br>
* If the event is not canceled, the entity will be teleported.
* <br>
* This event <strong>does not</strong> allow changing the target position.
* <br>
* This event is fired on the {@link NeoForge#EVENT_BUS}.
* <br>
* This event is only fired on the {@link LogicalSide#SERVER} side.
* <br>
*
* @apiNote This event is only fired once for the base entity, and is not fired for any of the passengers that are teleported with it. If you care about seeing
* passengers (which may be players) you need to check the entity's {@link Entity#getPassengers() passengers}.
* @since 10.5.2
*/
public static class Teleporter extends GlobalTeleport {

private final FloatingLong energyCost;

/**
* @param entity The entity that is teleporting.
* @param teleporterPos Destination teleporter position.
* @param targetDimension Destination dimension.
* @param energyCost The energy cost to perform the teleportation.
*/
public Teleporter(Entity entity, BlockPos teleporterPos, ResourceKey<Level> targetDimension, FloatingLong energyCost) {
super(entity, teleporterPos.getX() + 0.5, teleporterPos.getY(), teleporterPos.getZ() + 0.5, targetDimension);
this.energyCost = energyCost.copyAsConst();
}

/**
* @return The amount of energy the teleportation will cost
*/
public FloatingLong getEnergyCost() {
return energyCost;
}
}

/**
* This event is fired before a portable teleporter is used.
* <br>
* This event is Cancelable.
* <br>
* If the event is not canceled, the entity will be teleported.
* <br>
* This event <strong>does not</strong> allow changing the target position.
* <br>
* This event is fired on the {@link NeoForge#EVENT_BUS}.
* <br>
* This event is only fired on the {@link LogicalSide#SERVER} side.
*
* @since 10.5.2
*/
public static class PortableTeleporter extends Teleporter {

private final ItemStack portableTeleporter;

/**
* @param player The player that is teleporting.
* @param teleporterPos Destination teleporter position.
* @param targetDimension Destination dimension.
* @param portableTeleporter Portable Teleporter used for teleportation.
* @param energyCost The energy cost to perform the teleportation.
*/
public PortableTeleporter(Player player, BlockPos teleporterPos, ResourceKey<Level> targetDimension, ItemStack portableTeleporter, FloatingLong energyCost) {
super(player, teleporterPos, targetDimension, energyCost);
this.portableTeleporter = portableTeleporter;
}

@Override
public Player getEntity() {
return (Player) super.getEntity();
}

/**
* @return The ItemStack for the Portable Teleporter the player is using to teleport.
*/
public ItemStack getPortableTeleporter() {
return portableTeleporter;
}
}
}
11 changes: 11 additions & 0 deletions src/api/java/mekanism/api/robit/IRobit.java
Expand Up @@ -2,6 +2,7 @@

import mekanism.api.annotations.NothingNullByDefault;
import mekanism.api.security.ISecurityObject;
import net.minecraft.core.GlobalPos;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.entity.player.Player;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -31,4 +32,14 @@ public interface IRobit extends ISecurityObject {
* @implNote This method only syncs changes from the server side, so in general should only be called from the server side except for uses internal to the Robit.
*/
boolean setSkin(ResourceKey<RobitSkin> skin, @Nullable Player player);

/**
* Gets the position of the chargepad that the Robit considers to be its home.
*
* @return Global position containing the dimension and block position.
*
* @since 10.5.2
*/
@Nullable
GlobalPos getHome();
}
22 changes: 17 additions & 5 deletions src/main/java/mekanism/common/entity/EntityRobit.java
Expand Up @@ -19,6 +19,7 @@
import mekanism.api.energy.IEnergyContainer;
import mekanism.api.energy.IMekanismStrictEnergyHandler;
import mekanism.api.energy.IStrictEnergyHandler;
import mekanism.api.event.MekanismTeleportEvent;
import mekanism.api.inventory.IInventorySlot;
import mekanism.api.inventory.IMekanismInventory;
import mekanism.api.math.FloatingLong;
Expand Down Expand Up @@ -120,6 +121,7 @@
import net.neoforged.neoforge.client.model.data.ModelData;
import net.neoforged.neoforge.client.model.data.ModelProperty;
import net.neoforged.neoforge.common.CommonHooks;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.common.util.ITeleporter;
import net.neoforged.neoforge.items.ItemHandlerHelper;
import net.neoforged.neoforge.server.ServerLifecycleHooks;
Expand Down Expand Up @@ -374,15 +376,19 @@ public void goHome() {
if (level().isClientSide() || homeLocation == null) {
return;
}
MekanismTeleportEvent.Robit event = new MekanismTeleportEvent.Robit(this);
if (NeoForge.EVENT_BUS.post(event).isCanceled()) {
//Fail if the event was cancelled
return;
}
setFollowing(false);
BlockPos homePos = homeLocation.pos();
if (level().dimension() == homeLocation.dimension()) {
if (!event.isTransDimensional()) {
setDeltaMovement(0, 0, 0);
teleportTo(homePos.getX() + 0.5, homePos.getY() + 0.3, homePos.getZ() + 0.5);
teleportTo(event.getTargetX(), event.getTargetY(), event.getTargetZ());
} else {
ServerLevel newWorld = ((ServerLevel) this.level()).getServer().getLevel(homeLocation.dimension());
ServerLevel newWorld = ((ServerLevel) this.level()).getServer().getLevel(event.getTargetDimension());
if (newWorld != null) {
Vec3 destination = new Vec3(homePos.getX() + 0.5, homePos.getY() + 0.3, homePos.getZ() + 0.5);
Vec3 destination = event.getTarget();
changeDimension(newWorld, new ITeleporter() {
@Override
public Entity placeEntity(Entity entity, ServerLevel currentWorld, ServerLevel destWorld, float yaw, Function<Boolean, Entity> repositionEntity) {
Expand Down Expand Up @@ -535,6 +541,12 @@ public void setHome(GlobalPos home) {
homeLocation = home;
}

@Nullable
@Override
public GlobalPos getHome() {
return homeLocation;
}

@Override
public boolean isPushable() {
return !energyContainer.isEmpty();
Expand Down
Expand Up @@ -4,6 +4,7 @@
import mekanism.api.Action;
import mekanism.api.AutomationType;
import mekanism.api.energy.IEnergyContainer;
import mekanism.api.event.MekanismTeleportEvent;
import mekanism.api.math.FloatingLong;
import mekanism.common.Mekanism;
import mekanism.common.content.teleporter.TeleporterFrequency;
Expand All @@ -26,6 +27,7 @@
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.network.handling.PlayPayloadContext;
import net.neoforged.neoforge.server.ServerLifecycleHooks;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -62,18 +64,31 @@ public void handle(PlayPayloadContext context) {
Level teleWorld = ServerLifecycleHooks.getCurrentServer().getLevel(coords.dimension());
TileEntityTeleporter teleporter = WorldUtils.getTileEntity(TileEntityTeleporter.class, teleWorld, coords.pos());
if (teleporter != null) {
FloatingLong energyCost;
Runnable energyExtraction = null;
if (!player.isCreative()) {
FloatingLong energyCost = TileEntityTeleporter.calculateEnergyCost(player, teleWorld, coords);
energyCost = TileEntityTeleporter.calculateEnergyCost(player, teleWorld, coords);
IEnergyContainer energyContainer = StorageUtils.getEnergyContainer(stack, 0);
if (energyContainer == null || energyContainer.extract(energyCost, Action.SIMULATE, AutomationType.MANUAL).smallerThan(energyCost)) {
return;
}
energyContainer.extract(energyCost, Action.EXECUTE, AutomationType.MANUAL);
energyExtraction = () -> energyContainer.extract(energyCost, Action.EXECUTE, AutomationType.MANUAL);
} else {
energyCost = FloatingLong.ZERO;
}
//TODO: Figure out what this try catch is meant to be catching as I don't see much of a reason for it to exist
try {
teleporter.didTeleport.add(player.getUUID());
teleporter.teleDelay = 5;
BlockPos teleporterTargetPos = teleporter.getTeleporterTargetPos();
MekanismTeleportEvent.PortableTeleporter event = new MekanismTeleportEvent.PortableTeleporter(player, teleporterTargetPos, coords.dimension(), stack, energyCost);
if (NeoForge.EVENT_BUS.post(event).isCanceled()) {
//Fail if the event was cancelled
return;
}
if (energyExtraction != null) {
energyExtraction.run();
}
player.connection.aboveGroundTickCount = 0;
player.closeContainer();
PacketUtils.sendToAllTracking(new PacketPortalFX(player.blockPosition()), player.level(), coords.pos());
Expand All @@ -84,9 +99,8 @@ public void handle(PlayPayloadContext context) {
double oldY = player.getY();
double oldZ = player.getZ();
Level oldWorld = player.level();
BlockPos teleporterTargetPos = teleporter.getTeleporterTargetPos();
TileEntityTeleporter.teleportEntityTo(player, teleWorld, teleporterTargetPos);
TileEntityTeleporter.alignPlayer(player, teleporterTargetPos, teleporter);
TileEntityTeleporter.teleportEntityTo(player, teleWorld, event);
TileEntityTeleporter.alignPlayer(player, event, teleporter);
if (player.level() != oldWorld || player.distanceToSqr(oldX, oldY, oldZ) >= 25) {
//If the player teleported over 5 blocks, play the sound at both the destination and the source
oldWorld.playSound(null, oldX, oldY, oldZ, SoundEvents.ENDERMAN_TELEPORT, SoundSource.PLAYERS, 1.0F, 1.0F);
Expand Down
27 changes: 20 additions & 7 deletions src/main/java/mekanism/common/tile/TileEntityTeleporter.java
Expand Up @@ -17,6 +17,7 @@
import mekanism.api.AutomationType;
import mekanism.api.IContentsListener;
import mekanism.api.NBTConstants;
import mekanism.api.event.MekanismTeleportEvent;
import mekanism.api.math.FloatingLong;
import mekanism.api.security.SecurityMode;
import mekanism.api.text.EnumColor;
Expand Down Expand Up @@ -70,6 +71,7 @@
import net.minecraft.world.level.portal.PortalInfo;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.common.util.FakePlayer;
import net.neoforged.neoforge.common.util.ITeleporter;
import net.neoforged.neoforge.entity.PartEntity;
Expand Down Expand Up @@ -126,7 +128,11 @@ protected IInventorySlotHolder getInitialInventory(IContentsListener listener) {
return builder.build();
}

public static void alignPlayer(ServerPlayer player, BlockPos target, TileEntityTeleporter teleporter) {
public static void alignPlayer(ServerPlayer player, MekanismTeleportEvent.Teleporter event, TileEntityTeleporter teleporter) {
alignPlayer(player, BlockPos.containing(event.getTarget()), teleporter);
}

private static void alignPlayer(ServerPlayer player, BlockPos target, TileEntityTeleporter teleporter) {
Direction side = null;
if (teleporter.frameDirection != null && teleporter.frameDirection.getAxis().isHorizontal()) {
//If the frame is horizontal always face towards the other portion of the frame
Expand Down Expand Up @@ -295,12 +301,19 @@ private void teleport(TeleporterFrequency frequency, TeleportInfo teleportInfo)
//Calculate energy cost before teleporting the entity, as after teleporting it
// the cost will be negligible due to being on top of the destination
FloatingLong energyCost = calculateEnergyCost(entity, teleWorld, teleportInfo.closest);

MekanismTeleportEvent.Teleporter event = new MekanismTeleportEvent.Teleporter(entity, teleporterTargetPos, teleWorld.dimension(), energyCost);
if (NeoForge.EVENT_BUS.post(event).isCanceled()) {
//Skip the entity if the event was cancelled
continue;
}

double oldX = entity.getX();
double oldY = entity.getY();
double oldZ = entity.getZ();
Entity teleportedEntity = teleportEntityTo(entity, teleWorld, teleporterTargetPos);
Entity teleportedEntity = teleportEntityTo(entity, teleWorld, event);
if (teleportedEntity instanceof ServerPlayer player) {
alignPlayer(player, teleporterTargetPos, teleporter);
alignPlayer(player, event, teleporter);
MekanismCriteriaTriggers.TELEPORT.value().trigger(player);
}
for (GlobalPos coords : activeCoords) {
Expand Down Expand Up @@ -335,9 +348,10 @@ private void markTeleported(TileEntityTeleporter teleporter, Entity entity, bool
}

@Nullable
public static Entity teleportEntityTo(Entity entity, Level targetWorld, BlockPos target) {
if (entity.level().dimension() == targetWorld.dimension()) {
entity.teleportTo(target.getX() + 0.5, target.getY(), target.getZ() + 0.5);
public static Entity teleportEntityTo(Entity entity, Level targetWorld, MekanismTeleportEvent.Teleporter event) {
Vec3 destination = event.getTarget();
if (!event.isTransDimensional()) {
entity.teleportTo(destination.x, destination.y, destination.z);
if (!entity.getPassengers().isEmpty()) {
//Force re-apply any passengers so that players don't get "stuck" outside what they may be riding
((ServerChunkCache) entity.level().getChunkSource()).broadcast(entity, new ClientboundSetPassengersPacket(entity));
Expand All @@ -353,7 +367,6 @@ public static Entity teleportEntityTo(Entity entity, Level targetWorld, BlockPos
}
return entity;
}
Vec3 destination = new Vec3(target.getX() + 0.5, target.getY(), target.getZ() + 0.5);
//Note: We grab the passengers here instead of in placeEntity as changeDimension starts by removing any passengers
List<Entity> passengers = entity.getPassengers();
return entity.changeDimension((ServerLevel) targetWorld, new ITeleporter() {
Expand Down

0 comments on commit 59f9b2d

Please sign in to comment.