aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0715-More-Teleport-API.patch
blob: 7f4fab5c3051ae2eb798ac9abbe2322012c23a31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
Date: Sun, 5 Sep 2021 12:15:59 -0400
Subject: [PATCH] More Teleport API


diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index bc8f9bd50de3894e6262e13ed55252c98f22ed8a..4d64eedfbe5b967572b7140ddfb55efa1ccc3650 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1585,11 +1585,17 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
             return true; // CraftBukkit - Return event status
         }
 
-        PlayerTeleportEvent event = new PlayerTeleportEvent(player, from.clone(), to.clone(), cause);
+        // Paper start - Teleport API
+        Set<io.papermc.paper.entity.TeleportFlag.Relative> relativeFlags = java.util.EnumSet.noneOf(io.papermc.paper.entity.TeleportFlag.Relative.class);
+        for (Relative relativeArgument : set) {
+            relativeFlags.add(org.bukkit.craftbukkit.entity.CraftPlayer.toApiRelativeFlag(relativeArgument));
+        }
+        PlayerTeleportEvent event = new PlayerTeleportEvent(player, from.clone(), to.clone(), cause, java.util.Set.copyOf(relativeFlags));
+        // Paper end - Teleport API
         this.cserver.getPluginManager().callEvent(event);
 
         if (event.isCancelled() || !to.equals(event.getTo())) {
-            set = Collections.emptySet(); // Can't relative teleport
+            // set = Collections.emptySet(); // Can't relative teleport // Paper - Teleport API; Now you can!
             to = event.isCancelled() ? event.getFrom() : event.getTo();
             positionmoverotation = new PositionMoveRotation(CraftLocation.toVec3D(to), Vec3.ZERO, to.getYaw(), to.getPitch());
         }
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 4e6afa243d6108cb946a8a7cf96c4036a3c2ac0c..a314e401ee8a306dc12a2d98a3d400ae611a6e5a 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -222,15 +222,36 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
 
     @Override
     public boolean teleport(Location location, TeleportCause cause) {
+        // Paper start - Teleport passenger API
+        return teleport(location, cause, new io.papermc.paper.entity.TeleportFlag[0]);
+    }
+
+    @Override
+    public boolean teleport(Location location, TeleportCause cause, io.papermc.paper.entity.TeleportFlag... flags) {
+        // Paper end
         Preconditions.checkArgument(location != null, "location cannot be null");
         location.checkFinite();
+        // Paper start - Teleport passenger API
+        Set<io.papermc.paper.entity.TeleportFlag> flagSet = Set.of(flags);
+        boolean dismount = !flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_VEHICLE);
+        boolean ignorePassengers = flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS);
+        // Don't allow teleporting between worlds while keeping passengers
+        if (flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS) && this.entity.isVehicle() && location.getWorld() != this.getWorld()) {
+            return false;
+        }
+
+        // Don't allow to teleport between worlds if remaining on vehicle
+        if (!dismount && this.entity.isPassenger() && location.getWorld() != this.getWorld()) {
+            return false;
+        }
+        // Paper end
 
