Upstream Paper

This commit is contained in:
Sotr
2019-03-25 18:16:28 +08:00
15 changed files with 787 additions and 78 deletions

View File

@@ -23,6 +23,9 @@
*/
package co.aikar.timings;
import javax.annotation.Nonnull; // Akarin - javax.annotation
import javax.annotation.Nullable; // Akarin - javax.annotation
/**
* Provides an ability to time sections of code within the Minecraft Server
*/
@@ -32,6 +35,7 @@ public interface Timing extends AutoCloseable {
*
* @return Timing
*/
@Nonnull // Akarin - javax.annotation
Timing startTiming();
default Timing startTiming(boolean assertThread) { return startTiming(); }; // Akarin
default Timing startTimingUnsafe() { return startTiming(); }; // Akarin
@@ -51,6 +55,7 @@ public interface Timing extends AutoCloseable {
*
* @return Timing
*/
@Nonnull // Akarin - javax.annotation
Timing startTimingIfSync();
default Timing startTimingIfSync(boolean assertThread) { return startTimingIfSync(); }; // Akarin
@@ -64,8 +69,9 @@ public interface Timing extends AutoCloseable {
void stopTimingIfSync();
/**
* Stops timing and disregards current timing data.
* @deprecated Doesn't do anything - Removed
*/
@Deprecated
void abort();
/**
@@ -73,6 +79,7 @@ public interface Timing extends AutoCloseable {
*
* @return TimingHandler
*/
@Nullable
TimingHandler getTimingHandler();
@Override

View File

@@ -26,17 +26,25 @@ package co.aikar.timings;
import co.aikar.util.LoadingIntMap;
import io.akarin.server.core.AkarinGlobalConfig;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.bukkit.Bukkit;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull; // Akarin - javax.annotation
import javax.annotation.Nullable; // Akarin - javax.annotation
import org.bukkit.Bukkit;
class TimingHandler implements Timing {
private static AtomicInteger idPool = new AtomicInteger(1);
static Deque<TimingHandler> TIMING_STACK = new ArrayDeque<>();
final int id = idPool.getAndIncrement();
final String name;
final TimingIdentifier identifier;
private final boolean verbose;
private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
@@ -49,18 +57,11 @@ class TimingHandler implements Timing {
private boolean added;
private boolean timed;
private boolean enabled;
private TimingHandler parent;
private boolean unsafe; // Akarin
TimingHandler(TimingIdentifier id) {
if (id.name.startsWith("##")) {
verbose = true;
this.name = id.name.substring(3);
} else {
this.name = id.name;
verbose = false;
}
TimingHandler(@Nonnull TimingIdentifier id) { // Akarin - javax.annotation
this.identifier = id;
this.verbose = id.name.startsWith("##");
this.record = new TimingData(this.id);
this.groupHandler = id.groupHandler;
@@ -85,15 +86,17 @@ class TimingHandler implements Timing {
}
}
@Nonnull // Akarin - javax.annotation
@Override
// Akarin start
public Timing startTimingIfSync() {
// Akarin start
return startTiming(false);
}
@Nonnull // Akarin - javax.annotation
@Override
// Akarin end
public Timing startTimingIfSync(boolean assertThread) {
startTiming(assertThread);
// Akarin end
return this;
}
@@ -101,20 +104,31 @@ class TimingHandler implements Timing {
public void stopTimingIfSync() {
stopTiming();
}
// Akarin start
@Override
@Nonnull // Akarin - javax.annotation
public Timing startTiming() {
// Akarin start
return startTiming(false);
}
@Override
public Timing startTimingUnsafe() {
if (enabled && ++timingDepth == 1) {
start = System.nanoTime();
parent = TimingsManager.CURRENT;
TimingsManager.CURRENT = this;
unsafe = true;
// Akarin end
start = System.nanoTime();
TIMING_STACK.addLast(this);
}
return this;
}
// Akarin start
@Override
public Timing startTiming(boolean assertThread) {
if (enabled && (ThreadAssertion.isMainThread() || Bukkit.isPrimaryThread()) && ++timingDepth == 1) {
start = System.nanoTime();
TIMING_STACK.addLast(this);
if (assertThread && AkarinGlobalConfig.lazyThreadAssertion)
ThreadAssertion.setMainThread(true);
}
return this;
}
@@ -122,52 +136,47 @@ class TimingHandler implements Timing {
@Override
public void stopTimingUnsafe() {
if (enabled && timingDepth > 0 && --timingDepth == 0 && start != 0) {
addDiff(System.nanoTime() - start);
start = 0;
unsafe = false;
}
}
@Override
public Timing startTiming(boolean assertThread) {
if (enabled && (ThreadAssertion.isMainThread() || Bukkit.isPrimaryThread()) /*&& ++timingDepth == 1*/) {
if (AkarinGlobalConfig.lazyThreadAssertion && assertThread) ThreadAssertion.setMainThread(true);
if (++timingDepth != 1) return this;
// Akarin end
start = System.nanoTime();
parent = TimingsManager.CURRENT;
TimingsManager.CURRENT = this;
}
return this;
}
@Override
public void stopTiming() {
// Akarin start
if (enabled && timingDepth > 0 && (ThreadAssertion.isMainThread() || Bukkit.isPrimaryThread()) /*&& --timingDepth == 0 && start != 0*/) {
if (AkarinGlobalConfig.lazyThreadAssertion) ThreadAssertion.setMainThread(false);
if (--timingDepth != 0 || start == 0) return;
unsafe = false;
// Akarin end
addDiff(System.nanoTime() - start);
start = 0;
}
}
@Override
public void abort() {
if (enabled && timingDepth > 0) {
start = 0;
}
}
void addDiff(long diff) {
if (TimingsManager.CURRENT == this) {
TimingsManager.CURRENT = parent;
if (parent != null) {
parent.children.get(id).add(diff);
TimingHandler last = TIMING_STACK.removeLast();
if (last != this) {
Logger.getGlobal().log(Level.SEVERE, "TIMING_STACK_CORRUPTION - Report this to Paper! ( " + this.identifier + ":" + last +")", new Throwable());
TIMING_STACK.addLast(last); // Add it back
}
addDiff(System.nanoTime() - start, TIMING_STACK.peekLast());
start = 0;
unsafe = false;
}
}
// Akarin end
public void stopTiming() {
if (enabled && timingDepth > 0 && (ThreadAssertion.isMainThread() || Bukkit.isPrimaryThread()) && --timingDepth == 0 && start != 0) { // Akarin
TimingHandler last = TIMING_STACK.removeLast();
if (last != this) {
Logger.getGlobal().log(Level.SEVERE, "TIMING_STACK_CORRUPTION - Report this to Paper! ( " + this.identifier + ":" + last +")", new Throwable());
TIMING_STACK.addLast(last); // Add it back
}
addDiff(System.nanoTime() - start, TIMING_STACK.peekLast());
start = 0;
// Akarin start
if (AkarinGlobalConfig.lazyThreadAssertion)
ThreadAssertion.setMainThread(false);
unsafe = false;
// Akarin end
}
}
@Override
public final void abort() {
}
void addDiff(long diff, @Nullable TimingHandler parent) {
if (parent != null) {
parent.children.get(id).add(diff);
}
record.add(diff);
if (!added) {
added = true;
@@ -175,15 +184,13 @@ class TimingHandler implements Timing {
TimingsManager.HANDLERS.add(this);
}
if (groupHandler != null) {
groupHandler.addDiff(diff);
groupHandler.addDiff(diff, parent);
groupHandler.children.get(id).add(diff);
}
}
/**
* Reset this timer, setting all values to zero.
*
* @param full
*/
void reset(boolean full) {
record.reset();
@@ -197,6 +204,7 @@ class TimingHandler implements Timing {
checkEnabled();
}
@Nonnull // Akarin - javax.annotation
@Override
public TimingHandler getTimingHandler() {
return this;
@@ -213,8 +221,7 @@ class TimingHandler implements Timing {
}
/**
* This is simply for the Closeable interface so it can be used with
* try-with-resources ()
* This is simply for the Closeable interface so it can be used with try-with-resources ()
*/
@Override
public void close() {
@@ -233,6 +240,7 @@ class TimingHandler implements Timing {
return enabled;
}
@Nonnull // Akarin - javax.annotation
TimingData[] cloneChildren() {
final TimingData[] clonedChildren = new TimingData[children.size()];
int i = 0;

View File

@@ -594,4 +594,14 @@ public class PaperWorldConfig {
log("Using vanilla redstone algorithm.");
}
}
public boolean countAllMobsForSpawning = false;
private void countAllMobsForSpawning() {
countAllMobsForSpawning = getBoolean("count-all-mobs-for-spawning", false);
if (countAllMobsForSpawning) {
log("Counting all mobs for spawning. Mob farms may reduce natural spawns elsewhere in world.");
} else {
log("Using improved mob spawn limits (Only Natural Spawns impact spawn limits for more natural spawns)");
}
}
}

View File

@@ -92,7 +92,11 @@ public class PaperWorldEntityList extends ArrayList<Entity> {
public void updateEntityCount(Entity entity, int amt) {
// Only count natural spawns so that mob
if (!(entity instanceof IAnimal) || entity.spawnReason != SpawnReason.NATURAL) return;
if (!(entity instanceof IAnimal) || (
!world.paperConfig.countAllMobsForSpawning &&
entity.spawnReason != SpawnReason.NATURAL &&
entity.spawnReason != SpawnReason.CHUNK_GEN
)) return;
if (entity instanceof EntityInsentient) {
EntityInsentient entityinsentient = (EntityInsentient) entity;

View File

@@ -1131,7 +1131,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
while (iterator.hasNext()) {
Entity entity1 = (Entity) iterator.next();
a(entity1, generatoraccess);
a(entity1, generatoraccess, reason); // Paper
}
}

View File

@@ -58,6 +58,7 @@ public class EntityItem extends Entity {
// CraftBukkit start - Use wall time for pickup and despawn timers
int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks;
this.pickupDelay = Math.max(0, this.pickupDelay); // Paper - don't go below 0
if (this.age != -32768) this.age += elapsedTicks;
this.lastTick = MinecraftServer.currentTick;
// CraftBukkit end
@@ -147,6 +148,7 @@ public class EntityItem extends Entity {
// CraftBukkit start - Use wall time for pickup and despawn timers
int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks;
this.pickupDelay = Math.max(0, this.pickupDelay); // Paper - don't go below 0
if (this.age != -32768) this.age += elapsedTicks;
this.lastTick = MinecraftServer.currentTick;
// CraftBukkit end
@@ -363,7 +365,6 @@ public class EntityItem extends Entity {
// CraftBukkit end
if (this.pickupDelay == 0 && (this.g == null || 6000 - this.age <= 200 || this.g.equals(entityhuman.getUniqueID())) && entityhuman.inventory.pickup(itemstack)) {
entityhuman.receive(this, i);
// Paper Start
if (flyAtPlayer) {
entityhuman.receive(this, i);

View File

@@ -0,0 +1,25 @@
package net.minecraft.server;
import javax.annotation.Nullable;
public class ItemSign extends ItemBlockWallable {
public static boolean openSign; // CraftBukkit
public ItemSign(Item.Info item_info) {
super(Blocks.SIGN, Blocks.WALL_SIGN, item_info);
}
protected boolean a(BlockPosition blockposition, World world, @Nullable EntityHuman entityhuman, ItemStack itemstack, IBlockData iblockdata) {
boolean flag = super.a(blockposition, world, entityhuman, itemstack, iblockdata);
if (!world.isClientSide && !flag && entityhuman != null) {
// CraftBukkit start - SPIGOT-4678
// entityhuman.openSign((TileEntitySign) world.getTileEntity(blockposition));
ItemSign.openSign = true;
// CraftBukkit end
}
return flag;
}
}

View File

@@ -301,6 +301,12 @@ public final class ItemStack {
}
}
// SPIGOT-4678
if (this.item instanceof ItemSign && ItemSign.openSign) {
ItemSign.openSign = false;
entityhuman.openSign((TileEntitySign) world.getTileEntity(new BlockActionContext(itemactioncontext).getClickPosition()));
}
// SPIGOT-1288 - play sound stripped from ItemBlock
if (this.item instanceof ItemBlock) {
SoundEffectType soundeffecttype = ((ItemBlock) this.item).getBlock().getStepSound();

View File

@@ -1117,7 +1117,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati
//this.methodProfiler.exit(); // Akarin
//this.methodProfiler.enter(* // Akarin - remove caller
worldserver.getTracker().updatePlayers();
if (playerList.players.size() > 0) worldserver.getTracker().updatePlayers(); // Paper - No players, why spend time tracking them? (See patch)
//this.methodProfiler.exit(); // Akarin
//this.methodProfiler.exit(); // Akarin
worldserver.explosionDensityCache.clear(); // Paper - Optimize explosions

View File

@@ -121,7 +121,7 @@ public abstract class MobSpawnerAbstract {
}
}
// Paper end
Entity entity = ChunkRegionLoader.a(nbttagcompound, world, d3, d4, d5, false);
Entity entity = ChunkRegionLoader.spawnEntity(nbttagcompound, world, d3, d4, d5, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER); // Paper
if (entity == null) {
this.i();

View File

@@ -2052,7 +2052,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
if (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != origItem) {
// Refresh the current entity metadata
this.sendPacket(new PacketPlayOutEntityMetadata(entity.getId(), entity.datawatcher, true));
entity.tracker.broadcast(new PacketPlayOutEntityMetadata(entity.getId(), entity.datawatcher, true)); // Paper - update entity for all players
}
if (event.isCancelled()) {

View File

@@ -198,7 +198,7 @@ public abstract class PlayerList {
if (nbttagcompound != null && nbttagcompound.hasKeyOfType("RootVehicle", 10)) {
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("RootVehicle");
Entity entity = ChunkRegionLoader.a(nbttagcompound1.getCompound("Entity"), worldserver, true);
Entity entity = ChunkRegionLoader.spawnEntity(nbttagcompound1.getCompound("Entity"), worldserver, true, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.MOUNT); // Paper
if (entity != null) {
UUID uuid = nbttagcompound1.a("Attach");
@@ -665,9 +665,14 @@ public abstract class PlayerList {
// this.a(entityplayer1, entityplayer, worldserver); // CraftBukkit - removed
BlockPosition blockposition1;
// Paper start
boolean isBedSpawn = false;
boolean isRespawn = false;
// Paper end
// CraftBukkit start - fire PlayerRespawnEvent
if (location == null) {
boolean isBedSpawn = false;
//boolean isBedSpawn = false; Paper - moved up
CraftWorld cworld = (CraftWorld) this.server.server.getWorld(entityplayer.spawnWorld);
if (cworld != null && blockposition != null) {
blockposition1 = EntityHuman.getBed(cworld.getHandle(), blockposition, flag1);
@@ -697,6 +702,7 @@ public abstract class PlayerList {
location = respawnEvent.getRespawnLocation();
entityplayer.reset();
isRespawn = true; // Paper
} else {
location.setWorld(server.getWorldServer(dimensionmanager).getWorld());
}
@@ -758,6 +764,13 @@ public abstract class PlayerList {
if (entityplayer.playerConnection.isDisconnected()) {
this.savePlayerFile(entityplayer);
}
// Paper start
if (isRespawn) {
cserver.getPluginManager().callEvent(new com.destroystokyo.paper.event.player.PlayerPostRespawnEvent(entityplayer.getBukkitEntity(), location, isBedSpawn));
}
// Paper end
// CraftBukkit end
return entityplayer1;
}

View File

@@ -0,0 +1,152 @@
package net.minecraft.server;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public abstract class RemoteConnectionThread implements Runnable {
private static final Logger h = LogManager.getLogger();
private static final AtomicInteger i = new AtomicInteger(0);
protected boolean a;
protected IMinecraftServer b; protected IMinecraftServer getServer() { return b; } // Paper - OBFHELPER
protected final String c;
protected Thread d;
protected int e = 5;
protected List<DatagramSocket> f = Lists.newArrayList();
protected List<ServerSocket> g = Lists.newArrayList();
protected RemoteConnectionThread(IMinecraftServer iminecraftserver, String s) {
this.b = iminecraftserver;
this.c = s;
if (this.b.isDebugging()) {
this.c("Debugging is enabled, performance maybe reduced!");
}
}
public synchronized void a() {
this.d = new Thread(this, this.c + " #" + RemoteConnectionThread.i.incrementAndGet());
this.d.setUncaughtExceptionHandler(new ThreadNamedUncaughtExceptionHandler(RemoteConnectionThread.h));
this.d.start();
this.a = true;
}
public boolean c() {
return this.a;
}
protected void a(String s) {
this.b.g(s);
}
protected void b(String s) {
this.b.info(s);
}
protected void c(String s) {
this.b.warning(s);
}
protected void d(String s) {
this.b.f(s);
}
protected int getPlayerCount() { return d(); } // Paper - OBFHELPER
protected int d() {
return this.b.getPlayerCount();
}
protected void a(DatagramSocket datagramsocket) {
this.a("registerSocket: " + datagramsocket);
this.f.add(datagramsocket);
}
protected boolean a(DatagramSocket datagramsocket, boolean flag) {
this.a("closeSocket: " + datagramsocket);
if (null == datagramsocket) {
return false;
} else {
boolean flag1 = false;
if (!datagramsocket.isClosed()) {
datagramsocket.close();
flag1 = true;
}
if (flag) {
this.f.remove(datagramsocket);
}
return flag1;
}
}
protected boolean b(ServerSocket serversocket) {
return this.a(serversocket, true);
}
protected boolean a(ServerSocket serversocket, boolean flag) {
this.a("closeSocket: " + serversocket);
if (null == serversocket) {
return false;
} else {
boolean flag1 = false;
try {
if (!serversocket.isClosed()) {
serversocket.close();
flag1 = true;
}
} catch (IOException ioexception) {
this.c("IO: " + ioexception.getMessage());
}
if (flag) {
this.g.remove(serversocket);
}
return flag1;
}
}
protected void e() {
this.a(false);
}
protected void a(boolean flag) {
int i = 0;
Iterator iterator = this.f.iterator();
while (iterator.hasNext()) {
DatagramSocket datagramsocket = (DatagramSocket) iterator.next();
if (this.a(datagramsocket, false)) {
++i;
}
}
this.f.clear();
iterator = this.g.iterator();
while (iterator.hasNext()) {
ServerSocket serversocket = (ServerSocket) iterator.next();
if (this.a(serversocket, false)) {
++i;
}
}
this.g.clear();
if (flag && 0 < i) {
this.c("Force closed " + i + " sockets");
}
}
}

View File

@@ -0,0 +1,430 @@
package net.minecraft.server;
import com.google.common.collect.Maps;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.PortUnreachableException;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Map.Entry;
public class RemoteStatusListener extends RemoteConnectionThread {
private long h;
private int i;
private final int j; private int getServerPort() { return j; } // Paper - OBFHELPER
private final int k; private int getMaxPlayers() { return k; } // Paper - OBFHELPER
private final String l; private String getMotd() { return l; } // Paper - OBFHELPER
private final String m; private String getWorldName() { return m; } // Paper - OBFHELPER
private DatagramSocket n;
private final byte[] o = new byte[1460];
private DatagramPacket p;
private final Map<SocketAddress, String> q;
private String r; private String getServerHost() { return r; } // Paper - OBFHELPER
private String s;
private final Map<SocketAddress, RemoteStatusListener.RemoteStatusChallenge> t;
private final long u;
private final RemoteStatusReply v; private RemoteStatusReply getCachedFullResponse() { return v; } // Paper - OBFHELPER
private long w;
public RemoteStatusListener(IMinecraftServer iminecraftserver) {
super(iminecraftserver, "Query Listener");
this.i = iminecraftserver.a("query.port", 0);
this.s = iminecraftserver.e();
this.j = iminecraftserver.e_();
this.l = iminecraftserver.m();
this.k = iminecraftserver.getMaxPlayers();
this.m = iminecraftserver.getWorld();
this.w = 0L;
this.r = "0.0.0.0";
if (!this.s.isEmpty() && !this.r.equals(this.s)) {
this.r = this.s;
} else {
this.s = "0.0.0.0";
try {
InetAddress inetaddress = InetAddress.getLocalHost();
this.r = inetaddress.getHostAddress();
} catch (UnknownHostException unknownhostexception) {
this.c("Unable to determine local host IP, please set server-ip in '" + iminecraftserver.d_() + "' : " + unknownhostexception.getMessage());
}
}
if (0 == this.i) {
this.i = this.j;
this.b("Setting default query port to " + this.i);
iminecraftserver.a("query.port", (Object) this.i);
iminecraftserver.a("debug", (Object) false);
iminecraftserver.c_();
}
this.q = Maps.newHashMap();
this.v = new RemoteStatusReply(1460);
this.t = Maps.newHashMap();
this.u = (new Date()).getTime();
}
private void a(byte[] abyte, DatagramPacket datagrampacket) throws IOException {
this.n.send(new DatagramPacket(abyte, abyte.length, datagrampacket.getSocketAddress()));
}
private boolean a(DatagramPacket datagrampacket) throws IOException {
byte[] abyte = datagrampacket.getData();
int i = datagrampacket.getLength();
SocketAddress socketaddress = datagrampacket.getSocketAddress();
this.a("Packet len " + i + " [" + socketaddress + "]");
if (3 <= i && -2 == abyte[0] && -3 == abyte[1]) {
this.a("Packet '" + StatusChallengeUtils.a(abyte[2]) + "' [" + socketaddress + "]");
switch (abyte[2]) {
case 0:
if (!this.c(datagrampacket)) {
this.a("Invalid challenge [" + socketaddress + "]");
return false;
} else if (15 == i) {
this.a(this.b(datagrampacket), datagrampacket);
this.a("Rules [" + socketaddress + "]");
} else {
RemoteStatusReply remotestatusreply = new RemoteStatusReply(1460);
remotestatusreply.a((int) 0);
remotestatusreply.a(this.a(datagrampacket.getSocketAddress()));
/* Paper start - GS4 Query event
remotestatusreply.a(this.l);
remotestatusreply.a("SMP");
remotestatusreply.a(this.m);
remotestatusreply.a(Integer.toString(this.d()));
remotestatusreply.a(Integer.toString(this.k));
remotestatusreply.a((short) this.j);
remotestatusreply.a(this.r);
*/
com.destroystokyo.paper.event.server.GS4QueryEvent.QueryType queryType =
com.destroystokyo.paper.event.server.GS4QueryEvent.QueryType.BASIC;
com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse queryResponse = com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse.builder()
.motd(this.getMotd())
.map(this.getWorldName())
.currentPlayers(this.getPlayerCount())
.maxPlayers(this.getMaxPlayers())
.port(this.getServerPort())
.hostname(this.getServerHost())
.gameVersion(this.getServer().getVersion())
.serverVersion(org.bukkit.Bukkit.getServer().getName() + " on " + org.bukkit.Bukkit.getServer().getBukkitVersion())
.build();
com.destroystokyo.paper.event.server.GS4QueryEvent queryEvent =
new com.destroystokyo.paper.event.server.GS4QueryEvent(queryType, datagrampacket.getAddress(), queryResponse);
queryEvent.callEvent();
queryResponse = queryEvent.getResponse();
remotestatusreply.writeString(queryResponse.getMotd());
remotestatusreply.writeString("SMP");
remotestatusreply.writeString(queryResponse.getMap());
remotestatusreply.writeString(Integer.toString(queryResponse.getCurrentPlayers()));
remotestatusreply.writeString(Integer.toString(queryResponse.getMaxPlayers()));
remotestatusreply.writeShort((short) queryResponse.getPort());
remotestatusreply.writeString(queryResponse.getHostname());
// Paper end
this.a(remotestatusreply.a(), datagrampacket);
this.a("Status [" + socketaddress + "]");
}
default:
return true;
case 9:
this.d(datagrampacket);
this.a("Challenge [" + socketaddress + "]");
return true;
}
} else {
this.a("Invalid packet [" + socketaddress + "]");
return false;
}
}
private byte[] b(DatagramPacket datagrampacket) throws IOException {
long i = SystemUtils.getMonotonicMillis();
if (i < this.w + 5000L) {
byte[] abyte = this.v.a();
byte[] abyte1 = this.a(datagrampacket.getSocketAddress());
abyte[1] = abyte1[0];
abyte[2] = abyte1[1];
abyte[3] = abyte1[2];
abyte[4] = abyte1[3];
return abyte;
} else {
this.w = i;
this.v.b();
this.v.a((int) 0);
this.v.a(this.a(datagrampacket.getSocketAddress()));
this.v.a("splitnum");
this.v.a((int) 128);
this.v.a((int) 0);
/* Paper start - GS4 Query event
this.v.a("hostname");
this.v.a(this.l);
this.v.a("gametype");
this.v.a("SMP");
this.v.a("game_id");
this.v.a("MINECRAFT");
this.v.a("version");
this.v.a(this.b.getVersion());
this.v.a("plugins");
this.v.a(this.b.getPlugins());
this.v.a("map");
this.v.a(this.m);
this.v.a("numplayers");
this.v.a("" + this.d());
this.v.a("maxplayers");
this.v.a("" + this.k);
this.v.a("hostport");
this.v.a("" + this.j);
this.v.a("hostip");
this.v.a(this.r);
this.v.a((int) 0);
this.v.a((int) 1);
this.v.a("player_");
this.v.a((int) 0);
String[] astring = this.b.getPlayers();
String[] astring1 = astring;
int j = astring.length;
for (int k = 0; k < j; ++k) {
String s = astring1[k];
this.v.a(s);
}
this.v.a((int) 0);
*/
// Pack plugins
java.util.List<com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse.PluginInformation> plugins = java.util.Collections.emptyList();
org.bukkit.plugin.Plugin[] bukkitPlugins;
if(((DedicatedServer) this.getServer()).server.getQueryPlugins() && (bukkitPlugins = org.bukkit.Bukkit.getPluginManager().getPlugins()).length > 0) {
plugins = java.util.stream.Stream.of(bukkitPlugins)
.map(plugin -> com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse.PluginInformation.of(plugin.getName(), plugin.getDescription().getVersion()))
.collect(java.util.stream.Collectors.toList());
}
com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse queryResponse = com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse.builder()
.motd(this.getMotd())
.map(this.getWorldName())
.currentPlayers(this.getPlayerCount())
.maxPlayers(this.getMaxPlayers())
.port(this.getServerPort())
.hostname(this.getServerHost())
.plugins(plugins)
.players(this.getServer().getPlayers())
.gameVersion(this.getServer().getVersion())
.serverVersion(org.bukkit.Bukkit.getServer().getName() + " on " + org.bukkit.Bukkit.getServer().getBukkitVersion())
.build();
com.destroystokyo.paper.event.server.GS4QueryEvent.QueryType queryType =
com.destroystokyo.paper.event.server.GS4QueryEvent.QueryType.FULL;
com.destroystokyo.paper.event.server.GS4QueryEvent queryEvent =
new com.destroystokyo.paper.event.server.GS4QueryEvent(queryType, datagrampacket.getAddress(), queryResponse);
queryEvent.callEvent();
queryResponse = queryEvent.getResponse();
this.getCachedFullResponse().writeString("hostname");
this.getCachedFullResponse().writeString(queryResponse.getMotd());
this.getCachedFullResponse().writeString("gametype");
this.getCachedFullResponse().writeString("SMP");
this.getCachedFullResponse().writeString("game_id");
this.getCachedFullResponse().writeString("MINECRAFT");
this.getCachedFullResponse().writeString("version");
this.getCachedFullResponse().writeString(queryResponse.getGameVersion());
this.getCachedFullResponse().writeString("plugins");
java.lang.StringBuilder pluginsString = new java.lang.StringBuilder();
pluginsString.append(queryResponse.getServerVersion());
if(!queryResponse.getPlugins().isEmpty()) {
pluginsString.append(": ");
Iterator<com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse.PluginInformation> iter = queryResponse.getPlugins().iterator();
while(iter.hasNext()) {
com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse.PluginInformation info = iter.next();
pluginsString.append(info.getName());
if (info.getVersion() != null) {
pluginsString.append(' ').append(info.getVersion().replaceAll(";", ","));
}
if (iter.hasNext()) {
pluginsString.append(';').append(' ');
}
}
}
this.getCachedFullResponse().writeString(pluginsString.toString());
this.getCachedFullResponse().writeString("map");
this.getCachedFullResponse().writeString(queryResponse.getMap());
this.getCachedFullResponse().writeString("numplayers");
this.getCachedFullResponse().writeString(Integer.toString(queryResponse.getCurrentPlayers()));
this.getCachedFullResponse().writeString("maxplayers");
this.getCachedFullResponse().writeString(Integer.toString(queryResponse.getMaxPlayers()));
this.getCachedFullResponse().writeString("hostport");
this.getCachedFullResponse().writeString(Integer.toString(queryResponse.getPort()));
this.getCachedFullResponse().writeString("hostip");
this.getCachedFullResponse().writeString(queryResponse.getHostname());
// The "meaningless data" start, copied from above
this.getCachedFullResponse().writeInt(0);
this.getCachedFullResponse().writeInt(1);
this.getCachedFullResponse().writeString("player_");
this.getCachedFullResponse().writeInt(0);
// "Meaningless data" end
queryResponse.getPlayers().forEach(this.getCachedFullResponse()::writeStringUnchecked);
this.getCachedFullResponse().writeInt(0);
// Paper end
return this.v.a();
}
}
private byte[] a(SocketAddress socketaddress) {
return ((RemoteStatusListener.RemoteStatusChallenge) this.t.get(socketaddress)).c();
}
private Boolean c(DatagramPacket datagrampacket) {
SocketAddress socketaddress = datagrampacket.getSocketAddress();
if (!this.t.containsKey(socketaddress)) {
return false;
} else {
byte[] abyte = datagrampacket.getData();
return ((RemoteStatusListener.RemoteStatusChallenge) this.t.get(socketaddress)).a() != StatusChallengeUtils.c(abyte, 7, datagrampacket.getLength()) ? false : true;
}
}
private void d(DatagramPacket datagrampacket) throws IOException {
RemoteStatusListener.RemoteStatusChallenge remotestatuslistener_remotestatuschallenge = new RemoteStatusListener.RemoteStatusChallenge(datagrampacket);
this.t.put(datagrampacket.getSocketAddress(), remotestatuslistener_remotestatuschallenge);
this.a(remotestatuslistener_remotestatuschallenge.b(), datagrampacket);
}
private void f() {
if (this.a) {
long i = SystemUtils.getMonotonicMillis();
if (i >= this.h + 30000L) {
this.h = i;
Iterator iterator = this.t.entrySet().iterator();
while (iterator.hasNext()) {
Entry<SocketAddress, RemoteStatusListener.RemoteStatusChallenge> entry = (Entry) iterator.next();
if (((RemoteStatusListener.RemoteStatusChallenge) entry.getValue()).a(i)) {
iterator.remove();
}
}
}
}
}
public void run() {
this.b("Query running on " + this.s + ":" + this.i);
this.h = SystemUtils.getMonotonicMillis();
this.p = new DatagramPacket(this.o, this.o.length);
try {
while (this.a) {
try {
this.n.receive(this.p);
this.f();
this.a(this.p);
} catch (SocketTimeoutException sockettimeoutexception) {
this.f();
} catch (PortUnreachableException portunreachableexception) {
;
} catch (IOException ioexception) {
this.a((Exception) ioexception);
}
}
} finally {
this.e();
}
}
public void a() {
if (!this.a) {
if (0 < this.i && 65535 >= this.i) {
if (this.g()) {
super.a();
}
} else {
this.c("Invalid query port " + this.i + " found in '" + this.b.d_() + "' (queries disabled)");
}
}
}
private void a(Exception exception) {
if (this.a) {
this.c("Unexpected exception, buggy JRE? (" + exception + ")");
if (!this.g()) {
this.d("Failed to recover from buggy JRE, shutting down!");
this.a = false;
}
}
}
private boolean g() {
try {
this.n = new DatagramSocket(this.i, InetAddress.getByName(this.s));
this.a(this.n);
this.n.setSoTimeout(500);
return true;
} catch (SocketException socketexception) {
this.c("Unable to initialise query system on " + this.s + ":" + this.i + " (Socket): " + socketexception.getMessage());
} catch (UnknownHostException unknownhostexception) {
this.c("Unable to initialise query system on " + this.s + ":" + this.i + " (Unknown Host): " + unknownhostexception.getMessage());
} catch (Exception exception) {
this.c("Unable to initialise query system on " + this.s + ":" + this.i + " (E): " + exception.getMessage());
}
return false;
}
class RemoteStatusChallenge {
private final long time = (new Date()).getTime();
private final int token;
private final byte[] identity;
private final byte[] e;
private final String f;
public RemoteStatusChallenge(DatagramPacket datagrampacket) {
byte[] abyte = datagrampacket.getData();
this.identity = new byte[4];
this.identity[0] = abyte[3];
this.identity[1] = abyte[4];
this.identity[2] = abyte[5];
this.identity[3] = abyte[6];
this.f = new String(this.identity, StandardCharsets.UTF_8);
this.token = (new Random()).nextInt(16777216);
this.e = String.format("\t%s%d\u0000", this.f, this.token).getBytes(StandardCharsets.UTF_8);
}
public Boolean a(long i) {
return this.time < i;
}
public int a() {
return this.token;
}
public byte[] b() {
return this.e;
}
public byte[] c() {
return this.identity;
}
}
}

View File

@@ -0,0 +1,53 @@
package net.minecraft.server;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class RemoteStatusReply {
private final ByteArrayOutputStream a;
private final DataOutputStream b;
public RemoteStatusReply(int i) {
this.a = new ByteArrayOutputStream(i);
this.b = new DataOutputStream(this.a);
}
public void a(byte[] abyte) throws IOException {
this.b.write(abyte, 0, abyte.length);
}
public void writeString(String string) throws IOException { a(string); } // Paper - OBFHELPER
public void a(String s) throws IOException {
this.b.writeBytes(s);
this.b.write(0);
}
// Paper start - unchecked exception variant to use in Stream API
public void writeStringUnchecked(String string) {
try {
writeString(string);
} catch (IOException e) {
com.destroystokyo.paper.util.SneakyThrow.sneaky(e);
}
}
// Paper end
public void writeInt(int i) throws IOException { a(i); } // Paper - OBFHELPER
public void a(int i) throws IOException {
this.b.write(i);
}
public void writeShort(short i) throws IOException { a(i); } // Paper - OBFHELPER
public void a(short short0) throws IOException {
this.b.writeShort(Short.reverseBytes(short0));
}
public byte[] a() {
return this.a.toByteArray();
}
public void b() {
this.a.reset();
}
}