Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Extended OfflinePlayer API (#605)
- Loading branch information
1 parent
4b01553
commit 710f21b
Showing
2 changed files
with
382 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||
From: YouHaveTrouble <youhavetrouble@youhavetrouble.me> | ||
Date: Sun, 22 Aug 2021 05:11:09 +0200 | ||
Subject: [PATCH] Extended OfflinePlayer API | ||
|
||
|
||
diff --git a/src/main/java/org/bukkit/OfflinePlayer.java b/src/main/java/org/bukkit/OfflinePlayer.java | ||
index 7838731e0e16bdccfb79e74ceb64148f7c52db79..8ea59406ddb2293af66719a893e6f13d8377b81e 100644 | ||
--- a/src/main/java/org/bukkit/OfflinePlayer.java | ||
+++ b/src/main/java/org/bukkit/OfflinePlayer.java | ||
@@ -437,4 +437,114 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio | ||
* for the statistic | ||
*/ | ||
public void setStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType, int newValue); | ||
+ | ||
+ // Purpur start - OfflinePlayer API | ||
+ /** | ||
+ * Determines if the OfflinePlayer is allowed to fly via jump key double-tap like | ||
+ * in creative mode. | ||
+ * | ||
+ * @return True if the player is allowed to fly. | ||
+ */ | ||
+ public boolean getAllowFlight(); | ||
+ | ||
+ /** | ||
+ * Sets if the OfflinePlayer is allowed to fly via jump key double-tap like in | ||
+ * creative mode. | ||
+ * | ||
+ * @param flight If flight should be allowed. | ||
+ */ | ||
+ public void setAllowFlight(boolean flight); | ||
+ | ||
+ /** | ||
+ * Checks to see if this player is currently flying or not. | ||
+ * | ||
+ * @return True if the player is flying, else false. | ||
+ */ | ||
+ public boolean isFlying(); | ||
+ | ||
+ /** | ||
+ * Makes this player start or stop flying. | ||
+ * | ||
+ * @param value True to fly. | ||
+ */ | ||
+ public void setFlying(boolean value); | ||
+ | ||
+ /** | ||
+ * Sets the speed at which a client will fly. Negative values indicate | ||
+ * reverse directions. | ||
+ * | ||
+ * @param value The new speed, from -1 to 1. | ||
+ * @throws IllegalArgumentException If new speed is less than -1 or | ||
+ * greater than 1 | ||
+ */ | ||
+ public void setFlySpeed(float value) throws IllegalArgumentException; | ||
+ | ||
+ /** | ||
+ * Sets the speed at which a client will walk. Negative values indicate | ||
+ * reverse directions. | ||
+ * | ||
+ * @param value The new speed, from -1 to 1. | ||
+ * @throws IllegalArgumentException If new speed is less than -1 or | ||
+ * greater than 1 | ||
+ */ | ||
+ public void setWalkSpeed(float value) throws IllegalArgumentException; | ||
+ | ||
+ /** | ||
+ * Gets the current allowed speed that a client can fly. | ||
+ * | ||
+ * @return The current allowed speed, from -1 to 1 | ||
+ */ | ||
+ public float getFlySpeed(); | ||
+ | ||
+ /** | ||
+ * Gets the current allowed speed that a client can walk. | ||
+ * | ||
+ * @return The current allowed speed, from -1 to 1 | ||
+ */ | ||
+ public float getWalkSpeed(); | ||
+ | ||
+ /** | ||
+ * Gets the entity's current position | ||
+ * | ||
+ * @return a new copy of Location containing the position of this offline player | ||
+ */ | ||
+ @Nullable | ||
+ public Location getLocation(); | ||
+ | ||
+ /** | ||
+ * Sets OfflinePlayer's location. If player is online, it falls back to the Player#teleport implementation. | ||
+ * | ||
+ * @param destination | ||
+ * @return true if teleportation was successful | ||
+ */ | ||
+ public boolean teleportOffline(@NotNull org.bukkit.Location destination); | ||
+ | ||
+ /** | ||
+ * Sets OfflinePlayer's location. If player is online, it falls back to the Player#teleport implementation. | ||
+ * | ||
+ * @param destination | ||
+ * @param cause Teleport cause used if player is online | ||
+ * @return true if teleportation was successful | ||
+ */ | ||
+ public boolean teleportOffline(@NotNull org.bukkit.Location destination, @NotNull org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause); | ||
+ | ||
+ /** | ||
+ * Sets OfflinePlayer's location. If player is online, it falls back to the Player#teleportAsync implementation. | ||
+ * | ||
+ * @param destination | ||
+ * @return <code>true</code> if teleportation successful | ||
+ */ | ||
+ @NotNull | ||
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(@NotNull Location destination); | ||
+ | ||
+ /** | ||
+ * Sets OfflinePlayer's location. If player is online, it falls back to the Player#teleportAsync implementation. | ||
+ * | ||
+ * @param destination | ||
+ * @param cause Teleport cause used if player is online | ||
+ * @return <code>true</code> if teleportation successful | ||
+ */ | ||
+ @NotNull | ||
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(@NotNull Location destination, @NotNull org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause); | ||
+ // Purpur end - OfflinePlayer API | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||
From: YouHaveTrouble <youhavetrouble@youhavetrouble.me> | ||
Date: Sun, 22 Aug 2021 05:12:05 +0200 | ||
Subject: [PATCH] Extended OfflinePlayer API | ||
|
||
|
||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java | ||
index b20bfe5ab165bf86985e5ff2f93f415d9710e0e4..830d3163990002c7c1ba0a5a63531fa1bfff507d 100644 | ||
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java | ||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java | ||
@@ -525,4 +525,213 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa | ||
manager.save(); | ||
} | ||
} | ||
+ | ||
+ // Purpur start - OfflinePlayer API | ||
+ @Override | ||
+ public boolean getAllowFlight() { | ||
+ if (this.isOnline()) { | ||
+ return this.getPlayer().getAllowFlight(); | ||
+ } else { | ||
+ CompoundTag data = this.getData(); | ||
+ if (data == null) return false; | ||
+ if (!data.contains("abilities")) return false; | ||
+ CompoundTag abilities = data.getCompound("abilities"); | ||
+ return abilities.getByte("mayfly") == (byte) 1; | ||
+ } | ||
+ } | ||
+ | ||
+ @Override | ||
+ public void setAllowFlight(boolean flight) { | ||
+ if (this.isOnline()) { | ||
+ this.getPlayer().setAllowFlight(flight); | ||
+ } else { | ||
+ CompoundTag data = this.getData(); | ||
+ if (data == null) return; | ||
+ if (!data.contains("abilities")) return; | ||
+ CompoundTag abilities = data.getCompound("abilities"); | ||
+ abilities.putByte("mayfly", (byte) (flight ? 1 : 0)); | ||
+ data.put("abilities", abilities); | ||
+ save(data); | ||
+ } | ||
+ } | ||
+ | ||
+ @Override | ||
+ public boolean isFlying() { | ||
+ if (this.isOnline()) { | ||
+ return this.isFlying(); | ||
+ } else { | ||
+ CompoundTag data = this.getData(); | ||
+ if (data == null) return false; | ||
+ if (!data.contains("abilities")) return false; | ||
+ CompoundTag abilities = data.getCompound("abilities"); | ||
+ return abilities.getByte("flying") == (byte) 1; | ||
+ } | ||
+ } | ||
+ | ||
+ @Override | ||
+ public void setFlying(boolean value) { | ||
+ if (this.isOnline()) { | ||
+ this.getPlayer().setFlying(value); | ||
+ } else { | ||
+ CompoundTag data = this.getData(); | ||
+ if (data == null) return; | ||
+ if (!data.contains("abilities")) return; | ||
+ CompoundTag abilities = data.getCompound("abilities"); | ||
+ abilities.putByte("mayfly", (byte) (value ? 1 : 0)); | ||
+ data.put("abilities", abilities); | ||
+ save(data); | ||
+ } | ||
+ } | ||
+ | ||
+ @Override | ||
+ public void setFlySpeed(float value) throws IllegalArgumentException { | ||
+ if (value < -1f || value > 1f) throw new IllegalArgumentException("FlySpeed needs to be between -1 and 1"); | ||
+ if (this.isOnline()) { | ||
+ this.getPlayer().setFlySpeed(value); | ||
+ } else { | ||
+ CompoundTag data = this.getData(); | ||
+ if (data == null) return; | ||
+ if (!data.contains("abilities")) return; | ||
+ CompoundTag abilities = data.getCompound("abilities"); | ||
+ abilities.putFloat("flySpeed", value); | ||
+ data.put("abilities", abilities); | ||
+ save(data); | ||
+ } | ||
+ } | ||
+ | ||
+ @Override | ||
+ public float getFlySpeed() { | ||
+ if (this.isOnline()) { | ||
+ return this.getPlayer().getFlySpeed(); | ||
+ } else { | ||
+ CompoundTag data = this.getData(); | ||
+ if (data == null) return 0; | ||
+ if (!data.contains("abilities")) return 0; | ||
+ CompoundTag abilities = data.getCompound("abilities"); | ||
+ return abilities.getFloat("flySpeed"); | ||
+ } | ||
+ } | ||
+ | ||
+ @Override | ||
+ public void setWalkSpeed(float value) throws IllegalArgumentException { | ||
+ if (value < -1f || value > 1f) throw new IllegalArgumentException("WalkSpeed needs to be between -1 and 1"); | ||
+ if (this.isOnline()) { | ||
+ this.getPlayer().setWalkSpeed(value); | ||
+ } else { | ||
+ CompoundTag data = this.getData(); | ||
+ if (data == null) return; | ||
+ if (!data.contains("abilities")) return; | ||
+ CompoundTag abilities = data.getCompound("abilities"); | ||
+ abilities.putFloat("walkSpeed", value); | ||
+ data.put("abilities", abilities); | ||
+ save(data); | ||
+ } | ||
+ } | ||
+ | ||
+ @Override | ||
+ public float getWalkSpeed() { | ||
+ if (this.isOnline()) { | ||
+ return this.getPlayer().getWalkSpeed(); | ||
+ } else { | ||
+ CompoundTag data = this.getData(); | ||
+ if (data == null) return 0; | ||
+ if (!data.contains("abilities")) return 0; | ||
+ CompoundTag abilities = data.getCompound("abilities"); | ||
+ return abilities.getFloat("walkSpeed"); | ||
+ } | ||
+ } | ||
+ | ||
+ @Override | ||
+ public Location getLocation() { | ||
+ if (this.isOnline()) { | ||
+ return this.getPlayer().getLocation(); | ||
+ } else { | ||
+ CompoundTag data = this.getData(); | ||
+ if (data == null) return null; | ||
+ long worldUUIDMost = data.getLong("WorldUUIDMost"); | ||
+ long worldUUIDLeast = data.getLong("WorldUUIDLeast"); | ||
+ net.minecraft.nbt.ListTag position = data.getList("Pos", org.bukkit.craftbukkit.util.CraftMagicNumbers.NBT.TAG_DOUBLE); | ||
+ net.minecraft.nbt.ListTag rotation = data.getList("Rotation", org.bukkit.craftbukkit.util.CraftMagicNumbers.NBT.TAG_FLOAT); | ||
+ UUID worldUuid = new UUID(worldUUIDMost, worldUUIDLeast); | ||
+ org.bukkit.World world = server.getWorld(worldUuid); | ||
+ double x = position.getDouble(0); | ||
+ double y = position.getDouble(1); | ||
+ double z = position.getDouble(2); | ||
+ float yaw = rotation.getFloat(0); | ||
+ float pitch = rotation.getFloat(1); | ||
+ return new Location(world, x, y, z, yaw, pitch); | ||
+ } | ||
+ } | ||
+ | ||
+ @Override | ||
+ public boolean teleportOffline(Location destination) { | ||
+ if (this.isOnline()) { | ||
+ return this.getPlayer().teleport(destination); | ||
+ } else { | ||
+ return setLocation(destination); | ||
+ } | ||
+ } | ||
+ | ||
+ @Override | ||
+ public boolean teleportOffline(Location destination, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause){ | ||
+ if (this.isOnline()) { | ||
+ return this.getPlayer().teleport(destination, cause); | ||
+ } else { | ||
+ return setLocation(destination); | ||
+ } | ||
+ } | ||
+ | ||
+ @Override | ||
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(Location destination) { | ||
+ if (this.isOnline()) { | ||
+ return this.getPlayer().teleportAsync(destination); | ||
+ } else { | ||
+ return java.util.concurrent.CompletableFuture.completedFuture(setLocation(destination)); | ||
+ } | ||
+ } | ||
+ | ||
+ @Override | ||
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(Location destination, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { | ||
+ if (this.isOnline()) { | ||
+ return this.getPlayer().teleportAsync(destination, cause); | ||
+ } else { | ||
+ return java.util.concurrent.CompletableFuture.completedFuture(setLocation(destination)); | ||
+ } | ||
+ } | ||
+ | ||
+ private boolean setLocation(Location location) { | ||
+ CompoundTag data = this.getData(); | ||
+ if (data == null) return false; | ||
+ data.putLong("WorldUUIDMost", location.getWorld().getUID().getMostSignificantBits()); | ||
+ data.putLong("WorldUUIDLeast", location.getWorld().getUID().getLeastSignificantBits()); | ||
+ net.minecraft.nbt.ListTag position = new net.minecraft.nbt.ListTag(); | ||
+ position.add(net.minecraft.nbt.DoubleTag.valueOf(location.getX())); | ||
+ position.add(net.minecraft.nbt.DoubleTag.valueOf(location.getY())); | ||
+ position.add(net.minecraft.nbt.DoubleTag.valueOf(location.getZ())); | ||
+ data.put("Pos", position); | ||
+ net.minecraft.nbt.ListTag rotation = new net.minecraft.nbt.ListTag(); | ||
+ rotation.add(net.minecraft.nbt.FloatTag.valueOf(location.getYaw())); | ||
+ rotation.add(net.minecraft.nbt.FloatTag.valueOf(location.getPitch())); | ||
+ data.put("Rotation", rotation); | ||
+ save(data); | ||
+ return true; | ||
+ } | ||
+ | ||
+ /** | ||
+ * Safely replaces player's .dat file with provided CompoundTag | ||
+ * @param compoundTag | ||
+ */ | ||
+ private void save(CompoundTag compoundTag) { | ||
+ File playerDir = server.console.playerDataStorage.getPlayerDir(); | ||
+ try { | ||
+ File tempFile = File.createTempFile(this.getUniqueId()+"-", ".dat", playerDir); | ||
+ net.minecraft.nbt.NbtIo.writeCompressed(compoundTag, tempFile); | ||
+ File playerDataFile = new File(playerDir, this.getUniqueId()+".dat"); | ||
+ File playerDataFileOld = new File(playerDir, this.getUniqueId()+".dat_old"); | ||
+ net.minecraft.Util.safeReplaceFile(playerDataFile, tempFile, playerDataFileOld); | ||
+ } catch (java.io.IOException e) { | ||
+ e.printStackTrace(); | ||
+ } | ||
+ } | ||
+ // Purpur end - OfflinePlayer API | ||
} | ||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java | ||
index 3230631551e530afdf0022a4c863cc57c0374df2..d0408f5554b68aa2009ba7d67591c3c628403deb 100644 | ||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java | ||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java | ||
@@ -1925,6 +1925,28 @@ public class CraftPlayer extends CraftHumanEntity implements Player { | ||
return this.getHandle().getAbilities().walkingSpeed * 2f; | ||
} | ||
|
||
+ // Purpur start - OfflinePlayer API | ||
+ @Override | ||
+ public boolean teleportOffline(@NotNull Location destination) { | ||
+ return ((OfflinePlayer)this).teleportOffline(destination); | ||
+ } | ||
+ | ||
+ @Override | ||
+ public boolean teleportOffline(Location destination, PlayerTeleportEvent.TeleportCause cause) { | ||
+ return ((OfflinePlayer)this).teleportOffline(destination, cause); | ||
+ } | ||
+ | ||
+ @Override | ||
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(@NotNull Location destination) { | ||
+ return ((OfflinePlayer)this).teleportOfflineAsync(destination); | ||
+ } | ||
+ | ||
+ @Override | ||
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(@NotNull Location destination, PlayerTeleportEvent.TeleportCause cause) { | ||
+ return ((OfflinePlayer)this).teleportOfflineAsync(destination, cause); | ||
+ } | ||
+ // Purpur end - OfflinePlayer API | ||
+ | ||
private void validateSpeed(float value) { | ||
if (value < 0) { | ||
if (value < -1f) { |