-        if (this.entity.isVehicle() || this.entity.isRemoved()) {
+        if ((!ignorePassengers && this.entity.isVehicle()) || this.entity.isRemoved()) { // Paper - Teleport passenger API
             return false;
         }
 
         // If this entity is riding another entity, we must dismount before teleporting.
-        this.entity.stopRiding();
+        if (dismount) this.entity.stopRiding(); // Paper - Teleport passenger API
 
         // Let the server handle cross world teleports
         if (location.getWorld() != null && !location.getWorld().equals(this.getWorld())) {
@@ -976,6 +997,39 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
         return CraftEntity.perm;
     }
 
+    // Paper start - more teleport API / async chunk API
+    @Override
+    public java.util.concurrent.CompletableFuture<Boolean> teleportAsync(final Location location, final TeleportCause cause, final io.papermc.paper.entity.TeleportFlag... teleportFlags) {
+        Preconditions.checkArgument(location != null, "location");
+        location.checkFinite();
+        Location locationClone = location.clone(); // clone so we don't need to worry about mutations after this call.
+
+        net.minecraft.server.level.ServerLevel world = ((CraftWorld)locationClone.getWorld()).getHandle();
+        java.util.concurrent.CompletableFuture<Boolean> ret = new java.util.concurrent.CompletableFuture<>();
+
+        world.loadChunksForMoveAsync(getHandle().getBoundingBoxAt(locationClone.getX(), locationClone.getY(), locationClone.getZ()),
+            this instanceof CraftPlayer ? ca.spottedleaf.concurrentutil.util.Priority.HIGHER : ca.spottedleaf.concurrentutil.util.Priority.NORMAL, (list) -> {
+                net.minecraft.server.level.ServerChunkCache chunkProviderServer = world.getChunkSource();
+                for (net.minecraft.world.level.chunk.ChunkAccess chunk : list) {
+                    chunkProviderServer.addTicketAtLevel(net.minecraft.server.level.TicketType.POST_TELEPORT, chunk.getPos(), 33, CraftEntity.this.getEntityId());
+                }
+                net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> {
+                    try {
+                        ret.complete(CraftEntity.this.teleport(locationClone, cause, teleportFlags) ? Boolean.TRUE : Boolean.FALSE);
+                    } catch (Throwable throwable) {
+                        if (throwable instanceof ThreadDeath) {
+                            throw (ThreadDeath)throwable;
+                        }
+                        net.minecraft.server.MinecraftServer.LOGGER.error("Failed to teleport entity " + CraftEntity.this, throwable);
+                        ret.completeExceptionally(throwable);
+                    }
+                });
+            });
+
+        return ret;
+    }
+    // Paper end - more teleport API / async chunk API
+
     // Spigot start
     private final org.bukkit.entity.Entity.Spigot spigot = new org.bukkit.entity.Entity.Spigot()
     {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index fe0c355f8203c9bfa30d2ec48392a5a1a3d616ae..baae7a129853a296273b7f295f58cbb99187da22 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -1303,13 +1303,102 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
 
     @Override
     public void setRotation(float yaw, float pitch) {
-        throw new UnsupportedOperationException("Cannot set rotation of players. Consider teleporting instead.");
+        // Paper start - Teleport API
+        Location targetLocation = this.getEyeLocation();
+        targetLocation.setYaw(yaw);
+        targetLocation.setPitch(pitch);
+
+        org.bukkit.util.Vector direction = targetLocation.getDirection();
+        direction.multiply(9999999); // We need to move the target block.. FAR out
+        targetLocation.add(direction);
+        this.lookAt(targetLocation, io.papermc.paper.entity.LookAnchor.EYES);
+        // Paper end
     }
 
     @Override
     public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
+        // Paper start - Teleport API
+        return this.teleport(location, cause, new io.papermc.paper.entity.TeleportFlag[0]);
+    }
+
+    @Override
+    public void lookAt(@NotNull org.bukkit.entity.Entity entity, @NotNull io.papermc.paper.entity.LookAnchor playerAnchor, @NotNull io.papermc.paper.entity.LookAnchor entityAnchor) {
+        this.getHandle().lookAt(toNmsAnchor(playerAnchor), ((CraftEntity) entity).getHandle(), toNmsAnchor(entityAnchor));
+    }
+
+    @Override
+    public void lookAt(double x, double y, double z, @NotNull io.papermc.paper.entity.LookAnchor playerAnchor) {
+        this.getHandle().lookAt(toNmsAnchor(playerAnchor), new net.minecraft.world.phys.Vec3(x, y, z));
+    }
+
+    public static net.minecraft.commands.arguments.EntityAnchorArgument.Anchor toNmsAnchor(io.papermc.paper.entity.LookAnchor nmsAnchor) {
+        return switch (nmsAnchor) {
+            case EYES -> net.minecraft.commands.arguments.EntityAnchorArgument.Anchor.EYES;
+            case FEET -> net.minecraft.commands.arguments.EntityAnchorArgument.Anchor.FEET;
+        };
+    }
+
+    public static io.papermc.paper.entity.LookAnchor toApiAnchor(net.minecraft.commands.arguments.EntityAnchorArgument.Anchor playerAnchor) {
+        return switch (playerAnchor) {
+            case EYES -> io.papermc.paper.entity.LookAnchor.EYES;
+            case FEET -> io.papermc.paper.entity.LookAnchor.FEET;
+        };
+    }
+
+    public static net.minecraft.world.entity.Relative toNmsRelativeFlag(io.papermc.paper.entity.TeleportFlag.Relative apiFlag) {
+        return switch (apiFlag) {
+            case X -> net.minecraft.world.entity.Relative.X;
+            case Y -> net.minecraft.world.entity.Relative.Y;
+            case Z -> net.minecraft.world.entity.Relative.Z;
+            case PITCH -> net.minecraft.world.entity.Relative.X_ROT;
+            case YAW -> net.minecraft.world.entity.Relative.Y_ROT;
+        };
+    }
+
+    public static io.papermc.paper.entity.TeleportFlag.Relative toApiRelativeFlag(net.minecraft.world.entity.Relative nmsFlag) {
+        return switch (nmsFlag) {
+            case X -> io.papermc.paper.entity.TeleportFlag.Relative.X;
+            case Y -> io.papermc.paper.entity.TeleportFlag.Relative.Y;
+            case Z -> io.papermc.paper.entity.TeleportFlag.Relative.Z;
+            case X_ROT -> io.papermc.paper.entity.TeleportFlag.Relative.PITCH;
+            case Y_ROT -> io.papermc.paper.entity.TeleportFlag.Relative.YAW;
+            default -> throw new RuntimeException("not yet"); // TODO figure out what to do with new flags
+        };
+    }
+
+    @Override
+    public boolean teleport(Location location, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause, io.papermc.paper.entity.TeleportFlag... flags) {
+        Set<io.papermc.paper.entity.TeleportFlag.Relative> relativeArguments;
+        Set<io.papermc.paper.entity.TeleportFlag> allFlags;
+        if (flags.length == 0) {
+            relativeArguments = Set.of();
+            allFlags = Set.of();
+        } else {
+            relativeArguments = java.util.EnumSet.noneOf(io.papermc.paper.entity.TeleportFlag.Relative.class);
+            allFlags = new HashSet<>();
+            for (io.papermc.paper.entity.TeleportFlag flag : flags) {
+                if (flag instanceof final io.papermc.paper.entity.TeleportFlag.Relative relativeTeleportFlag) {
+                    relativeArguments.add(relativeTeleportFlag);
+                }
+                allFlags.add(flag);
+            }
+        }
+        boolean dismount = !allFlags.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_VEHICLE);
+        boolean ignorePassengers = allFlags.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS);
+        // Paper end - Teleport API
         Preconditions.checkArgument(location != null, "location");
         Preconditions.checkArgument(location.getWorld() != null, "location.world");
