From 54419e0d6b06c2d7ebed3d0b9af37594257f66ff Mon Sep 17 00:00:00 2001 From: frostdev-ops Date: Sat, 11 Jul 2026 12:54:20 -0400 Subject: [PATCH] Persist camera orientation between views --- src/main/java/cr0s/warpdrive/api/ICamera.java | 15 ++++ .../block/detection/BlockMonitor.java | 9 +- .../block/detection/TileEntityCamera.java | 41 ++++++++- .../block/weapon/TileEntityLaserCamera.java | 41 ++++++++- .../network/MessageCameraOrientation.java | 89 +++++++++++++++++++ .../cr0s/warpdrive/network/PacketHandler.java | 8 +- .../cr0s/warpdrive/render/EntityCamera.java | 20 +++-- 7 files changed, 210 insertions(+), 13 deletions(-) create mode 100644 src/main/java/cr0s/warpdrive/api/ICamera.java create mode 100644 src/main/java/cr0s/warpdrive/network/MessageCameraOrientation.java diff --git a/src/main/java/cr0s/warpdrive/api/ICamera.java b/src/main/java/cr0s/warpdrive/api/ICamera.java new file mode 100644 index 000000000..bf5a01864 --- /dev/null +++ b/src/main/java/cr0s/warpdrive/api/ICamera.java @@ -0,0 +1,15 @@ +package cr0s.warpdrive.api; + +public interface ICamera extends IVideoChannel { + + String CAMERA_YAW_TAG = "cameraYaw"; + String CAMERA_PITCH_TAG = "cameraPitch"; + + boolean hasCameraOrientation(); + + float getCameraYaw(); + + float getCameraPitch(); + + void setCameraOrientation(final float yaw, final float pitch); +} diff --git a/src/main/java/cr0s/warpdrive/block/detection/BlockMonitor.java b/src/main/java/cr0s/warpdrive/block/detection/BlockMonitor.java index b9435a2d6..c4ebfdf81 100644 --- a/src/main/java/cr0s/warpdrive/block/detection/BlockMonitor.java +++ b/src/main/java/cr0s/warpdrive/block/detection/BlockMonitor.java @@ -2,6 +2,7 @@ import cr0s.warpdrive.Commons; import cr0s.warpdrive.WarpDrive; +import cr0s.warpdrive.api.ICamera; import cr0s.warpdrive.api.IVideoChannel; import cr0s.warpdrive.api.WarpDriveText; import cr0s.warpdrive.block.BlockAbstractRotatingContainer; @@ -73,8 +74,12 @@ public boolean onBlockActivated(@Nonnull final World world, @Nonnull final Block camera.blockPos.getX(), camera.blockPos.getY(), camera.blockPos.getZ() )); + final TileEntity tileEntityCamera = world.getTileEntity(camera.blockPos); + final ICamera cameraTile = tileEntityCamera instanceof ICamera ? (ICamera) tileEntityCamera : null; ClientCameraHandler.setupViewpoint( - camera.type, entityPlayer, entityPlayer.rotationYaw, entityPlayer.rotationPitch, + camera.type, entityPlayer, + cameraTile != null && cameraTile.hasCameraOrientation() ? cameraTile.getCameraYaw() : entityPlayer.rotationYaw, + cameraTile != null && cameraTile.hasCameraOrientation() ? cameraTile.getCameraPitch() : entityPlayer.rotationPitch, blockPos, blockState, camera.blockPos, world.getBlockState(camera.blockPos)); return true; @@ -83,4 +88,4 @@ public boolean onBlockActivated(@Nonnull final World world, @Nonnull final Block return super.onBlockActivated(world, blockPos, blockState, entityPlayer, enumHand, enumFacing, hitX, hitY, hitZ); } -} \ No newline at end of file +} diff --git a/src/main/java/cr0s/warpdrive/block/detection/TileEntityCamera.java b/src/main/java/cr0s/warpdrive/block/detection/TileEntityCamera.java index 5e2cb6706..9777dcd5d 100644 --- a/src/main/java/cr0s/warpdrive/block/detection/TileEntityCamera.java +++ b/src/main/java/cr0s/warpdrive/block/detection/TileEntityCamera.java @@ -2,6 +2,7 @@ import cr0s.warpdrive.Commons; import cr0s.warpdrive.WarpDrive; +import cr0s.warpdrive.api.ICamera; import cr0s.warpdrive.api.IVideoChannel; import cr0s.warpdrive.api.WarpDriveText; import cr0s.warpdrive.block.TileEntityAbstractMachine; @@ -49,9 +50,11 @@ import net.minecraftforge.common.util.Constants.NBT; import net.minecraftforge.fml.common.Optional; -public class TileEntityCamera extends TileEntityAbstractMachine implements IVideoChannel { +public class TileEntityCamera extends TileEntityAbstractMachine implements ICamera { private int videoChannel = -1; + private float cameraYaw = Float.NaN; + private float cameraPitch = Float.NaN; private static final int REGISTRY_UPDATE_INTERVAL_TICKS = 15 * 20; private static final int PACKET_SEND_INTERVAL_TICKS = 60 * 20; @@ -430,6 +433,28 @@ public void setVideoChannel(final int parVideoChannel) { registryUpdateTicks = 0; } } + + @Override + public boolean hasCameraOrientation() { + return Float.isFinite(cameraYaw) && Float.isFinite(cameraPitch); + } + + @Override + public float getCameraYaw() { + return cameraYaw; + } + + @Override + public float getCameraPitch() { + return cameraPitch; + } + + @Override + public void setCameraOrientation(final float yaw, final float pitch) { + cameraYaw = yaw; + cameraPitch = pitch; + markDirty(); + } @Override public void invalidate() { @@ -454,6 +479,14 @@ public void readFromNBT(@Nonnull final NBTTagCompound tagCompound) { super.readFromNBT(tagCompound); videoChannel = tagCompound.getInteger("frequency") + tagCompound.getInteger(VIDEO_CHANNEL_TAG); + if ( tagCompound.hasKey(CAMERA_YAW_TAG) + && tagCompound.hasKey(CAMERA_PITCH_TAG) ) { + cameraYaw = tagCompound.getFloat(CAMERA_YAW_TAG); + cameraPitch = tagCompound.getFloat(CAMERA_PITCH_TAG); + } else { + cameraYaw = Float.NaN; + cameraPitch = Float.NaN; + } if (WarpDriveConfig.LOGGING_VIDEO_CHANNEL) { WarpDrive.logger.info(this + " readFromNBT"); } @@ -486,6 +519,10 @@ public NBTTagCompound writeToNBT(@Nonnull NBTTagCompound tagCompound) { tagCompound = super.writeToNBT(tagCompound); tagCompound.setInteger(VIDEO_CHANNEL_TAG, videoChannel); + if (hasCameraOrientation()) { + tagCompound.setFloat(CAMERA_YAW_TAG, cameraYaw); + tagCompound.setFloat(CAMERA_PITCH_TAG, cameraPitch); + } if (WarpDriveConfig.LOGGING_VIDEO_CHANNEL) { WarpDrive.logger.info(this + " writeToNBT"); } @@ -662,4 +699,4 @@ public String toString() { videoChannel, Commons.format(world, pos) ); } -} \ No newline at end of file +} diff --git a/src/main/java/cr0s/warpdrive/block/weapon/TileEntityLaserCamera.java b/src/main/java/cr0s/warpdrive/block/weapon/TileEntityLaserCamera.java index b1e8900e3..6577c1172 100644 --- a/src/main/java/cr0s/warpdrive/block/weapon/TileEntityLaserCamera.java +++ b/src/main/java/cr0s/warpdrive/block/weapon/TileEntityLaserCamera.java @@ -2,6 +2,7 @@ import cr0s.warpdrive.Commons; import cr0s.warpdrive.WarpDrive; +import cr0s.warpdrive.api.ICamera; import cr0s.warpdrive.api.IVideoChannel; import cr0s.warpdrive.block.TileEntityLaser; import cr0s.warpdrive.config.WarpDriveConfig; @@ -18,9 +19,11 @@ import net.minecraftforge.fml.common.Optional; -public class TileEntityLaserCamera extends TileEntityLaser implements IVideoChannel { +public class TileEntityLaserCamera extends TileEntityLaser implements ICamera { private int videoChannel = -1; + private float cameraYaw = Float.NaN; + private float cameraPitch = Float.NaN; private static final int REGISTRY_UPDATE_INTERVAL_TICKS = 15 * 20; private static final int PACKET_SEND_INTERVAL_TICKS = 60 * 20; @@ -83,11 +86,41 @@ public void setVideoChannel(final int parVideoChannel) { registryUpdateTicks = 0; } } + + @Override + public boolean hasCameraOrientation() { + return Float.isFinite(cameraYaw) && Float.isFinite(cameraPitch); + } + + @Override + public float getCameraYaw() { + return cameraYaw; + } + + @Override + public float getCameraPitch() { + return cameraPitch; + } + + @Override + public void setCameraOrientation(final float yaw, final float pitch) { + cameraYaw = yaw; + cameraPitch = pitch; + markDirty(); + } @Override public void readFromNBT(@Nonnull final NBTTagCompound tagCompound) { super.readFromNBT(tagCompound); setVideoChannel(tagCompound.getInteger("cameraFrequency") + tagCompound.getInteger(VIDEO_CHANNEL_TAG)); + if ( tagCompound.hasKey(CAMERA_YAW_TAG) + && tagCompound.hasKey(CAMERA_PITCH_TAG) ) { + cameraYaw = tagCompound.getFloat(CAMERA_YAW_TAG); + cameraPitch = tagCompound.getFloat(CAMERA_PITCH_TAG); + } else { + cameraYaw = Float.NaN; + cameraPitch = Float.NaN; + } } @Nonnull @@ -95,6 +128,10 @@ public void readFromNBT(@Nonnull final NBTTagCompound tagCompound) { public NBTTagCompound writeToNBT(@Nonnull NBTTagCompound tagCompound) { tagCompound = super.writeToNBT(tagCompound); tagCompound.setInteger(VIDEO_CHANNEL_TAG, videoChannel); + if (hasCameraOrientation()) { + tagCompound.setFloat(CAMERA_YAW_TAG, cameraYaw); + tagCompound.setFloat(CAMERA_PITCH_TAG, cameraPitch); + } return tagCompound; } @@ -145,4 +182,4 @@ public String toString() { videoChannel, Commons.format(world, pos)); } -} \ No newline at end of file +} diff --git a/src/main/java/cr0s/warpdrive/network/MessageCameraOrientation.java b/src/main/java/cr0s/warpdrive/network/MessageCameraOrientation.java new file mode 100644 index 000000000..3a6d78bed --- /dev/null +++ b/src/main/java/cr0s/warpdrive/network/MessageCameraOrientation.java @@ -0,0 +1,89 @@ +package cr0s.warpdrive.network; + +import cr0s.warpdrive.api.ICamera; +import cr0s.warpdrive.api.IVideoChannel; +import cr0s.warpdrive.block.detection.TileEntityMonitor; + +import io.netty.buffer.ByteBuf; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.WorldServer; + +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +public class MessageCameraOrientation implements IMessage, IMessageHandler { + + private BlockPos blockPosMonitor; + private BlockPos blockPosCamera; + private float yaw; + private float pitch; + + @SuppressWarnings("unused") + public MessageCameraOrientation() { + // required on receiving side + } + + public MessageCameraOrientation(final BlockPos blockPosMonitor, final BlockPos blockPosCamera, final float yaw, final float pitch) { + this.blockPosMonitor = blockPosMonitor; + this.blockPosCamera = blockPosCamera; + this.yaw = yaw; + this.pitch = pitch; + } + + @Override + public void fromBytes(final ByteBuf buffer) { + blockPosMonitor = BlockPos.fromLong(buffer.readLong()); + blockPosCamera = BlockPos.fromLong(buffer.readLong()); + yaw = buffer.readFloat(); + pitch = buffer.readFloat(); + } + + @Override + public void toBytes(final ByteBuf buffer) { + buffer.writeLong(blockPosMonitor.toLong()); + buffer.writeLong(blockPosCamera.toLong()); + buffer.writeFloat(yaw); + buffer.writeFloat(pitch); + } + + private void handle(final EntityPlayerMP entityPlayerMP) { + if (!Float.isFinite(yaw) || !Float.isFinite(pitch)) { + return; + } + + final WorldServer world = entityPlayerMP.getServerWorld(); + if ( !world.isBlockLoaded(blockPosMonitor, false) + || !world.isBlockLoaded(blockPosCamera, false) + || entityPlayerMP.getDistanceSq(blockPosMonitor.getX() + 0.5D, blockPosMonitor.getY() + 0.5D, blockPosMonitor.getZ() + 0.5D) > 64.0D ) { + return; + } + + final TileEntity tileEntityMonitor = world.getTileEntity(blockPosMonitor); + final TileEntity tileEntityCamera = world.getTileEntity(blockPosCamera); + if ( !(tileEntityMonitor instanceof TileEntityMonitor) + || !(tileEntityCamera instanceof ICamera) ) { + return; + } + + final int videoChannel = ((TileEntityMonitor) tileEntityMonitor).getVideoChannel(); + final ICamera camera = (ICamera) tileEntityCamera; + if ( !IVideoChannel.isValid(videoChannel) + || videoChannel != camera.getVideoChannel() ) { + return; + } + + camera.setCameraOrientation(MathHelper.wrapDegrees(yaw), MathHelper.clamp(pitch, -90.0F, 90.0F)); + } + + @Override + public IMessage onMessage(final MessageCameraOrientation message, final MessageContext context) { + final EntityPlayerMP entityPlayerMP = context.getServerHandler().player; + entityPlayerMP.getServerWorld().addScheduledTask(() -> message.handle(entityPlayerMP)); + return null; + } +} diff --git a/src/main/java/cr0s/warpdrive/network/PacketHandler.java b/src/main/java/cr0s/warpdrive/network/PacketHandler.java index eae824a00..32671d5c9 100644 --- a/src/main/java/cr0s/warpdrive/network/PacketHandler.java +++ b/src/main/java/cr0s/warpdrive/network/PacketHandler.java @@ -65,6 +65,7 @@ public static void init() { } catch (final Exception exception) { throw new RuntimeException(exception); } + simpleNetworkManager.registerMessage(MessageCameraOrientation.class , MessageCameraOrientation.class , 104, Side.SERVER); } // Beam effect sent to client side @@ -235,6 +236,11 @@ public static void sendLaserTargetingPacket(final int x, final int y, final int x, y, z, yaw, pitch)); } } + + public static void sendCameraOrientationPacket(final BlockPos blockPosMonitor, final BlockPos blockPosCamera, + final float yaw, final float pitch) { + simpleNetworkManager.sendToServer(new MessageCameraOrientation(blockPosMonitor, blockPosCamera, yaw, pitch)); + } // Sending cloaking area definition (server -> client) public static void sendCloakPacket(final EntityPlayerMP entityPlayerMP, final CloakedArea area, final boolean isUncloaking) { @@ -348,4 +354,4 @@ public static void sendUnseating() { WarpDrive.logger.info("Sent unseating packet"); } } -} \ No newline at end of file +} diff --git a/src/main/java/cr0s/warpdrive/render/EntityCamera.java b/src/main/java/cr0s/warpdrive/render/EntityCamera.java index 6e21b5dc1..76a686e96 100644 --- a/src/main/java/cr0s/warpdrive/render/EntityCamera.java +++ b/src/main/java/cr0s/warpdrive/render/EntityCamera.java @@ -15,6 +15,7 @@ import net.minecraft.util.EnumHandSide; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; @@ -36,6 +37,8 @@ public final class EntityCamera extends EntityLivingBase { private int cameraZ; private EntityPlayer player; + private float playerYawLast; + private float playerPitchLast; private int dx = 0, dy = 0, dz = 0; @@ -62,6 +65,8 @@ public EntityCamera(final World world, final int x, final int y, final int z, fi cameraY = y; cameraZ = z; this.player = player; + playerYawLast = player.rotationYaw; + playerPitchLast = player.rotationPitch; } @Override @@ -91,6 +96,7 @@ private void closeCamera() { return; } + PacketHandler.sendCameraOrientationPacket(ClientCameraHandler.blockPosCheck1, new BlockPos(cameraX, cameraY, cameraZ), rotationYaw, rotationPitch); ClientCameraHandler.resetViewpoint(); world.removeEntity(this); isActive = false; @@ -113,13 +119,15 @@ public void onEntityUpdate() { return; } + final float deltaYaw = MathHelper.wrapDegrees(player.rotationYaw - playerYawLast); + final float deltaPitch = player.rotationPitch - playerPitchLast; + rotationYaw = MathHelper.wrapDegrees(rotationYaw + deltaYaw); + rotationPitch = MathHelper.clamp(rotationPitch + deltaPitch, -90.0F, 90.0F); + playerYawLast = player.rotationYaw; + playerPitchLast = player.rotationPitch; + final Block block = world.getBlockState(new BlockPos(cameraX, cameraY, cameraZ)).getBlock(); final Minecraft mc = Minecraft.getMinecraft(); - if (mc.getRenderViewEntity() != null) { - mc.getRenderViewEntity().rotationYaw = player.rotationYaw; - // mc.renderViewEntity.rotationYawHead = player.rotationYawHead; - mc.getRenderViewEntity().rotationPitch = player.rotationPitch; - } ClientCameraHandler.overlayLoggingMessage = "Mouse " + Mouse.isButtonDown(0) + " " + Mouse.isButtonDown(1) + " " + Mouse.isButtonDown(2) + " " + Mouse.isButtonDown(3) + "\nBackspace " + Keyboard.isKeyDown(Keyboard.KEY_BACKSLASH) @@ -274,4 +282,4 @@ public ItemStack getItemStackFromSlot(@Nonnull final EntityEquipmentSlot slotIn) public void setItemStackToSlot(@Nonnull final EntityEquipmentSlot slotIn, @Nullable final ItemStack itemStack) { } -} \ No newline at end of file +}