diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Replay.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Replay.java index 97a3e75..64de72e 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Replay.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Replay.java @@ -1,5 +1,8 @@ package me.unurled.sacredrealms.sr.components.cutscene; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; import me.unurled.sacredrealms.sr.SR; import me.unurled.sacredrealms.sr.components.cutscene.recording.Recording; import me.unurled.sacredrealms.sr.components.cutscene.tasks.ReplayTask; @@ -12,10 +15,12 @@ public class Replay { private final Player viewer; private final Recording recording; private final BukkitTask replayTask; + private final Map spawnedEntities; public Replay(Recording recording, @NotNull Player viewer) { this.recording = recording; this.viewer = viewer; + spawnedEntities = new HashMap<>(); viewer.getInventory().clear(); // TODO: control cutscene replay @@ -42,4 +47,8 @@ public class Replay { replayTask.cancel(); } } + + public Map spawnedEntities() { + return spawnedEntities; + } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/Recordable.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/Recordable.java index 8f7252d..c5085a6 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/Recordable.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/Recordable.java @@ -1,9 +1,12 @@ package me.unurled.sacredrealms.sr.components.cutscene.recording; +import com.github.retrooper.packetevents.protocol.player.User; +import me.unurled.sacredrealms.sr.components.cutscene.Replay; + public abstract class Recordable { private long tick; - public abstract void replay(); + public abstract void replay(Replay replay, User user) throws Exception; public long tick() { return tick; diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/recordables/DespawnEntityRecordable.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/recordables/DespawnEntityRecordable.java index 8c6d608..f2776b6 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/recordables/DespawnEntityRecordable.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/recordables/DespawnEntityRecordable.java @@ -1,7 +1,11 @@ package me.unurled.sacredrealms.sr.components.cutscene.recording.recordables; +import com.github.retrooper.packetevents.protocol.player.User; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDestroyEntities; import java.util.UUID; +import me.unurled.sacredrealms.sr.components.cutscene.Replay; import me.unurled.sacredrealms.sr.components.cutscene.recording.Recordable; +import org.jetbrains.annotations.NotNull; public class DespawnEntityRecordable extends Recordable { @@ -14,8 +18,11 @@ public class DespawnEntityRecordable extends Recordable { } @Override - public void replay() { - // TODO: fake spawn entity + public void replay(@NotNull Replay replay, @NotNull User user) throws Exception { + WrapperPlayServerDestroyEntities destroyEntities = + new WrapperPlayServerDestroyEntities(replay.spawnedEntities().get(entityId)); + user.sendPacket(destroyEntities); + replay.spawnedEntities().remove(entityId); } public UUID entityId() { diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/recordables/LocationChangeRecordable.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/recordables/LocationChangeRecordable.java index 892eb56..91669bf 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/recordables/LocationChangeRecordable.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/recordables/LocationChangeRecordable.java @@ -1,6 +1,8 @@ package me.unurled.sacredrealms.sr.components.cutscene.recording.recordables; +import com.github.retrooper.packetevents.protocol.player.User; import java.util.UUID; +import me.unurled.sacredrealms.sr.components.cutscene.Replay; import me.unurled.sacredrealms.sr.components.cutscene.recording.Recordable; import org.bukkit.Location; @@ -17,7 +19,7 @@ public class LocationChangeRecordable extends Recordable { } @Override - public void replay() {} + public void replay(Replay replay, User user) throws Exception {} public UUID entityId() { return entityId; diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/recordables/SpawnEntityRecordable.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/recordables/SpawnEntityRecordable.java index aaa21db..8eb3dd8 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/recordables/SpawnEntityRecordable.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/recording/recordables/SpawnEntityRecordable.java @@ -1,8 +1,20 @@ package me.unurled.sacredrealms.sr.components.cutscene.recording.recordables; +import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; +import com.github.retrooper.packetevents.protocol.player.User; +import com.github.retrooper.packetevents.protocol.player.UserProfile; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoUpdate; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnPlayer; +import io.github.retrooper.packetevents.util.SpigotConversionUtil; +import java.lang.reflect.Field; +import java.util.EnumSet; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import me.unurled.sacredrealms.sr.components.cutscene.Replay; import me.unurled.sacredrealms.sr.components.cutscene.recording.Recordable; import net.kyori.adventure.text.Component; +import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; @@ -33,8 +45,49 @@ public class SpawnEntityRecordable extends Recordable { } @Override - public void replay() { - // TODO: fake spawn entity + public void replay(Replay replay, User user) throws Exception { // TODO: check everything + + Class entityClass = Class.forName("net.minecraft.world.entity.Entity"); + Field field = entityClass.getDeclaredField("ENTITY_COUNTER"); + field.setAccessible(true); + AtomicInteger ENTITY_COUNTER = (AtomicInteger) field.get(null); + int id = ENTITY_COUNTER.incrementAndGet(); + + if (entityType == EntityType.PLAYER) { + WrapperPlayServerPlayerInfoUpdate.PlayerInfo data = + new WrapperPlayServerPlayerInfoUpdate.PlayerInfo( + new UserProfile(entityId, name.toString()), + false, + 0, + SpigotConversionUtil.fromBukkitGameMode(GameMode.SURVIVAL), + name, + null); + + user.sendPacket( + new WrapperPlayServerPlayerInfoUpdate( + EnumSet.of(WrapperPlayServerPlayerInfoUpdate.Action.ADD_PLAYER), data)); + user.sendPacket( + new WrapperPlayServerSpawnPlayer( + id, entityId, SpigotConversionUtil.fromBukkitLocation(location))); + + replay.spawnedEntities().put(entityId, id); + } else { + com.github.retrooper.packetevents.protocol.entity.type.EntityType entityType1 = + EntityTypes.getByName(entityType.getKey().toString()); + + WrapperPlayServerSpawnEntity spawnEntityPacket = + new WrapperPlayServerSpawnEntity( + id, + UUID.randomUUID(), + entityType1, + SpigotConversionUtil.fromBukkitLocation(location), + 0, + 0, + null); + + user.sendPacket(spawnEntityPacket); + replay.spawnedEntities().put(entityId, id); + } } public EntityType entityType() { diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/tasks/ReplayTask.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/tasks/ReplayTask.java index d81a73d..e612a59 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/tasks/ReplayTask.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/tasks/ReplayTask.java @@ -3,20 +3,25 @@ package me.unurled.sacredrealms.sr.components.cutscene.tasks; import static me.unurled.sacredrealms.sr.utils.Logger.error; import static me.unurled.srcore.utils.Component.textComp; +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.protocol.player.User; import me.unurled.sacredrealms.sr.components.cutscene.Replay; import me.unurled.sacredrealms.sr.components.cutscene.ReplayManager; import me.unurled.sacredrealms.sr.components.cutscene.recording.Recordable; import me.unurled.srcore.api.Manager; +import org.jetbrains.annotations.NotNull; public class ReplayTask implements Runnable { private final Replay replay; private final ReplayManager rm; + private final User user; private long currentTick = 0; - public ReplayTask(Replay replay) { + public ReplayTask(@NotNull Replay replay) { this.replay = replay; rm = Manager.getInstance(ReplayManager.class); + this.user = PacketEvents.getAPI().getPlayerManager().getUser(replay.viewer()); } @Override @@ -38,7 +43,7 @@ public class ReplayTask implements Runnable { for (Recordable r : recordables) { // figure out how to replay a recordable - r.replay(); + r.replay(replay, user); } } catch (Exception e) { error("Tick: " + currentTick); diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/pointer/DisplayForPlayer.java b/src/main/java/me/unurled/sacredrealms/sr/components/pointer/DisplayForPlayer.java new file mode 100644 index 0000000..5edfb8c --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/pointer/DisplayForPlayer.java @@ -0,0 +1,107 @@ +package me.unurled.sacredrealms.sr.components.pointer; + +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.protocol.entity.data.EntityData; +import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes; +import com.github.retrooper.packetevents.protocol.player.User; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity; +import io.github.retrooper.packetevents.util.SpigotConversionUtil; +import java.util.ArrayList; +import java.util.List; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.BlockState; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.joml.Vector3f; + +// TODO: test this +public class DisplayForPlayer implements Runnable { + + private static final int RENDER_DISTANCE_BLOCKS = 128; + + private final Pointer pointer; + private final Player player; + private final User user; + + public DisplayForPlayer(Pointer pointer, Player player) { + this.pointer = pointer; + this.player = player; + this.user = PacketEvents.getAPI().getPlayerManager().getUser(player); + } + + public static Location getLocationInRange(@NotNull Player player, Location targetLocation) { + Location playerLocation = player.getLocation(); + + // Calculate the distance between player and target location + double distance = playerLocation.distance(targetLocation); + + // If the location is within the render distance, return the original location + if (distance <= RENDER_DISTANCE_BLOCKS) { + return targetLocation; + } + + // If the location is out of range, calculate a new location within the render distance + // Calculate the direction vector from the player to the target + Location direction = + targetLocation + .clone() + .subtract(playerLocation) + .toVector() + .normalize() + .toLocation(player.getWorld()); + + // Create a new location within the render distance pointing towards the target location + return playerLocation.clone().add(direction.multiply(RENDER_DISTANCE_BLOCKS)); + } + + @Override + public void run() { + // get difference between player loc and pointer loc, if it is too large then set pointer + // blockdisplay's location to in range of player + + Location location = getLocationInRange(player, pointer.location()); + + WrapperPlayServerSpawnEntity packet = + new WrapperPlayServerSpawnEntity( + pointer.entityId(), + pointer.entityUUID(), + SpigotConversionUtil.fromBukkitEntityType(EntityType.BLOCK_DISPLAY), + SpigotConversionUtil.fromBukkitLocation(location), + 0, + 0, + null); + + user.sendPacket(packet); + + List list = new ArrayList<>(); + + BlockState ironBar = + player + .getWorld() + .getBlockAt(new Location(player.getWorld(), 0, 0, 0)) + .getState() + .getBlockData() + .createBlockState(); + ironBar.setType(Material.IRON_BARS); + + // make it iron bars + EntityData data = new EntityData(23, EntityDataTypes.BLOCK_STATE, ironBar); + list.add(data); + + // scale + EntityData data2 = new EntityData(12, EntityDataTypes.VECTOR3F, new Vector3f(2, 150, 2)); + list.add(data2); + + // glowing + EntityData glowing = new EntityData(0, EntityDataTypes.BYTE, (byte) 0x40); + list.add(glowing); + + WrapperPlayServerEntityMetadata packet1 = + new WrapperPlayServerEntityMetadata(pointer.entityId(), list); + + user.sendPacket(packet1); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/pointer/Pointer.java b/src/main/java/me/unurled/sacredrealms/sr/components/pointer/Pointer.java new file mode 100644 index 0000000..d378a28 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/pointer/Pointer.java @@ -0,0 +1,54 @@ +package me.unurled.sacredrealms.sr.components.pointer; + +import java.util.UUID; +import org.bukkit.Location; +import org.bukkit.entity.BlockDisplay; + +public class Pointer { + + private final Location location; + private final int entityId; + private BlockDisplay display; + private UUID entityUUID; + private Location entityLocation; + + public Pointer(int entityId, Location location) { + this.entityId = entityId; + this.location = location; + } + + public Location location() { + return location; + } + + public int entityId() { + return entityId; + } + + public BlockDisplay display() { + return display; + } + + public Pointer setDisplay(BlockDisplay display) { + this.display = display; + return this; + } + + public UUID entityUUID() { + return entityUUID; + } + + public Pointer setEntityUUID(UUID entityUUID) { + this.entityUUID = entityUUID; + return this; + } + + public Location entityLocation() { + return entityLocation; + } + + public Pointer setEntityLocation(Location entityLocation) { + this.entityLocation = entityLocation; + return this; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/pointer/PointerManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/pointer/PointerManager.java new file mode 100644 index 0000000..323ecd0 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/pointer/PointerManager.java @@ -0,0 +1,41 @@ +package me.unurled.sacredrealms.sr.components.pointer; + +import static me.unurled.sacredrealms.sr.utils.Logger.error; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import me.unurled.sacredrealms.sr.SR; +import me.unurled.srcore.api.Manager; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitTask; + +public class PointerManager extends Manager { + + private final Map tasks = new HashMap<>(); + + public void startPointing(Player player, Location location) { + int id = -1; + try { + Class entityClass = Class.forName("net.minecraft.world.entity.Entity"); + Field field = entityClass.getDeclaredField("ENTITY_COUNTER"); + field.setAccessible(true); + AtomicInteger ENTITY_COUNTER = (AtomicInteger) field.get(null); + id = ENTITY_COUNTER.incrementAndGet(); + } catch (Exception e) { + error("Failed to find Entity counter field in nms."); + } + if (id == -1) { + return; + } + Pointer pointer = new Pointer(id, location); + DisplayForPlayer task = new DisplayForPlayer(pointer, player); + BukkitTask btask = + Bukkit.getScheduler().runTaskTimerAsynchronously(SR.getPlugin(), task, 0, 5L); + tasks.put(player.getUniqueId(), btask); + } +}