+        // Paper start - Teleport passenger API
+        // Don't allow teleporting between worlds while keeping passengers
+        if (ignorePassengers && entity.isVehicle() && location.getWorld() != this.getWorld()) {
+            return false;
+        }
+
+        // Don't allow to teleport between worlds if remaining on vehicle
+        if (!dismount && entity.isPassenger() && location.getWorld() != this.getWorld()) {
+            return false;
+        }
+        // Paper end
         location.checkFinite();
 
         ServerPlayer entity = this.getHandle();
@@ -1322,7 +1411,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
             return false;
         }
 
-        if (entity.isVehicle()) {
+        if (entity.isVehicle() && !ignorePassengers) { // Paper - Teleport API
             return false;
         }
 
@@ -1331,7 +1420,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         // To = Players new Location if Teleport is Successful
         Location to = location;
         // Create & Call the Teleport Event.
-        PlayerTeleportEvent event = new PlayerTeleportEvent(this, from, to, cause);
+        PlayerTeleportEvent event = new PlayerTeleportEvent(this, from, to, cause, Set.copyOf(relativeArguments)); // Paper - Teleport API
         this.server.getPluginManager().callEvent(event);
 
         // Return False to inform the Plugin that the Teleport was unsuccessful/cancelled.
@@ -1340,7 +1429,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         }
 
         // If this player is riding another entity, we must dismount before teleporting.
-        entity.stopRiding();
+        if (dismount) entity.stopRiding(); // Paper - Teleport API
 
         // SPIGOT-5509: Wakeup, similar to riding
         if (this.isSleeping()) {
@@ -1356,13 +1445,21 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         ServerLevel toWorld = ((CraftWorld) to.getWorld()).getHandle();
 
         // Close any foreign inventory
-        if (this.getHandle().containerMenu != this.getHandle().inventoryMenu) {
+        if (this.getHandle().containerMenu != this.getHandle().inventoryMenu && !allFlags.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_OPEN_INVENTORY)) { // Paper
             this.getHandle().closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.TELEPORT); // Paper - Inventory close reason
         }
 
         // Check if the fromWorld and toWorld are the same.
         if (fromWorld == toWorld) {
-            entity.connection.teleport(to);
+            // Paper start - Teleport API
+            final Set<net.minecraft.world.entity.Relative> nms = java.util.EnumSet.noneOf(net.minecraft.world.entity.Relative.class);
+            for (final io.papermc.paper.entity.TeleportFlag.Relative bukkit : relativeArguments) {
+                nms.add(toNmsRelativeFlag(bukkit));
+            }
+            entity.connection.internalTeleport(new net.minecraft.world.entity.PositionMoveRotation(
+                io.papermc.paper.util.MCUtil.toVec3(to), net.minecraft.world.phys.Vec3.ZERO, to.getYaw(), to.getPitch()
+            ), nms);
+            // Paper end - Teleport API
         } else {
             entity.portalProcess = null; // SPIGOT-7785: there is no need to carry this over as it contains the old world/location and we might run into trouble if there is a portal in the same spot in both worlds
             // The respawn reason should never be used if the passed location is non null.