and some more patches

This commit is contained in:
Jake 2021-11-23 16:44:41 -08:00 committed by MiniDigger | Martin
parent 76879ba957
commit 9ae92d26b8
15 changed files with 62 additions and 74 deletions

View file

@ -0,0 +1,134 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Thu, 15 Nov 2018 13:38:37 +0000
Subject: [PATCH] force entity dismount during teleportation
Entities must be dismounted before teleportation in order to avoid
multiple issues in the server with regards to teleportation, shamefully,
too many plugins rely on the events firing, which means that not firing
these events caues more issues than it solves;
In order to counteract this, Entity dismount/exit vehicle events have
been modified to supress cancellation (and has a method to allow plugins
to check if this has been set), noting that cancellation will be silently
surpressed given that plugins are not expecting this event to not be cancellable.
This is a far from ideal scenario, however: given the current state of this
event and other alternatives causing issues elsewhere, I believe that
this is going to be the best soultion all around.
Improvements/suggestions welcome!
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index db7f2715534ed71a2b285de095238586fe6a35b0..f51c416e7938b7905f7efb154ab14cad643962e9 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -1323,11 +1323,13 @@ public class ServerPlayer extends Player {
}
}
- @Override
- public void stopRiding() {
+ // Paper start
+ @Override public void stopRiding() { stopRiding(false); }
+ @Override public void stopRiding(boolean suppressCancellation) {
+ // Paper end
Entity entity = this.getVehicle();
- super.stopRiding();
+ super.stopRiding(suppressCancellation); // Paper
Entity entity1 = this.getVehicle();
if (entity1 != entity && this.connection != null) {
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 752e4593bed746e663f018f85ae1ac43cd7b0795..a1744b820a49cd4195b0e9ced960dc6bd2d58d45 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -2279,11 +2279,16 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
}
public void removeVehicle() {
+ // Paper start
+ stopRiding(false);
+ }
+ public void stopRiding(boolean suppressCancellation) {
+ // Paper end
if (this.vehicle != null) {
Entity entity = this.vehicle;
this.vehicle = null;
- if (!entity.removePassenger(this)) this.vehicle = entity; // CraftBukkit
+ if (!entity.removePassenger(this, suppressCancellation)) this.vehicle = entity; // CraftBukkit // Paper
}
}
@@ -2346,7 +2351,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
return true; // CraftBukkit
}
- protected boolean removePassenger(Entity entity) { // CraftBukkit
+ // Paper start
+ protected boolean removePassenger(Entity entity) { return removePassenger(entity, false);}
+ protected boolean removePassenger(Entity entity, boolean suppressCancellation) { // CraftBukkit
+ // Paper end
if (entity.getVehicle() == this) {
throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)");
} else {
@@ -2356,7 +2364,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
if (this.getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) {
VehicleExitEvent event = new VehicleExitEvent(
(Vehicle) this.getBukkitEntity(),
- (LivingEntity) entity.getBukkitEntity()
+ (LivingEntity) entity.getBukkitEntity(), !suppressCancellation // Paper
);
// Suppress during worldgen
if (this.valid) {
@@ -2370,7 +2378,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
}
// CraftBukkit end
// Spigot start
- org.spigotmc.event.entity.EntityDismountEvent event = new org.spigotmc.event.entity.EntityDismountEvent(entity.getBukkitEntity(), this.getBukkitEntity());
+ org.spigotmc.event.entity.EntityDismountEvent event = new org.spigotmc.event.entity.EntityDismountEvent(entity.getBukkitEntity(), this.getBukkitEntity(), !suppressCancellation); // Paper
// Suppress during worldgen
if (this.valid) {
Bukkit.getPluginManager().callEvent(event);
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 5d47b536e89f192b10aa1434cd9cdccc49cd3eec..00900427e88f8e9118df4fb2e0e98c26b9f1b7d4 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -3336,9 +3336,15 @@ public abstract class LivingEntity extends Entity {
@Override
public void stopRiding() {
+ // Paper start
+ stopRiding(false);
+ }
+ @Override
+ public void stopRiding(boolean suppressCancellation) {
+ // Paper end
Entity entity = this.getVehicle();
- super.stopRiding();
+ super.stopRiding(suppressCancellation); // Paper - suppress
if (entity != null && entity != this.getVehicle() && !this.level.isClientSide) {
this.dismountVehicle(entity);
}
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
index 37b65c7a12b4dc8ff4e9017015abbb86890dbf5e..6927eeb04a9aa1a33666f3655f2bb889bf046dc9 100644
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
@@ -1094,7 +1094,13 @@ public abstract class Player extends LivingEntity {
@Override
public void removeVehicle() {
- super.removeVehicle();
+ // Paper start
+ stopRiding(false);
+ }
+ @Override
+ public void stopRiding(boolean suppressCancellation) {
+ // Paper end
+ super.stopRiding(suppressCancellation); // Paper - suppress
this.boardingCooldown = 0;
}

View file

@ -0,0 +1,115 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Sun, 7 Oct 2018 04:29:59 -0500
Subject: [PATCH] Add more Zombie API
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
index 42f3f632caba1897b762a6897923585901a63bd1..8c7c621f09e8669a6874a2ce101972cb1c03f8ae 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
@@ -93,6 +93,7 @@ public class Zombie extends Monster {
private int inWaterTime;
public int conversionTime;
private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field
+ private boolean shouldBurnInDay = true; // Paper
public Zombie(EntityType<? extends Zombie> type, Level world) {
super(type, world);
@@ -261,6 +262,12 @@ public class Zombie extends Monster {
super.aiStep();
}
+ // Paper start
+ public void stopDrowning() {
+ this.conversionTime = -1;
+ this.getEntityData().set(Zombie.DATA_DROWNED_CONVERSION_ID, false);
+ }
+ // Paper end
public void startUnderWaterConversion(int ticksUntilWaterConversion) {
this.lastTick = MinecraftServer.currentTick; // CraftBukkit
this.conversionTime = ticksUntilWaterConversion;
@@ -290,9 +297,15 @@ public class Zombie extends Monster {
}
public boolean isSunSensitive() {
- return true;
+ return this.shouldBurnInDay; // Paper - use api value instead
}
+ // Paper start
+ public void setShouldBurnInDay(boolean shouldBurnInDay) {
+ this.shouldBurnInDay = shouldBurnInDay;
+ }
+ // Paper end
+
@Override
public boolean hurt(DamageSource source, float amount) {
if (!super.hurt(source, amount)) {
@@ -412,6 +425,7 @@ public class Zombie extends Monster {
nbt.putBoolean("CanBreakDoors", this.canBreakDoors());
nbt.putInt("InWaterTime", this.isInWater() ? this.inWaterTime : -1);
nbt.putInt("DrownedConversionTime", this.isUnderWaterConverting() ? this.conversionTime : -1);
+ nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper
}
@Override
@@ -423,6 +437,11 @@ public class Zombie extends Monster {
if (nbt.contains("DrownedConversionTime", 99) && nbt.getInt("DrownedConversionTime") > -1) {
this.startUnderWaterConversion(nbt.getInt("DrownedConversionTime"));
}
+ // Paper start
+ if (nbt.contains("Paper.ShouldBurnInDay")) {
+ this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay");
+ }
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java
index 2e643e92569512ac4e75c0ef2ee7aa4b688e7356..77e4875484bdaedfba576a6b008245c488b2a112 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java
@@ -93,6 +93,42 @@ public class CraftZombie extends CraftMonster implements Zombie {
@Override
public void setAgeLock(boolean b) {
}
+ // Paper start
+ @Override
+ public boolean isDrowning() {
+ return getHandle().isUnderWaterConverting();
+ }
+
+ @Override
+ public void startDrowning(int drownedConversionTime) {
+ getHandle().startUnderWaterConversion(drownedConversionTime);
+ }
+
+ @Override
+ public void stopDrowning() {
+ getHandle().stopDrowning();
+ }
+
+ @Override
+ public boolean shouldBurnInDay() {
+ return getHandle().isSunSensitive();
+ }
+
+ @Override
+ public boolean isArmsRaised() {
+ return getHandle().isAggressive();
+ }
+
+ @Override
+ public void setArmsRaised(final boolean raised) {
+ getHandle().setAggressive(raised);
+ }
+
+ @Override
+ public void setShouldBurnInDay(boolean shouldBurnInDay) {
+ getHandle().setShouldBurnInDay(shouldBurnInDay);
+ }
+ // Paper end
@Override
public boolean getAgeLock() {

View file

@ -0,0 +1,75 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Fri, 16 Nov 2018 23:08:50 -0500
Subject: [PATCH] Book Size Limits
Puts some limits on the size of books.
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
index 769353df1fcdaacecd80085165a1d72f99b577ee..4875e323e8ba52cf91259262b8418310061718ad 100644
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
@@ -368,6 +368,13 @@ public class PaperConfig {
}
}
+ public static int maxBookPageSize = 2560;
+ public static double maxBookTotalSizeMultiplier = 0.98D;
+ private static void maxBookSize() {
+ maxBookPageSize = Math.min(8192, getInt("settings.book-size.page-max", maxBookPageSize));
+ maxBookTotalSizeMultiplier = getDouble("settings.book-size.total-multiplier", maxBookTotalSizeMultiplier);
+ }
+
public static boolean asyncChunks = false;
private static void asyncChunks() {
ConfigurationSection section;
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 25ec1613f4c9ecc59ec5e27717ff7de9ad9e4398..2d2b1a293a1359ffa6d33635856406a909c452ce 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1000,6 +1000,45 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
@Override
public void handleEditBook(ServerboundEditBookPacket packet) {
+ // Paper start
+ if (!this.cserver.isPrimaryThread()) {
+ List<String> pageList = packet.getPages();
+ long byteTotal = 0;
+ int maxBookPageSize = com.destroystokyo.paper.PaperConfig.maxBookPageSize;
+ double multiplier = Math.max(0.3D, Math.min(1D, com.destroystokyo.paper.PaperConfig.maxBookTotalSizeMultiplier));
+ long byteAllowed = maxBookPageSize;
+ for (String testString : pageList) {
+ int byteLength = testString.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;
+ if (byteLength > 256 * 4) {
+ ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " tried to send a book with with a page too large!");
+ server.scheduleOnMain(() -> this.disconnect("Book too large!"));
+ return;
+ }
+ byteTotal += byteLength;
+ int length = testString.length();
+ int multibytes = 0;
+ if (byteLength != length) {
+ for (char c : testString.toCharArray()) {
+ if (c > 127) {
+ multibytes++;
+ }
+ }
+ }
+ byteAllowed += (maxBookPageSize * Math.min(1, Math.max(0.1D, (double) length / 255D))) * multiplier;
+
+ if (multibytes > 1) {
+ // penalize MB
+ byteAllowed -= multibytes;
+ }
+ }
+
+ if (byteTotal > byteAllowed) {
+ ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " tried to send too large of a book. Book Size: " + byteTotal + " - Allowed: "+ byteAllowed + " - Pages: " + pageList.size());
+ server.scheduleOnMain(() -> this.disconnect("Book too large!"));
+ return;
+ }
+ }
+ // Paper end
// CraftBukkit start
if (this.lastBookTick + 20 > MinecraftServer.currentTick) {
this.disconnect("Book edited too quickly!");

View file

@ -0,0 +1,66 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sun, 7 Oct 2018 12:05:28 -0700
Subject: [PATCH] Add PlayerConnectionCloseEvent
This event is invoked when a player has disconnected. It is guaranteed that,
if the server is in online-mode, that the provided uuid and username have been
validated.
The event is invoked for players who have not yet logged into the world, whereas
PlayerQuitEvent is only invoked on players who have logged into the world.
The event is invoked for players who have already logged into the world,
although whether or not the player exists in the world at the time of
firing is undefined. (That is, whether the plugin can retrieve a Player object
using the event parameters is undefined). However, it is guaranteed that this
event is invoked AFTER PlayerQuitEvent, if the player has already logged into
the world.
This event is guaranteed to never fire unless AsyncPlayerPreLoginEvent has
been called beforehand, and this event may not be called in parallel with
AsyncPlayerPreLoginEvent for the same connection.
Cancelling the AsyncPlayerPreLoginEvent guarantees the corresponding
PlayerConnectionCloseEvent is never called.
The event may be invoked asynchronously or synchronously. As it stands,
it is never invoked asynchronously. However, plugins should check
Event#isAsynchronous to be future-proof.
On purpose, the deprecated PlayerPreLoginEvent event is left out of the
API spec for this event. Plugins should not be using that event, and
how PlayerPreLoginEvent interacts with PlayerConnectionCloseEvent
is undefined.
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index 0b3f1585618e29685ffcba704f03ff5e581f0851..821f22b8fde2d76bfcb417138f9bd83af766dcd7 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -418,6 +418,26 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
this.getPacketListener().onDisconnect(new TranslatableComponent("multiplayer.disconnect.generic"));
}
this.queue.clear(); // Free up packet queue.
+ // Paper start - Add PlayerConnectionCloseEvent
+ final PacketListener packetListener = this.getPacketListener();
+ if (packetListener instanceof ServerGamePacketListenerImpl) {
+ /* Player was logged in */
+ final ServerGamePacketListenerImpl playerConnection = (ServerGamePacketListenerImpl) packetListener;
+ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(playerConnection.player.getUUID(),
+ playerConnection.player.getScoreboardName(), ((java.net.InetSocketAddress)address).getAddress(), false).callEvent();
+ } else if (packetListener instanceof ServerLoginPacketListenerImpl) {
+ /* Player is login stage */
+ final ServerLoginPacketListenerImpl loginListener = (ServerLoginPacketListenerImpl) packetListener;
+ switch (loginListener.state) {
+ case READY_TO_ACCEPT:
+ case DELAY_ACCEPT:
+ case ACCEPTED:
+ final com.mojang.authlib.GameProfile profile = loginListener.gameProfile; /* Should be non-null at this stage */
+ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.getId(), profile.getName(),
+ ((java.net.InetSocketAddress)address).getAddress(), false).callEvent();
+ }
+ }
+ // Paper end
}
}

View file

@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Tue, 18 Dec 2018 02:15:08 +0000
Subject: [PATCH] Prevent Enderman from loading chunks
diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
index c7714824d8e85087397047cf9bcb0528968b1f93..cfdb631500962ec19007db55bd4453687b91c2f8 100644
--- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
@@ -490,7 +490,8 @@ public class EnderMan extends Monster implements NeutralMob {
int j = Mth.floor(this.enderman.getY() + random.nextDouble() * 2.0D);
int k = Mth.floor(this.enderman.getZ() - 1.0D + random.nextDouble() * 2.0D);
BlockPos blockposition = new BlockPos(i, j, k);
- BlockState iblockdata = world.getBlockState(blockposition);
+ BlockState iblockdata = world.getTypeIfLoaded(blockposition); // Paper
+ if (iblockdata == null) return; // Paper
BlockPos blockposition1 = blockposition.below();
BlockState iblockdata1 = world.getBlockState(blockposition1);
BlockState iblockdata2 = this.enderman.getCarriedBlock();
@@ -536,7 +537,8 @@ public class EnderMan extends Monster implements NeutralMob {
int j = Mth.floor(this.enderman.getY() + random.nextDouble() * 3.0D);
int k = Mth.floor(this.enderman.getZ() - 2.0D + random.nextDouble() * 4.0D);
BlockPos blockposition = new BlockPos(i, j, k);
- BlockState iblockdata = world.getBlockState(blockposition);
+ BlockState iblockdata = world.getTypeIfLoaded(blockposition); // Paper
+ if (iblockdata == null) return; // Paper
Vec3 vec3d = new Vec3((double) this.enderman.getBlockX() + 0.5D, (double) j + 0.5D, (double) this.enderman.getBlockZ() + 0.5D);
Vec3 vec3d1 = new Vec3((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D);
BlockHitResult movingobjectpositionblock = world.clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.enderman));

View file

@ -0,0 +1,164 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach@zachbr.io>
Date: Wed, 2 Jan 2019 00:35:43 -0600
Subject: [PATCH] Add APIs to replace OfflinePlayer#getLastPlayed
Currently OfflinePlayer#getLastPlayed could more accurately be described
as "OfflinePlayer#getLastTimeTheirDataWasSaved".
The API doc says it should return the last time the server "witnessed"
the player, whilst also saying it should return the last time they
logged in. The current implementation does neither.
Given this interesting contradiction in the API documentation and the
current defacto implementation, I've elected to deprecate (with no
intent to remove) and replace it with two new methods, clearly named and
documented as to their purpose.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index f51c416e7938b7905f7efb154ab14cad643962e9..71cd61112fa36d1de2f5fc0dd75993fafc3d76ab 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -222,6 +222,7 @@ public class ServerPlayer extends Player {
public int latency;
public boolean wonGame;
private int containerUpdateDelay; // Paper
+ public long loginTime; // Paper
// Paper start - cancellable death event
public boolean queueHealthUpdatePacket = false;
public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index fb16158ed403abaac740729cb351cd94a36653ac..e9447ac47a858fbc9e16559c18e377517b8089ac 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -173,6 +173,7 @@ public abstract class PlayerList {
}
public void placeNewPlayer(Connection connection, ServerPlayer player) {
+ player.loginTime = System.currentTimeMillis(); // Paper
GameProfile gameprofile = player.getGameProfile();
GameProfileCache usercache = this.server.getProfileCache();
Optional<GameProfile> optional = usercache.get(gameprofile.getId());
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
index c7e4c6d29378675b76ebb179022ddbb02831a1dc..88bc0807e8bf66a65422f85f1112336334eb3de2 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
@@ -244,6 +244,61 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
return this.getData() != null;
}
+ // Paper start
+ @Override
+ public long getLastLogin() {
+ Player player = getPlayer();
+ if (player != null) return player.getLastLogin();
+
+ CompoundTag data = getPaperData();
+
+ if (data != null) {
+ if (data.contains("LastLogin")) {
+ return data.getLong("LastLogin");
+ } else {
+ // if the player file cannot provide accurate data, this is probably the closest we can approximate
+ File file = getDataFile();
+ return file.lastModified();
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public long getLastSeen() {
+ Player player = getPlayer();
+ if (player != null) return player.getLastSeen();
+
+ CompoundTag data = getPaperData();
+
+ if (data != null) {
+ if (data.contains("LastSeen")) {
+ return data.getLong("LastSeen");
+ } else {
+ // if the player file cannot provide accurate data, this is probably the closest we can approximate
+ File file = getDataFile();
+ return file.lastModified();
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ private CompoundTag getPaperData() {
+ CompoundTag result = getData();
+
+ if (result != null) {
+ if (!result.contains("Paper")) {
+ result.put("Paper", new CompoundTag());
+ }
+ result = result.getCompound("Paper");
+ }
+
+ return result;
+ }
+ // Paper end
+
@Override
public Location getBedSpawnLocation() {
CompoundTag data = this.getData();
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index c5a29d7ca512eddfd01fe1f89f9c1b82bbfd6535..b1e6edae0de15e2b03c5e9a1344886e790f079e3 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -154,6 +154,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
private org.bukkit.event.player.PlayerResourcePackStatusEvent.Status resourcePackStatus;
private String resourcePackHash;
private static final boolean DISABLE_CHANNEL_LIMIT = System.getProperty("paper.disableChannelLimit") != null; // Paper - add a flag to disable the channel limit
+ private long lastSaveTime;
// Paper end
public CraftPlayer(CraftServer server, ServerPlayer entity) {
@@ -1524,6 +1525,18 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
this.firstPlayed = firstPlayed;
}
+ // Paper start
+ @Override
+ public long getLastLogin() {
+ return getHandle().loginTime;
+ }
+
+ @Override
+ public long getLastSeen() {
+ return isOnline() ? System.currentTimeMillis() : this.lastSaveTime;
+ }
+ // Paper end
+
public void readExtraData(CompoundTag nbttagcompound) {
this.hasPlayedBefore = true;
if (nbttagcompound.contains("bukkit")) {
@@ -1546,6 +1559,8 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
public void setExtraData(CompoundTag nbttagcompound) {
+ this.lastSaveTime = System.currentTimeMillis(); // Paper
+
if (!nbttagcompound.contains("bukkit")) {
nbttagcompound.put("bukkit", new CompoundTag());
}
@@ -1560,6 +1575,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
data.putLong("firstPlayed", this.getFirstPlayed());
data.putLong("lastPlayed", System.currentTimeMillis());
data.putString("lastKnownName", handle.getScoreboardName());
+
+ // Paper start - persist for use in offline save data
+ if (!nbttagcompound.contains("Paper")) {
+ nbttagcompound.put("Paper", new CompoundTag());
+ }
+
+ CompoundTag paper = nbttagcompound.getCompound("Paper");
+ paper.putLong("LastLogin", handle.loginTime);
+ paper.putLong("LastSeen", System.currentTimeMillis());
+ // Paper end
}
@Override

View file

@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: connorhartley <vectrixu+gh@gmail.com>
Date: Mon, 7 Jan 2019 14:43:48 -0600
Subject: [PATCH] Workaround for vehicle tracking issue on disconnect
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 71cd61112fa36d1de2f5fc0dd75993fafc3d76ab..e8ef3adad0bfddf17271e095ec928c8acb9413f2 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -1565,6 +1565,13 @@ public class ServerPlayer extends Player {
public void disconnect() {
this.disconnected = true;
this.ejectPassengers();
+
+ // Paper start - Workaround an issue where the vehicle doesn't track the passenger disconnection dismount.
+ if (this.isPassenger() && this.getVehicle() instanceof ServerPlayer) {
+ this.stopRiding();
+ }
+ // Paper end
+
if (this.isSleeping()) {
this.stopSleepInBed(true, false);
}

View file

@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach@zachbr.io>
Date: Thu, 31 Jan 2019 16:33:36 -0500
Subject: [PATCH] Fire BlockPistonRetractEvent for all empty pistons
There is an explicit check in the handling code for empty pistons that
prevents sticky pistons from firing the event. However when we look back
at the history we see that this check was originally added so that ONLY
sticky pistons would fire the retract event. I'm not sure why.
https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/1092acbddf07edfa4100bc6824504ac75088e913
Over the course of several updates, the meaning of that field appears to
have changed from "is NOT sticky" to "is sticky". So now its having the
opposite effect. Only normal pistons fire the retraction event. And like
all things in CB, it's just been carried around since.
If we are to believe the history, the correct fix for this issue is to
flip it so it only fires for sticky pistons, but that puts us in a
bind. It's already firing for non-sticky pistons, changing it now would
likely result in breakage. Furthermore, there is little documentation as
to WHY that was ever intended to be the case.
Instead we opt to remove the check entirely so that the event fires for
all piston types.
diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
index aa4c582172221f7d48c9a64e91bdfb95a2453a6c..2f1345d3c3671953a806cb243a152e080fbb9108 100644
--- a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
@@ -146,7 +146,7 @@ public class PistonBaseBlock extends DirectionalBlock {
}
// CraftBukkit start
- if (!this.isSticky) {
+ //if (!this.sticky) { // Paper - Prevents empty sticky pistons from firing retract - history behind is odd
org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
BlockPistonRetractEvent event = new BlockPistonRetractEvent(block, ImmutableList.<org.bukkit.block.Block>of(), CraftBlock.notchToBlockFace(enumdirection));
world.getCraftServer().getPluginManager().callEvent(event);
@@ -154,7 +154,7 @@ public class PistonBaseBlock extends DirectionalBlock {
if (event.isCancelled()) {
return;
}
- }
+ //} // Paper
// PAIL: checkME - what happened to setTypeAndData?
// CraftBukkit end
world.blockEvent(pos, this, b0, enumdirection.get3DDataValue());

View file

@ -0,0 +1,33 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach@zachbr.io>
Date: Mon, 4 Feb 2019 23:33:24 -0500
Subject: [PATCH] Block Entity#remove from being called on Players
This doesn't result in the same behavior as other entities and causes
several problems. Anyone ever complain about the "Cannot send chat
message" thing? That's one of the issues this causes, among others.
If a plugin developer can come up with a valid reason to call this on a
Player we will look at limiting the scope of this change. It appears to
be unintentional in the few cases we've seen so far.
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index b1e6edae0de15e2b03c5e9a1344886e790f079e3..131a8fcf8b0e9ddb5e9f456c494ddea27d4cb88e 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -2352,6 +2352,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public void resetCooldown() {
getHandle().resetAttackStrengthTicker();
}
+
+ @Override
+ public void remove() {
+ if (this.getHandle().getClass().equals(ServerPlayer.class)) { // special case for NMS plugins inheriting
+ throw new UnsupportedOperationException("Calling Entity#remove on players produces undefined (bad) behavior");
+ } else {
+ super.remove();
+ }
+ }
// Paper end
// Spigot start
private final Player.Spigot spigot = new Player.Spigot()