54 Commits

Author SHA1 Message Date
cakoyo
8b5de991da Merge pull request #60 from MatrixTunnel/fix/readme
Add new Jenkins by a friend
2018-10-14 17:53:22 +08:00
Sotr
2ce7ad3504 Upstream Paper 2018-10-05 04:03:07 +08:00
Sotr
458d25a6ff Upstream Paper 2018-09-02 16:04:33 +08:00
Sotr
7c02d15374 Fix a NPE and packet compat 2018-09-02 16:03:38 +08:00
Sotr
5b84e0b4ae Fixes timings 2018-08-31 15:31:24 +08:00
Sotr
521961be73 Upstream Paper 2018-08-31 15:23:13 +08:00
Sotr
6f58c7158d Fixes CCE 2018-08-29 15:57:05 +08:00
Sotr
1356bf841f Fixup worlds accessing 2018-08-29 02:00:16 +08:00
Sotr
48a23b9e63 Fixes startup 2018-08-29 01:26:33 +08:00
Sotr
c063f92ff1 Minecraft 1.13.1 2018-08-28 23:46:03 +08:00
Sotr
2c18d26438 Upstream Paper 2018-08-19 15:15:21 +08:00
Sotr
3b949e82ba No update need 2018-08-16 16:28:31 +08:00
Sotr
49c3060432 Missing nms file w/ cleanup 2018-08-14 19:49:39 +08:00
Sotr
88415e09b5 Removes debug codes (^ ^) / 2018-08-14 18:51:17 +08:00
Sotr
0a7f790e78 Fixes thread check - Close GH-50 2018-08-14 18:46:24 +08:00
Sotr
ef0ce05cae Upstream Paper 2018-08-14 18:25:15 +08:00
Sotr
2593ab8eba Fixes pool size w/ re-add mode 2 2018-08-13 08:36:03 +08:00
Sotr
732cc57ee5 Fixes tick order, dead lock and misc 2018-08-13 07:56:55 +08:00
Sotr
2f867dd364 Better handle timings w/ safety fixes 2018-08-13 07:12:23 +08:00
Sotr
75ab6a414c Fixes the lock 2018-08-13 04:59:13 +08:00
Sotr
4eac44c8df [ci skip] Akarin 2018-08-13 04:43:15 +08:00
Sotr
0c3fec2c10 Upstream Paper and misc 2018-08-13 04:39:22 +08:00
Sotr
46e8595949 Upstream Paper 2018-08-13 01:04:39 +08:00
Sotr
59b1567405 Shared random to world 2018-08-13 00:56:48 +08:00
Sotr
f8e01fa0f9 Safety timings w/ Configurable ticking mode 2018-08-13 00:38:12 +08:00
Sotr
1c5da01dec [ci skip] Cleanup 2018-08-12 20:53:24 +08:00
Sotr
916c298fbf Safety plugin w/ Fully parallel entity ticking 2018-08-12 20:17:12 +08:00
Sotr
72f3d31652 Sync implementation changes for better performance 2018-08-11 18:03:17 +08:00
Sotr
64f2e7dcd1 Fixes a rare dead lock 2018-08-11 17:43:04 +08:00
Sotr
2695e3551f CircleCI 2.0 2018-08-11 03:08:07 +08:00
Sotr
c16c1ef11c Removed unneed volatile and atomic 2018-08-11 02:40:07 +08:00
Sotr
687b8369a6 Synchronization block instead of ReentrantReadWriteLock 2018-08-11 02:32:54 +08:00
Sotr
779148ddc1 Have our nice lock back 2018-08-11 02:17:28 +08:00
Sotr
d4f5420183 Fixes lock 2018-08-11 01:03:07 +08:00
Sotr
b4fa26e0c1 Fixes crap 2018-08-11 00:09:00 +08:00
Sotr
90adbd71c7 Cleanup 2018-08-10 22:50:03 +08:00
Sotr
9f7d36ff50 Properly set reentrant number 2018-08-10 22:45:58 +08:00
Sotr
6dc0a094df Upstream Paper 2018-08-10 22:09:08 +08:00
Sotr
5005a19195 Hopefully fixes dead lock 2018-08-10 22:01:24 +08:00
Sotr
a64ee6fa0d Resolves GH-46 2018-08-10 21:05:37 +08:00
Sotr
67817f6ba2 Upstream Paper (untested) 2018-08-10 19:11:55 +08:00
Sotr
7eea320298 Corrects shadow type 2018-08-10 19:10:31 +08:00
Sotr
0edce075eb Hopefully fixes dead lock 2018-08-08 19:30:39 +08:00
Sotr
042c7b3301 [ci skip] Upstream Paper 2018-08-08 01:50:41 +08:00
Sotr
a7b15975df Fixes netty decode 2018-08-08 01:41:09 +08:00
Sotr
5d8221f7aa Corrects check in last change 2018-08-05 20:13:34 +08:00
Sotr
598d8ed104 Fixes spawner modify feature 2018-08-05 20:01:41 +08:00
Sotr
56066ac93e Upstream Paper (fixed) 2018-08-05 19:21:01 +08:00
Sotr
eac7e704f1 1.13 submodule! 2018-08-05 19:16:43 +08:00
Sotr
e45247fab4 oops! update submodule 2018-08-05 19:13:12 +08:00
Sotr
996c3e4949 Upstream Paper 2018-08-05 19:09:01 +08:00
Sotr
48b9fea5af Corrects status branch w/ build 2018-08-05 06:47:08 +08:00
Sotr
7e6de94cec [CI-SKIP] Update Readme 2018-08-05 06:36:31 +08:00
Sotr
563d3973c1 Base 1.13 implementation 2018-08-05 06:28:24 +08:00
91 changed files with 6036 additions and 12597 deletions

1
.gitmodules vendored
View File

@@ -1,3 +1,4 @@
[submodule "work/Paper"]
path = work/Paper
url = https://github.com/Akarin-project/Paper.git
branch = pre/1.13

View File

@@ -2,7 +2,7 @@
[![Powered by](https://img.shields.io/badge/Powered_by-Akarin_project-ee6aa7.svg?style=flat)](https://akarin.io)
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/fw2pJAj)
[![bStats](https://img.shields.io/badge/bStats-Torch-0099ff.svg?style=flat)](https://bstats.org/plugin/bukkit/Torch)
[![Circle CI](https://circleci.com/gh/Akarin-project/Akarin/tree/master.svg?style=svg)](https://circleci.com/gh/Akarin-project/Akarin/tree/ver/1.12.2)
[![Circle CI](https://circleci.com/gh/Akarin-project/Akarin/tree/master.svg?style=svg)](https://circleci.com/gh/Akarin-project/Akarin/tree/ver/1.13)
Akarin is currently **under heavy development** and contributions are welcome!
@@ -24,8 +24,8 @@ Get Akarin
---
### Download
#### Recommended
+ ~~[**Jenkins**](http://ci.ilummc.com/job/Akarin/)~~ - Kudos to [Izzel_Aliz](https://github.com/IzzelAliz)
+ [**Circle CI**](https://circleci.com/gh/Akarin-project/Akarin/tree/ver/1.12.2) - Checkout the 'Artifacts' tab of the latest build
+ [**Jenkins**](https://jenkins.bennydoesstuff.me/view/Akarin/) - Kudos to [BennyDoesStuff](https://github.com/BennyDoesTheStuff)
+ [**Circle CI**](https://circleci.com/gh/Akarin-project/Akarin/tree/ver/1.13) - Checkout the 'Artifacts' tab of the latest build
*Open an [Issue](https://github.com/Akarin-project/Akarin/issues) or a [Pull Request](https://github.com/Akarin-project/Akarin/pulls) if you want to add your website here*

View File

@@ -0,0 +1,82 @@
package com.destroystokyo.paper.antixray;
import io.netty.buffer.ByteBuf;
import net.minecraft.server.Chunk;
import net.minecraft.server.DataPalette;
import net.minecraft.server.PacketPlayOutMapChunk;
public class ChunkPacketInfo<T> {
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
private final Chunk chunk;
private final int chunkSectionSelector;
private ByteBuf data; // Akarin
private final int[] bitsPerObject = new int[16];
private final Object[] dataPalettes = new Object[16];
private final int[] dataBitsIndexes = new int[16];
private final Object[][] predefinedObjects = new Object[16][];
public ChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
this.packetPlayOutMapChunk = packetPlayOutMapChunk;
this.chunk = chunk;
this.chunkSectionSelector = chunkSectionSelector;
}
public PacketPlayOutMapChunk getPacketPlayOutMapChunk() {
return packetPlayOutMapChunk;
}
public Chunk getChunk() {
return chunk;
}
public int getChunkSectionSelector() {
return chunkSectionSelector;
}
public ByteBuf getData() { // Akarin
return data;
}
public void setData(ByteBuf data) { // Akarin
this.data = data;
}
public int getBitsPerObject(int chunkSectionIndex) {
return bitsPerObject[chunkSectionIndex];
}
public void setBitsPerObject(int chunkSectionIndex, int bitsPerObject) {
this.bitsPerObject[chunkSectionIndex] = bitsPerObject;
}
@SuppressWarnings("unchecked")
public DataPalette<T> getDataPalette(int chunkSectionIndex) {
return (DataPalette<T>) dataPalettes[chunkSectionIndex];
}
public void setDataPalette(int chunkSectionIndex, DataPalette<T> dataPalette) {
dataPalettes[chunkSectionIndex] = dataPalette;
}
public int getDataBitsIndex(int chunkSectionIndex) {
return dataBitsIndexes[chunkSectionIndex];
}
public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) {
dataBitsIndexes[chunkSectionIndex] = dataBitsIndex;
}
@SuppressWarnings("unchecked")
public T[] getPredefinedObjects(int chunkSectionIndex) {
return (T[]) predefinedObjects[chunkSectionIndex];
}
public void setPredefinedObjects(int chunkSectionIndex, T[] predefinedObjects) {
this.predefinedObjects[chunkSectionIndex] = predefinedObjects;
}
public boolean isWritten(int chunkSectionIndex) {
return bitsPerObject[chunkSectionIndex] != 0;
}
}

View File

@@ -0,0 +1,62 @@
package com.destroystokyo.paper.antixray;
import io.netty.buffer.ByteBuf;
public class DataBitsReader {
private ByteBuf dataBits; // Akarin
private int bitsPerObject;
private int mask;
private int longInDataBitsIndex;
private int bitInLongIndex;
private long current;
public void setDataBits(ByteBuf dataBits) { // Akarin
this.dataBits = dataBits;
}
public void setBitsPerObject(int bitsPerObject) {
this.bitsPerObject = bitsPerObject;
mask = (1 << bitsPerObject) - 1;
}
public void setIndex(int index) {
this.longInDataBitsIndex = index;
bitInLongIndex = 0;
init();
}
private void init() {
if (dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin
// Akarin start
dataBits.getLong(longInDataBitsIndex);
/*
current = ((((long) dataBits[longInDataBitsIndex]) << 56)
| (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
| (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
| (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
| (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
| (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
| (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
| (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
*/ // Akarin end
}
}
public int read() {
int value = (int) (current >>> bitInLongIndex) & mask;
bitInLongIndex += bitsPerObject;
if (bitInLongIndex > 63) {
bitInLongIndex -= 64;
longInDataBitsIndex += 8;
init();
if (bitInLongIndex > 0) {
value |= current << bitsPerObject - bitInLongIndex & mask;
}
}
return value;
}
}

View File

@@ -0,0 +1,94 @@
package com.destroystokyo.paper.antixray;
import io.netty.buffer.ByteBuf;
public class DataBitsWriter {
private ByteBuf dataBits; // Akarin
private int bitsPerObject;
private long mask;
private int longInDataBitsIndex;
private int bitInLongIndex;
private long current;
private boolean dirty;
public void setDataBits(ByteBuf dataBits) { // Akarin
this.dataBits = dataBits;
}
public void setBitsPerObject(int bitsPerObject) {
this.bitsPerObject = bitsPerObject;
mask = (1 << bitsPerObject) - 1;
}
public void setIndex(int index) {
this.longInDataBitsIndex = index;
bitInLongIndex = 0;
init();
}
private void init() {
if (dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin
// Akarin start
current = dataBits.getLong(longInDataBitsIndex);
/*
current = ((((long) dataBits[longInDataBitsIndex]) << 56)
| (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
| (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
| (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
| (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
| (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
| (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
| (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
*/ // Akarin end
}
dirty = false;
}
public void finish() {
if (dirty && dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin
// Akarin start
dataBits.setLong(longInDataBitsIndex, current);
/*
dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff);
dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff);
dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff);
dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff);
dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff);
dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff);
dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff);
dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff);
*/ // Akarin end
}
}
public void write(int value) {
current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex;
dirty = true;
bitInLongIndex += bitsPerObject;
if (bitInLongIndex > 63) {
finish();
bitInLongIndex -= 64;
longInDataBitsIndex += 8;
init();
if (bitInLongIndex > 0) {
current = current & ~(mask >>> bitsPerObject - bitInLongIndex) | (value & mask) >>> bitsPerObject - bitInLongIndex;
dirty = true;
}
}
}
public void skip() {
bitInLongIndex += bitsPerObject;
if (bitInLongIndex > 63) {
finish();
bitInLongIndex -= 64;
longInDataBitsIndex += 8;
init();
}
}
}

View File

@@ -1,17 +1,15 @@
package net.minecraft.server;
import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray
import com.google.common.collect.Lists;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.io.IOException;
import java.security.KeyStore.PrivateKeyEntry;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
// Paper start
import com.destroystokyo.paper.antixray.PacketPlayOutMapChunkInfo; // Anti-Xray
// Paper end
/**
* Akarin Changes Note
* 1) WrappedByteBuf -> ByteBuf (compatibility)
@@ -21,7 +19,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
private int a;
private int b;
private int c;
private ByteBuf d; // Akarin - byte[] -> ByteBuf
private ByteBuf d; private ByteBuf getData() { return this.d; } // Paper - OBFHELPER // Akarin - byte[] -> ByteBuf
private List<NBTTagCompound> e;
private boolean f;
private volatile boolean ready = false; // Paper - Async-Anti-Xray - Ready flag for the network manager
@@ -33,21 +31,21 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
// Paper end
public PacketPlayOutMapChunk(Chunk chunk, int i) {
PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo = chunk.world.chunkPacketBlockController.getPacketPlayOutMapChunkInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info
ChunkPacketInfo<IBlockData> chunkPacketInfo = chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info
this.a = chunk.locX;
this.b = chunk.locZ;
this.f = i == '\uffff';
boolean flag = chunk.getWorld().worldProvider.m();
boolean flag = chunk.getWorld().worldProvider.g();
this.d = allocateBuffer(this.a(chunk, flag, i)); // Akarin
// Paper start - Anti-Xray - Add chunk packet info
if (packetPlayOutMapChunkInfo != null) {
packetPlayOutMapChunkInfo.setData(this.d);
if (chunkPacketInfo != null) {
chunkPacketInfo.setData(this.getData());
}
// Paper end
this.c = this.writeChunk(new PacketDataSerializer(this.d), chunk, flag, i, packetPlayOutMapChunkInfo); // Paper - Anti-Xray - Add chunk packet info // Akarin
this.c = this.writeChunk(new PacketDataSerializer(this.getData()), chunk, flag, i, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info // Akarin
this.e = Lists.newArrayList();
Iterator iterator = chunk.getTileEntities().entrySet().iterator();
@@ -57,14 +55,15 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
TileEntity tileentity = (TileEntity) entry.getValue();
int j = blockposition.getY() >> 4;
if (this.e() || (i & 1 << j) != 0) {
NBTTagCompound nbttagcompound = tileentity.d();
if (this.f() || (i & 1 << j) != 0) {
NBTTagCompound nbttagcompound = tileentity.aa_();
if (tileentity instanceof TileEntitySkull) { TileEntitySkull.sanitizeTileEntityUUID(nbttagcompound); } // Paper
this.e.add(nbttagcompound);
}
}
chunk.world.chunkPacketBlockController.modifyBlocks(this, packetPlayOutMapChunkInfo); // Paper - Anti-Xray - Modify blocks
chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
}
// Paper start - Async-Anti-Xray - Getter and Setter for the ready flag
@@ -105,8 +104,8 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
packetdataserializer.writeInt(this.b);
packetdataserializer.writeBoolean(this.f);
packetdataserializer.d(this.c);
packetdataserializer.d(this.d.array().length); // Akarin
packetdataserializer.writeBytes(this.d.array()); // Akarin
packetdataserializer.d(this.getData().capacity()); // Akarin
packetdataserializer.writeBytes(this.getData().array()); // Akarin
packetdataserializer.d(this.e.size());
Iterator iterator = this.e.iterator();
@@ -122,33 +121,35 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
packetlistenerplayout.a(this);
}
private ByteBuf allocateBuffer(int expectedCapacity) { return g(expectedCapacity); } // Akarin - OBFHELPER
private ByteBuf g(int expectedCapacity) { // Akarin - added argument
ByteBuf bytebuf = Unpooled.buffer(expectedCapacity); // Akarin
private ByteBuf h() { return allocateBuffer(-1); } // Akarin
private ByteBuf allocateBuffer(int expectedCapacity) { // Akarin - added argument
ByteBuf bytebuf = expectedCapacity == -1 ? Unpooled.buffer() : Unpooled.buffer(expectedCapacity); // Akarin
bytebuf.writerIndex(0);
return bytebuf;
}
// Paper start - Anti-Xray - Support default method
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); } // OBFHELPER
// Paper start - Anti-Xray - Support default methods
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); }
public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i) {
return this.a(packetdataserializer, chunk, flag, i, null);
}
// Paper end
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector, PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector, packetPlayOutMapChunkInfo); } // Paper - Anti-Xray - OBFHELPER
public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i, PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo) { // Paper - Anti-Xray - Add chunk packet info
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector, ChunkPacketInfo<IBlockData> chunkPacketInfo) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector, chunkPacketInfo); } // Paper - OBFHELPER // Paper - Anti-Xray - Add chunk packet info
public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i, ChunkPacketInfo<IBlockData> chunkPacketInfo) { // Paper - Anti-Xray - Add chunk packet info
int j = 0;
ChunkSection[] achunksection = chunk.getSections();
int k = 0;
for (int l = achunksection.length; k < l; ++k) {
int l;
for (l = achunksection.length; k < l; ++k) {
ChunkSection chunksection = achunksection[k];
if (chunksection != Chunk.a && (!this.e() || !chunksection.a()) && (i & 1 << k) != 0) {
if (chunksection != Chunk.a && (!this.f() || !chunksection.a()) && (i & 1 << k) != 0) {
j |= 1 << k;
chunksection.getBlocks().writeBlocks(packetdataserializer, packetPlayOutMapChunkInfo, k); // Paper - Anti-Xray - Add chunk packet info
chunksection.getBlocks().writeDataPaletteBlock(packetdataserializer, chunkPacketInfo, k); // Paper - Anti-Xray - Add chunk packet info
packetdataserializer.writeBytes(chunksection.getEmittedLightArray().asBytes());
if (flag) {
packetdataserializer.writeBytes(chunksection.getSkyLightArray().asBytes());
@@ -156,8 +157,12 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
}
}
if (this.e()) {
packetdataserializer.writeBytes(chunk.getBiomeIndex());
if (this.f()) {
BiomeBase[] abiomebase = chunk.getBiomeIndex();
for (l = 0; l < abiomebase.length; ++l) {
packetdataserializer.writeInt(IRegistry.BIOME.a(abiomebase[l])); // Paper - decompile fix
}
}
return j;
@@ -171,7 +176,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
for (int l = achunksection.length; k < l; ++k) {
ChunkSection chunksection = achunksection[k];
if (chunksection != Chunk.a && (!this.e() || !chunksection.a()) && (i & 1 << k) != 0) {
if (chunksection != Chunk.a && (!this.f() || !chunksection.a()) && (i & 1 << k) != 0) {
j += chunksection.getBlocks().a();
j += chunksection.getEmittedLightArray().asBytes().length;
if (flag) {
@@ -180,14 +185,14 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
}
}
if (this.e()) {
j += chunk.getBiomeIndex().length;
if (this.f()) {
j += chunk.getBiomeIndex().length * 4;
}
return j;
}
public boolean e() {
public boolean f() {
return this.f;
}
}

View File

@@ -4,6 +4,7 @@
set -e
basedir="$(cd "$1" && pwd -P)"
workdir="$basedir/work"
version="ver/1.13"
paperbasedir="$basedir/work/Paper"
paperworkdir="$basedir/work/Paper/work"
@@ -16,6 +17,7 @@ if [ "$2" == "--setup" ] || [ "$3" == "--setup" ] || [ "$4" == "--setup" ]; then
rm Minecraft/ -r
fi
git clone https://github.com/Akarin-project/Minecraft.git
cd "Minecraft" && git checkout "$version"
fi
cd "$paperbasedir"
@@ -25,6 +27,8 @@ fi
echo "[Akarin] Ready to build"
(
cd "$paperworkdir/BuildData" && git checkout "$version"
cd "$paperbasedir"
echo "[Akarin] Touch sources.."
cd "$paperbasedir"

View File

@@ -2,9 +2,10 @@
(
set -e
basedir="$pwd"
basedir="$(pwd -P)"
version="pre/1.13"
(git submodule update --init --remote && chmod +x scripts/build.sh && ./scripts/build.sh "$basedir" "$1" "$2" "$3") || (
(git submodule update --init --remote && cd "work/Paper" && git checkout "$version" && cd "$basedir" && chmod +x scripts/build.sh && ./scripts/build.sh "$basedir" "$1" "$2" "$3") || (
echo "Failed to build Akarin"
exit 1
) || exit 1

View File

@@ -3,15 +3,15 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>akarin</artifactId>
<packaging>jar</packaging>
<version>1.12.2-R0.4-SNAPSHOT</version>
<version>1.13.1-R0.1-SNAPSHOT</version>
<name>Akarin</name>
<url>https://github.com/Akarin-project/Akarin</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<api.version>1.12.2-R0.1-SNAPSHOT</api.version>
<minecraft.version>1.12.2</minecraft.version>
<minecraft_version>1_12_R1</minecraft_version>
<api.version>1.13.1-R0.1-SNAPSHOT</api.version>
<minecraft.version>1.13.1</minecraft.version>
<minecraft_version>1_13_R2</minecraft_version>
<buildtag.prefix>git-Bukkit-</buildtag.prefix>
<buildtag.suffix></buildtag.suffix>
<maven.build.timestamp.format>yyyyMMdd-HHmm</maven.build.timestamp.format>
@@ -26,13 +26,6 @@
</parent>
<dependencies>
<!-- bugfixes (netty#6607) -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.24.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.destroystokyo.paper</groupId>
<artifactId>paper-api</artifactId>
@@ -46,30 +39,11 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.sf.jopt-simple</groupId>
<artifactId>jopt-simple</artifactId>
<version>5.0.4</version>
<groupId>org.spigotmc</groupId>
<artifactId>minecraft-server</artifactId>
<version>${minecraft.version}-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.21.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.sf.trove4j</groupId>
<artifactId>trove4j</artifactId>
<version>3.0.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.minecrell</groupId>
<artifactId>terminalconsoleappender</artifactId>
@@ -81,7 +55,6 @@
<version>4.5.2</version>
<scope>runtime</scope>
</dependency>
<!--
Required to add the missing Log4j2Plugins.dat file from log4j-core
which has been removed by Mojang. Without it, log4j has to classload
@@ -94,8 +67,6 @@
<version>2.8.1</version>
<scope>compile</scope>
</dependency>
<!-- Paper - Add additional Log4J dependencies -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
@@ -107,7 +78,6 @@
<artifactId>log4j-iostreams</artifactId>
<version>2.8.1</version>
</dependency>
<!-- Paper - Async loggers -->
<dependency>
<groupId>com.lmax</groupId>
@@ -115,7 +85,30 @@
<version>3.4.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>6.2.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.23.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.sf.trove4j</groupId>
<artifactId>trove4j</artifactId>
<version>3.0.3</version>
<scope>compile</scope>
</dependency>
<!-- testing -->
<dependency>
<groupId>junit</groupId>
@@ -129,12 +122,11 @@
<version>1.3</version>
<scope>test</scope>
</dependency>
<!-- Akarin -->
<dependency>
<groupId>io.akarin</groupId>
<artifactId>legacylauncher</artifactId>
<version>1.26</version>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.spongepowered</groupId>
@@ -190,7 +182,7 @@
<version>1.3</version>
<configuration>
<outputPrefix>git-Akarin-</outputPrefix>
<scmDirectory>../..</scmDirectory> <!-- Akarin -->
<scmDirectory>../..</scmDirectory>
</configuration>
<executions>
<execution>
@@ -211,6 +203,7 @@
<manifestEntries>
<Main-Class>net.minecraft.launchwrapper.Launch</Main-Class>
<Implementation-Title>CraftBukkit</Implementation-Title>
<!--suppress MavenModelInspection -->
<Implementation-Version>${describe}</Implementation-Version>
<Implementation-Vendor>${maven.build.timestamp}</Implementation-Vendor>
<Specification-Title>Bukkit</Specification-Title>
@@ -243,7 +236,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<version>3.1.1</version>
<executions>
<execution>
<phase>package</phase>
@@ -315,8 +308,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<!-- BUILDTOOLS-362 / SUREFIRE-1444 - newer version fails in Docker -->
<version>2.20</version>
<version>2.12.4</version>
<configuration>
<workingDirectory>${basedir}/target/test-server</workingDirectory>
<excludes>
@@ -340,7 +332,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.16</version>
<version>1.17</version>
<executions>
<execution>
<phase>process-classes</phase>

View File

@@ -0,0 +1,127 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;
import static co.aikar.util.JSONUtil.toArray;
/**
* Akarin Changes Note
* 1) Thread safe timing (safety)
*/
/**
* <p>Lightweight object for tracking timing data</p>
*
* This is broken out to reduce memory usage
*/
class TimingData {
private final int id;
private int count = 0;
private int lagCount = 0;
private long totalTime = 0;
private long lagTotalTime = 0;
private AtomicInteger curTickCount = new AtomicInteger(); // Akarin
private LongAdder curTickTotal = new LongAdder(); // Akarin
TimingData(int id) {
this.id = id;
}
private TimingData(TimingData data) {
this.id = data.id;
this.totalTime = data.totalTime;
this.lagTotalTime = data.lagTotalTime;
this.count = data.count;
this.lagCount = data.lagCount;
}
void add(long diff) {
curTickCount.incrementAndGet();
curTickTotal.add(diff);
}
void processTick(boolean violated) {
totalTime += curTickTotal.sum();
count += curTickCount.get();
if (violated) {
lagTotalTime += curTickTotal.sum();
lagCount += curTickCount.get();
}
curTickTotal.reset();
curTickCount.set(0);
}
void reset() {
count = 0;
lagCount = 0;
curTickTotal.reset();
curTickCount.set(0);
totalTime = 0;
lagTotalTime = 0;
}
protected TimingData clone() {
return new TimingData(this);
}
List<Object> export() {
List<Object> list = toArray(
id,
count,
totalTime);
if (lagCount > 0) {
list.add(lagCount);
list.add(lagTotalTime);
}
return list;
}
boolean hasData() {
return count > 0;
}
long getTotalTime() {
return totalTime;
}
int getCurTickCount() {
return curTickCount.get();
}
void setCurTickCount(int curTickCount) {
this.curTickCount.getAndSet(curTickCount);
}
long getCurTickTotal() {
return curTickTotal.sum();
}
void setCurTickTotal(long curTickTotal) {
this.curTickTotal.reset();
this.curTickTotal.add(curTickTotal);
}
}

View File

@@ -0,0 +1,218 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import co.aikar.util.LoadingIntMap;
import io.akarin.api.internal.Akari;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.bukkit.Bukkit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* Akarin Changes Note
* 1) Thread safe timing (safety)
*/
class TimingHandler implements Timing {
private static AtomicInteger idPool = new AtomicInteger(1);
final int id = idPool.getAndIncrement();
final String name;
private final boolean verbose;
private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
final TimingData record;
private final TimingHandler groupHandler;
private AtomicLong start = new AtomicLong(); // Akarin
private AtomicInteger timingDepth = new AtomicInteger(); // Akarin
private volatile boolean added; // Akarin
private boolean timed;
private boolean enabled;
private volatile TimingHandler parent; // Akarin
TimingHandler(TimingIdentifier id) {
if (id.name.startsWith("##")) {
verbose = true;
this.name = id.name.substring(3);
} else {
this.name = id.name;
verbose = false;
}
this.record = new TimingData(this.id);
this.groupHandler = id.groupHandler;
TimingIdentifier.getGroup(id.group).handlers.add(this);
checkEnabled();
}
final void checkEnabled() {
enabled = Timings.timingsEnabled && (!verbose || Timings.verboseEnabled);
}
void processTick(boolean violated) {
if (timingDepth.get() != 0 || record.getCurTickCount() == 0) {
timingDepth.set(0);
start.set(0);
return;
}
record.processTick(violated);
Akari.timingsLock.lock(); // Akarin
for (TimingData handler : children.values()) {
handler.processTick(violated);
}
Akari.timingsLock.unlock(); // Akarin
}
@Override
public Timing startTimingIfSync() {
startTiming();
return this;
}
@Override
public void stopTimingIfSync() {
stopTiming();
}
public Timing startTiming() {
if (enabled && /*Bukkit.isPrimaryThread() &&*/ timingDepth.incrementAndGet() == 1) { // Akarin
start.getAndSet(System.nanoTime());
parent = TimingsManager.CURRENT;
TimingsManager.CURRENT = this;
}
return this;
}
public void stopTiming() {
if (enabled && start.get() != 0 && /*Bukkit.isPrimaryThread() &&*/ timingDepth.decrementAndGet() == 0) { // Akarin
long prev = start.getAndSet(0); // Akarin
addDiff(System.nanoTime() - prev); // Akarin
}
}
@Override
public void abort() {
if (enabled && timingDepth.get() > 0) {
start.getAndSet(0);
}
}
void addDiff(long diff) {
if (TimingsManager.CURRENT == this) {
TimingsManager.CURRENT = parent;
if (parent != null) {
Akari.timingsLock.lock(); // Akarin
parent.children.get(id).add(diff);
Akari.timingsLock.unlock(); // Akarin
}
}
record.add(diff);
if (!added) {
added = true;
timed = true;
Akari.timingsLock.lock(); // Akarin
TimingsManager.HANDLERS.add(this);
Akari.timingsLock.unlock(); // Akarin
}
if (groupHandler != null) {
groupHandler.addDiff(diff);
Akari.timingsLock.lock(); // Akarin
groupHandler.children.get(id).add(diff);
Akari.timingsLock.unlock(); // Akarin
}
}
/**
* Reset this timer, setting all values to zero.
*
* @param full
*/
void reset(boolean full) {
record.reset();
if (full) {
timed = false;
}
start.set(0);
timingDepth.set(0);
added = false;
Akari.timingsLock.lock(); // Akarin
children.clear();
Akari.timingsLock.unlock(); // Akarin
checkEnabled();
}
@Override
public TimingHandler getTimingHandler() {
return this;
}
@Override
public boolean equals(Object o) {
return (this == o);
}
@Override
public int hashCode() {
return id;
}
/**
* This is simply for the Closeable interface so it can be used with
* try-with-resources ()
*/
@Override
public void close() {
stopTimingIfSync();
}
public boolean isSpecial() {
return this == TimingsManager.FULL_SERVER_TICK || this == TimingsManager.TIMINGS_TICK;
}
boolean isTimed() {
return timed;
}
public boolean isEnabled() {
return enabled;
}
TimingData[] cloneChildren() {
int i = 0;
Akari.timingsLock.lock(); // Akarin
final TimingData[] clonedChildren = new TimingData[children.size()];
for (TimingData child : children.values()) {
clonedChildren[i++] = child.clone();
}
Akari.timingsLock.unlock(); // Akarin
return clonedChildren;
}
}

View File

@@ -0,0 +1,355 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import co.aikar.timings.TimingHistory.RegionData.RegionId;
import co.aikar.util.JSONUtil;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import co.aikar.util.LoadingMap;
import co.aikar.util.MRUMapCache;
import io.akarin.api.internal.Akari;
import java.lang.management.ManagementFactory;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static co.aikar.timings.TimingsManager.FULL_SERVER_TICK;
import static co.aikar.timings.TimingsManager.MINUTE_REPORTS;
import static co.aikar.util.JSONUtil.*;
@SuppressWarnings({"deprecation", "SuppressionAnnotation", "Convert2Lambda", "Anonymous2MethodRef"})
public class TimingHistory {
public static long lastMinuteTime;
public static long timedTicks;
public static long playerTicks;
public static long entityTicks;
public static long tileEntityTicks;
public static long activatedEntityTicks;
private static int worldIdPool = 1;
static Map<String, Integer> worldMap = LoadingMap.newHashMap(new Function<String, Integer>() {
@Override
public Integer apply(String input) {
return worldIdPool++;
}
});
private final long endTime;
private final long startTime;
private final long totalTicks;
private final long totalTime; // Represents all time spent running the server this history
private final MinuteReport[] minuteReports;
private final TimingHistoryEntry[] entries;
final Set<Material> tileEntityTypeSet = Sets.newHashSet();
final Set<EntityType> entityTypeSet = Sets.newHashSet();
private final Map<Object, Object> worlds;
TimingHistory() {
this.endTime = System.currentTimeMillis() / 1000;
this.startTime = TimingsManager.historyStart / 1000;
if (timedTicks % 1200 != 0 || MINUTE_REPORTS.isEmpty()) {
this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size() + 1]);
this.minuteReports[this.minuteReports.length - 1] = new MinuteReport();
} else {
this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size()]);
}
long ticks = 0;
for (MinuteReport mp : this.minuteReports) {
ticks += mp.ticksRecord.timed;
}
this.totalTicks = ticks;
this.totalTime = FULL_SERVER_TICK.record.getTotalTime();
Akari.timingsLock.lock(); // Akarin
this.entries = new TimingHistoryEntry[TimingsManager.HANDLERS.size()];
int i = 0;
for (TimingHandler handler : TimingsManager.HANDLERS) {
entries[i++] = new TimingHistoryEntry(handler);
}
Akari.timingsLock.unlock(); // Akarin
// Information about all loaded chunks/entities
//noinspection unchecked
this.worlds = toObjectMapper(Bukkit.getWorlds(), new Function<World, JSONPair>() {
@Override
public JSONPair apply(World world) {
Map<RegionId, RegionData> regions = LoadingMap.newHashMap(RegionData.LOADER);
for (Chunk chunk : world.getLoadedChunks()) {
RegionData data = regions.get(new RegionId(chunk.getX(), chunk.getZ()));
for (Entity entity : chunk.getEntities()) {
if (entity == null) {
Bukkit.getLogger().warning("Null entity detected in chunk at position x: " + chunk.getX() + ", z: " + chunk.getZ());
continue;
}
data.entityCounts.get(entity.getType()).increment();
}
for (BlockState tileEntity : chunk.getTileEntities()) {
if (tileEntity == null) {
Bukkit.getLogger().warning("Null tileentity detected in chunk at position x: " + chunk.getX() + ", z: " + chunk.getZ());
continue;
}
data.tileEntityCounts.get(tileEntity.getBlock().getType()).increment();
}
}
return pair(
worldMap.get(world.getName()),
toArrayMapper(regions.values(),new Function<RegionData, Object>() {
@Override
public Object apply(RegionData input) {
return toArray(
input.regionId.x,
input.regionId.z,
toObjectMapper(input.entityCounts.entrySet(),
new Function<Map.Entry<EntityType, Counter>, JSONPair>() {
@Override
public JSONPair apply(Map.Entry<EntityType, Counter> entry) {
entityTypeSet.add(entry.getKey());
return pair(
String.valueOf(entry.getKey().getTypeId()),
entry.getValue().count()
);
}
}
),
toObjectMapper(input.tileEntityCounts.entrySet(),
new Function<Map.Entry<Material, Counter>, JSONPair>() {
@Override
public JSONPair apply(Map.Entry<Material, Counter> entry) {
tileEntityTypeSet.add(entry.getKey());
return pair(
String.valueOf(entry.getKey().getId()),
entry.getValue().count()
);
}
}
)
);
}
})
);
}
});
}
static class RegionData {
final RegionId regionId;
@SuppressWarnings("Guava")
static Function<RegionId, RegionData> LOADER = new Function<RegionId, RegionData>() {
@Override
public RegionData apply(RegionId id) {
return new RegionData(id);
}
};
RegionData(RegionId id) {
this.regionId = id;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RegionData that = (RegionData) o;
return regionId.equals(that.regionId);
}
@Override
public int hashCode() {
return regionId.hashCode();
}
@SuppressWarnings("unchecked")
final Map<EntityType, Counter> entityCounts = MRUMapCache.of(LoadingMap.of(
new EnumMap<EntityType, Counter>(EntityType.class), Counter.LOADER
));
@SuppressWarnings("unchecked")
final Map<Material, Counter> tileEntityCounts = MRUMapCache.of(LoadingMap.of(
new EnumMap<Material, Counter>(Material.class), Counter.LOADER
));
static class RegionId {
final int x, z;
final long regionId;
RegionId(int x, int z) {
this.x = x >> 5 << 5;
this.z = z >> 5 << 5;
this.regionId = ((long) (this.x) << 32) + (this.z >> 5 << 5) - Integer.MIN_VALUE;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RegionId regionId1 = (RegionId) o;
return regionId == regionId1.regionId;
}
@Override
public int hashCode() {
return (int) (regionId ^ (regionId >>> 32));
}
}
}
static void resetTicks(boolean fullReset) {
if (fullReset) {
// Non full is simply for 1 minute reports
timedTicks = 0;
}
lastMinuteTime = System.nanoTime();
playerTicks = 0;
tileEntityTicks = 0;
entityTicks = 0;
activatedEntityTicks = 0;
}
Object export() {
return createObject(
pair("s", startTime),
pair("e", endTime),
pair("tk", totalTicks),
pair("tm", totalTime),
pair("w", worlds),
pair("h", toArrayMapper(entries, new Function<TimingHistoryEntry, Object>() {
@Override
public Object apply(TimingHistoryEntry entry) {
TimingData record = entry.data;
if (!record.hasData()) {
return null;
}
return entry.export();
}
})),
pair("mp", toArrayMapper(minuteReports, new Function<MinuteReport, Object>() {
@Override
public Object apply(MinuteReport input) {
return input.export();
}
}))
);
}
static class MinuteReport {
final long time = System.currentTimeMillis() / 1000;
final TicksRecord ticksRecord = new TicksRecord();
final PingRecord pingRecord = new PingRecord();
final TimingData fst = TimingsManager.FULL_SERVER_TICK.minuteData.clone();
final double tps = 1E9 / ( System.nanoTime() - lastMinuteTime ) * ticksRecord.timed;
final double usedMemory = TimingsManager.FULL_SERVER_TICK.avgUsedMemory;
final double freeMemory = TimingsManager.FULL_SERVER_TICK.avgFreeMemory;
final double loadAvg = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();
List<Object> export() {
return toArray(
time,
Math.round(tps * 100D) / 100D,
Math.round(pingRecord.avg * 100D) / 100D,
fst.export(),
toArray(ticksRecord.timed,
ticksRecord.player,
ticksRecord.entity,
ticksRecord.activatedEntity,
ticksRecord.tileEntity
),
usedMemory,
freeMemory,
loadAvg
);
}
}
private static class TicksRecord {
final long timed;
final long player;
final long entity;
final long tileEntity;
final long activatedEntity;
TicksRecord() {
timed = timedTicks - (TimingsManager.MINUTE_REPORTS.size() * 1200);
player = playerTicks;
entity = entityTicks;
tileEntity = tileEntityTicks;
activatedEntity = activatedEntityTicks;
}
}
private static class PingRecord {
final double avg;
PingRecord() {
final Collection<? extends Player> onlinePlayers = Bukkit.getOnlinePlayers();
int totalPing = 0;
for (Player player : onlinePlayers) {
totalPing += player.spigot().getPing();
}
avg = onlinePlayers.isEmpty() ? 0 : totalPing / onlinePlayers.size();
}
}
private static class Counter {
private int count = 0;
@SuppressWarnings({"rawtypes", "SuppressionAnnotation", "Guava"})
static Function LOADER = new LoadingMap.Feeder<Counter>() {
@Override
public Counter apply() {
return new Counter();
}
};
public int increment() {
return ++count;
}
public int count() {
return count;
}
}
}

View File

@@ -0,0 +1,198 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import com.google.common.collect.EvictingQueue;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.PluginClassLoader;
import co.aikar.util.LoadingMap;
import io.akarin.api.internal.Akari;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
/**
* Akarin Changes Note
* 1) Thread safe timing (safety)
*/
public final class TimingsManager {
static final Map<TimingIdentifier, TimingHandler> TIMING_MAP =
Collections.synchronizedMap(LoadingMap.newHashMap(
TimingHandler::new,
4096, .5F
));
public static final FullServerTickHandler FULL_SERVER_TICK = new FullServerTickHandler();
public static final TimingHandler TIMINGS_TICK = Timings.ofSafe("Timings Tick", FULL_SERVER_TICK);
public static final Timing PLUGIN_GROUP_HANDLER = Timings.ofSafe("Plugins");
public static List<String> hiddenConfigs = new ArrayList<String>();
public static boolean privacy = false;
static final Collection<TimingHandler> HANDLERS = new ArrayDeque<TimingHandler>();
static final ArrayDeque<TimingHistory.MinuteReport> MINUTE_REPORTS = new ArrayDeque<TimingHistory.MinuteReport>();
static EvictingQueue<TimingHistory> HISTORY = EvictingQueue.create(12);
static TimingHandler CURRENT;
static long timingStart = 0;
static long historyStart = 0;
static boolean needsFullReset = false;
static boolean needsRecheckEnabled = false;
private TimingsManager() {}
/**
* Resets all timing data on the next tick
*/
static void reset() {
needsFullReset = true;
}
/**
* Ticked every tick by CraftBukkit to count the number of times a timer
* caused TPS loss.
*/
static void tick() {
if (Timings.timingsEnabled) {
boolean violated = FULL_SERVER_TICK.isViolated();
Akari.timingsLock.lock(); // Akarin
for (TimingHandler handler : HANDLERS) {
if (handler.isSpecial()) {
// We manually call this
continue;
}
handler.processTick(violated);
}
Akari.timingsLock.unlock(); // Akarin
TimingHistory.playerTicks += Bukkit.getOnlinePlayers().size();
TimingHistory.timedTicks++;
// Generate TPS/Ping/Tick reports every minute
}
}
static void stopServer() {
Timings.timingsEnabled = false;
recheckEnabled();
}
static void recheckEnabled() {
synchronized (TIMING_MAP) {
for (TimingHandler timings : TIMING_MAP.values()) {
timings.checkEnabled();
}
}
needsRecheckEnabled = false;
}
static void resetTimings() {
if (needsFullReset) {
// Full resets need to re-check every handlers enabled state
// Timing map can be modified from async so we must sync on it.
synchronized (TIMING_MAP) {
for (TimingHandler timings : TIMING_MAP.values()) {
timings.reset(true);
}
}
Bukkit.getLogger().log(Level.INFO, "Timings Reset");
HISTORY.clear();
needsFullReset = false;
needsRecheckEnabled = false;
timingStart = System.currentTimeMillis();
} else {
// Soft resets only need to act on timings that have done something
// Handlers can only be modified on main thread.
Akari.timingsLock.lock(); // Akarin
for (TimingHandler timings : HANDLERS) {
timings.reset(false);
}
Akari.timingsLock.unlock(); // Akarin
}
Akari.timingsLock.lock(); // Akarin
HANDLERS.clear();
Akari.timingsLock.unlock(); // Akarin
MINUTE_REPORTS.clear();
TimingHistory.resetTicks(true);
historyStart = System.currentTimeMillis();
}
static TimingHandler getHandler(String group, String name, Timing parent) {
return TIMING_MAP.get(new TimingIdentifier(group, name, parent));
}
/**
* <p>Due to access restrictions, we need a helper method to get a Command TimingHandler with String group</p>
*
* Plugins should never call this
*
* @param pluginName Plugin this command is associated with
* @param command Command to get timings for
* @return TimingHandler
*/
public static Timing getCommandTiming(String pluginName, Command command) {
Plugin plugin = null;
final Server server = Bukkit.getServer();
if (!( server == null || pluginName == null ||
"minecraft".equals(pluginName) || "bukkit".equals(pluginName) ||
"spigot".equalsIgnoreCase(pluginName) || "paper".equals(pluginName)
)) {
plugin = server.getPluginManager().getPlugin(pluginName);
}
if (plugin == null) {
// Plugin is passing custom fallback prefix, try to look up by class loader
plugin = getPluginByClassloader(command.getClass());
}
if (plugin == null) {
return Timings.ofSafe("Command: " + pluginName + ":" + command.getTimingName());
}
return Timings.ofSafe(plugin, "Command: " + pluginName + ":" + command.getTimingName());
}
/**
* Looks up the class loader for the specified class, and if it is a PluginClassLoader, return the
* Plugin that created this class.
*
* @param clazz Class to check
* @return Plugin if created by a plugin
*/
public static Plugin getPluginByClassloader(Class<?> clazz) {
if (clazz == null) {
return null;
}
final ClassLoader classLoader = clazz.getClassLoader();
if (classLoader instanceof PluginClassLoader) {
PluginClassLoader pluginClassLoader = (PluginClassLoader) classLoader;
return pluginClassLoader.getPlugin();
}
return null;
}
}

View File

@@ -1,85 +0,0 @@
package com.destroystokyo.paper.antixray;
import io.netty.buffer.ByteBuf;
import net.minecraft.server.Chunk;
import net.minecraft.server.DataPalette;
import net.minecraft.server.IBlockData;
import net.minecraft.server.PacketPlayOutMapChunk;
/**
* Akarin Changes Note
* 1) byte[] -> ByteBuf (compatibility)
*/
public class PacketPlayOutMapChunkInfo {
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
private final Chunk chunk;
private final int chunkSectionSelector;
private ByteBuf data; // Akarin - byte[] -> ByteBuf
private final int[] bitsPerValue = new int[16];
private final DataPalette[] dataPalettes = new DataPalette[16];
private final int[] dataBitsIndexes = new int[16];
private final IBlockData[][] predefinedBlockData = new IBlockData[16][];
public PacketPlayOutMapChunkInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
this.packetPlayOutMapChunk = packetPlayOutMapChunk;
this.chunk = chunk;
this.chunkSectionSelector = chunkSectionSelector;
}
public PacketPlayOutMapChunk getPacketPlayOutMapChunk() {
return packetPlayOutMapChunk;
}
public Chunk getChunk() {
return chunk;
}
public int getChunkSectionSelector() {
return chunkSectionSelector;
}
public byte[] getData() {
return data.array(); // Akarin
}
public void setData(ByteBuf data) { // Akarin - byte[] -> ByteBuf
this.data = data;
}
public int getBitsPerValue(int chunkSectionIndex) {
return bitsPerValue[chunkSectionIndex];
}
public void setBitsPerValue(int chunkSectionIndex, int bitsPerValue) {
this.bitsPerValue[chunkSectionIndex] = bitsPerValue;
}
public DataPalette getDataPalette(int chunkSectionIndex) {
return dataPalettes[chunkSectionIndex];
}
public void setDataPalette(int chunkSectionIndex, DataPalette dataPalette) {
dataPalettes[chunkSectionIndex] = dataPalette;
}
public int getDataBitsIndex(int chunkSectionIndex) {
return dataBitsIndexes[chunkSectionIndex];
}
public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) {
dataBitsIndexes[chunkSectionIndex] = dataBitsIndex;
}
public IBlockData[] getPredefinedBlockData(int chunkSectionIndex) {
return predefinedBlockData[chunkSectionIndex];
}
public void setPredefinedBlockData(int chunkSectionIndex, IBlockData[] predefinedBlockData) {
this.predefinedBlockData[chunkSectionIndex] = predefinedBlockData;
}
public boolean isWritten(int chunkSectionIndex) {
return bitsPerValue[chunkSectionIndex] != 0;
}
}

View File

@@ -3,12 +3,9 @@ package io.akarin.api.internal;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Queue;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -17,15 +14,11 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
import co.aikar.timings.Timing;
import co.aikar.timings.Timings;
import io.akarin.api.internal.Akari.AssignableFactory;
import io.akarin.api.internal.Akari.TimingSignal;
import io.akarin.api.internal.utils.ReentrantSpinningLock;
import io.akarin.api.internal.utils.thread.SuspendableExecutorCompletionService;
import io.akarin.api.internal.utils.thread.SuspendableThreadPoolExecutor;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.World;
import net.minecraft.server.WorldServer;
@SuppressWarnings("restriction")
public abstract class Akari {
@@ -70,17 +63,7 @@ public abstract class Akari {
}
}
public static class TimingSignal {
public final World tickedWorld;
public final boolean isEntities;
public TimingSignal(World world, boolean entities) {
tickedWorld = world;
isEntities = entities;
}
}
public static SuspendableExecutorCompletionService<TimingSignal> STAGE_TICK;
public static SuspendableExecutorCompletionService<?> STAGE_TICK;
static {
resizeTickExecutors(3);
@@ -141,10 +124,15 @@ public abstract class Akari {
return serverVersion + " (MC: " + MinecraftServer.getServer().getVersion() + ")";
}
public static final ReentrantSpinningLock eventLock = new ReentrantSpinningLock();
public static final ReentrantSpinningLock timingsLock = new ReentrantSpinningLock();
/*
* Timings
*/
public final static Timing worldTiming = getTiming("Akarin - Full World Tick");
public final static Timing eventSuspendTiming = getTiming("Akarin - Event Suspend");
public final static Timing eventResumeTiming = getTiming("Akarin - Event Resume");
public final static Timing callbackTiming = getTiming("Akarin - Callback Queue");

View File

@@ -1,30 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.api.internal.mixin;
public interface IMixinRealTimeTicking {
long getRealTimeTicks();
}

View File

@@ -3,6 +3,7 @@ package io.akarin.api.internal.mixin;
import java.util.Random;
public interface IMixinWorldServer {
public Object lock();
public Object tickLock();
public Random rand();
public Object trackLock();
}

View File

@@ -8,7 +8,6 @@ public class ReentrantSpinningLock {
* Impl Note:
* A write lock can reentrant as a read lock, while a
* read lock is not allowed to reentrant as a write lock.
* READ LOCK IS UNTESTED, USE WITH CATION.
*/
private final AtomicBoolean writeLocked = new AtomicBoolean(false);
@@ -17,7 +16,7 @@ public class ReentrantSpinningLock {
private int reentrantLocks = 0;
/**
* Lock as a typical reentrant write lock
* Lock as a typical write lock
*/
public void lock() {
long currentThreadId = Thread.currentThread().getId();
@@ -32,9 +31,9 @@ public class ReentrantSpinningLock {
public void unlock() {
if (reentrantLocks == 0) {
heldThreadId = 0;
//if (readerThreads.get() == 0 || readerThreads.getAndDecrement() == 1) { // Micro-optimization: this saves one subtract
if (readerThreads.get() == 0 || readerThreads.getAndDecrement() == 1) { // Micro-optimization: this saves one subtract
writeLocked.set(false);
//}
}
} else {
--reentrantLocks;
}
@@ -43,9 +42,8 @@ public class ReentrantSpinningLock {
private final AtomicInteger readerThreads = new AtomicInteger(0);
/**
* Lock as a typical reentrant read lock
* Lock as a typical read lock
*/
@Deprecated
public void lockWeak() {
long currentThreadId = Thread.currentThread().getId();
if (heldThreadId == currentThreadId) {
@@ -59,7 +57,6 @@ public class ReentrantSpinningLock {
}
}
@Deprecated
public void unlockWeak() {
if (reentrantLocks == 0) {
heldThreadId = 0;
@@ -82,7 +79,6 @@ public class ReentrantSpinningLock {
}
}
@Deprecated
public class SpinningReadLock {
public void lock() {
lockWeak();
@@ -96,7 +92,7 @@ public class ReentrantSpinningLock {
return wrappedWriteLock;
}
public SpinningReadLock readLock() {
public SpinningReadLock readLocked() {
return wrappedReadLock;
}
}

View File

@@ -25,7 +25,7 @@ public class AkarinGlobalConfig {
+ "Some options may impact gameplay, so use with caution,\n"
+ "and make sure you know what each option does before configuring.\n"
+ "\n"
+ "Akarin forums: https://akarin.io/ \n";
+ "Akarin website: https://akarin.io/ \n";
/*========================================================================*/
public static YamlConfiguration config;
static int version;
@@ -190,29 +190,6 @@ public class AkarinGlobalConfig {
noResponseDoGC = getBoolean("alternative.gc-before-stuck-restart", true);
}
public static String messageKick;
public static String messageBan;
public static String messageBanReason;
public static String messageBanExpires;
public static String messageBanIp;
public static String messageDupLogin;
public static String messageJoin;
public static String messageJoinRenamed;
public static String messageKickKeepAlive;
public static String messagePlayerQuit;
private static void messagekickKeepAlive() {
messageKick = getString("messages.disconnect.kick-player", "Kicked by an operator.");
messageBan = getString("messages.disconnect.ban-player-name", "You are banned from this server! %s %s");
messageBanReason = getString("messages.disconnect.ban-reason", "\nReason: ");
messageBanExpires = getString("messages.disconnect.ban-expires", "\nYour ban will be removed on ");
messageBanIp = getString("messages.disconnect.ban-player-ip", "Your IP address is banned from this server! %s %s");
messageDupLogin = getString("messages.disconnect.kick-player-duplicate-login", "You logged in from another location");
messageJoin = getString("messages.connect.player-join-server", "§e%s joined the game");
messageJoinRenamed = getString("messages.connect.renamed-player-join-server", "§e%s (formerly known as %s) joined the game");
messageKickKeepAlive = getString("messages.disconnect.kick-player-timeout-keep-alive", "Timed out");
messagePlayerQuit = getString("messages.disconnect.player-quit-server", "§e%s left the game");
}
public static String serverBrandName;
private static void serverBrandName() {
serverBrandName = getString("alternative.modified-server-brand-name", "");

View File

@@ -40,7 +40,6 @@ public class AkarinSlackScheduler extends Thread {
MinecraftServer server = MinecraftServer.getServer();
while (server.isRunning()) {
long startProcessTiming = System.currentTimeMillis();
// Send time updates to everyone, it will get the right time from the world the player is in.
// Time update, from MinecraftServer#D
if (++updateTime >= AkarinGlobalConfig.timeUpdateInterval) {
@@ -78,7 +77,7 @@ public class AkarinSlackScheduler extends Thread {
// Force hardcore difficulty, from WorldServer#doTick
if (AkarinGlobalConfig.forceHardcoreDifficulty)
for (WorldServer world : server.worlds) {
for (WorldServer world : server.getWorlds()) {
if (world.getWorldData().isHardcore() && world.getDifficulty() != EnumDifficulty.HARD) {
world.getWorldData().setDifficulty(EnumDifficulty.HARD);
}
@@ -98,9 +97,10 @@ public class AkarinSlackScheduler extends Thread {
}
try {
Thread.sleep(100 - (System.currentTimeMillis() - startProcessTiming));
} catch (InterruptedException interrupted) {
;
Thread.sleep(100);
} catch (InterruptedException ex) {
Akari.logger.warn("Slack scheduler thread was interrupted unexpectly!");
ex.printStackTrace();
}
}
}

View File

@@ -41,6 +41,7 @@ public abstract class Bootstrap {
Akari.logger.warn("Visit our website for latest information https://akarin.io/");
}
/*
@Redirect(method = "main", at = @At(
value = "INVOKE_STRING",
target = "Ljava/io/PrintStream;println(Ljava/lang/String;)V",
@@ -49,4 +50,5 @@ public abstract class Bootstrap {
private static void notifyLoading(PrintStream stream, String text) {
Akari.logger.info("Loading libraries as parallel capable..");
}
*/
}

View File

@@ -1,139 +0,0 @@
package io.akarin.server.mixin.bootstrap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import io.akarin.api.internal.Akari;
import net.minecraft.server.BiomeBase;
import net.minecraft.server.Block;
import net.minecraft.server.BlockFire;
import net.minecraft.server.DispenserRegistry;
import net.minecraft.server.Enchantment;
import net.minecraft.server.EntityTypes;
import net.minecraft.server.Item;
import net.minecraft.server.MobEffectList;
import net.minecraft.server.PotionBrewer;
import net.minecraft.server.PotionRegistry;
import net.minecraft.server.SoundEffect;
@Mixin(value = DispenserRegistry.class, remap = false)
public abstract class ParallelRegistry {
/**
* Registry order: SoundEffect -> Block
*/
private static final ExecutorService STAGE_BLOCK = Executors.newSingleThreadExecutor(Akari.STAGE_FACTORY);
/**
* Registry order: Item -> PotionBrewer & orderless: BlockFire, BiomeBase (After STAGE_BLOCK)
*/
private static final ExecutorService STAGE_BLOCK_BASE = Executors.newFixedThreadPool(3, Akari.STAGE_FACTORY);
/**
* Registry order: MobEffectList -> PotionRegistry & orderless: Enchantment, EntityTypes
*/
private static final ExecutorService STAGE_STANDALONE = Executors.newFixedThreadPool(3, Akari.STAGE_FACTORY);
// We should keep the original order in codes thought orderless in runtime
@Redirect(method = "c()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/SoundEffect.b()V"
))
private static void soundEffect() {
STAGE_BLOCK.execute(() -> {
SoundEffect.b();
Block.w();
STAGE_BLOCK_BASE.execute(() -> BlockFire.e()); // This single task only cost ~4ms, however, firing a task only takes ~1ms
STAGE_BLOCK_BASE.execute(() -> {
Item.t();
PotionBrewer.a();
});
STAGE_BLOCK_BASE.execute(() -> BiomeBase.q());
});
}
@Redirect(method = "c()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/Block.w()V"
))
private static void block() {} // STAGE_BLOCK
@Redirect(method = "c()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/BlockFire.e()V"
))
private static void blockFire() {} // STAGE_BLOCK_BASE
@Redirect(method = "c()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/MobEffectList.k()V"
))
private static void mobEffectList() {} // STAGE_STANDALONE
@Redirect(method = "c()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/Enchantment.g()V"
))
private static void enchantment() {
STAGE_STANDALONE.execute(() -> Enchantment.g());
STAGE_STANDALONE.execute(() -> EntityTypes.c());
STAGE_STANDALONE.execute(() -> {
MobEffectList.k();
PotionRegistry.b();
});
}
@Redirect(method = "c()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/Item.t()V"
))
private static void item() {} // STAGE_BLOCK_BASE
@Redirect(method = "c()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/PotionRegistry.b()V"
))
private static void potionRegistry() {} // STAGE_STANDALONE
@Redirect(method = "c()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/PotionBrewer.a()V"
))
private static void potionBrewer() {} // STAGE_BLOCK_BASE
@Redirect(method = "c()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/EntityTypes.c()V"
))
private static void entityTypes() {} // STAGE_STANDALONE
@Redirect(method = "c()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/BiomeBase.q()V"
))
private static void biomeBase() {} // STAGE_BLOCK_BASE
@Inject(method = "c()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/DispenserRegistry.b()V",
shift = At.Shift.BEFORE
))
private static void await(CallbackInfo info) throws InterruptedException {
// Shutdown BLOCK and STANDALONE stage
STAGE_STANDALONE.shutdown();
STAGE_BLOCK.shutdown();
STAGE_BLOCK.awaitTermination(10, TimeUnit.MINUTES);
STAGE_BLOCK_BASE.shutdown(); // This must after STAGE_BLOCK terminated
STAGE_BLOCK_BASE.awaitTermination(20, TimeUnit.MINUTES);
STAGE_STANDALONE.awaitTermination(30, TimeUnit.MINUTES); // Behind the shutdown of BLOCK_BASE should faster
}
}

View File

@@ -14,9 +14,7 @@ public abstract class MixinAsyncCatcher {
@Overwrite
public static void catchOp(String reason) {
if (enabled) {
if (Akari.isPrimaryThread()) return;
if (enabled && !Akari.isPrimaryThread()) {
if (AkarinGlobalConfig.throwOnAsyncCaught) {
throw new IllegalStateException("Asynchronous " + reason + "!");
} else {

View File

@@ -1,61 +0,0 @@
package io.akarin.server.mixin.core;
import java.util.Date;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import com.mojang.authlib.GameProfile;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.CommandAbstract;
import net.minecraft.server.CommandBan;
import net.minecraft.server.CommandException;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.ExceptionUsage;
import net.minecraft.server.GameProfileBanEntry;
import net.minecraft.server.ICommand;
import net.minecraft.server.ICommandListener;
import net.minecraft.server.MinecraftServer;
@Mixin(value = CommandBan.class, remap = false)
public abstract class MixinCommandBan {
@Overwrite
public void execute(MinecraftServer server, ICommandListener sender, String[] args) throws CommandException {
if (args.length >= 1 && args[0].length() > 1) {
GameProfile profile = server.getUserCache().getProfile(args[0]);
if (profile == null) {
throw new CommandException("commands.ban.failed", new Object[] {args[0]});
} else {
// Akarin start - use string
boolean hasReason = true; // Akarin
String message = null;
if (args.length >= 2) {
message = "";
for (int i = 2; i < args.length; i++) {
message = message + args[i];
}
} else {
hasReason = false; // Akarin
message = Akari.EMPTY_STRING; // Akarin - modify message
}
// Akarin end
GameProfileBanEntry entry = new GameProfileBanEntry(profile, (Date) null, sender.getName(), (Date) null, message);
server.getPlayerList().getProfileBans().add(entry);
EntityPlayer entityplayer = server.getPlayerList().getPlayer(args[0]);
if (entityplayer != null) {
entityplayer.playerConnection.disconnect(hasReason ? message : AkarinGlobalConfig.messageBan);
}
CommandAbstract.a(sender, (ICommand) this, "commands.ban.success", args[0]); // OBFHELPER: notifyCommandListener
}
} else {
throw new ExceptionUsage("commands.ban.usage");
}
}
}

View File

@@ -1,49 +0,0 @@
package io.akarin.server.mixin.core;
import java.util.Date;
import java.util.List;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.CommandAbstract;
import net.minecraft.server.CommandBanIp;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.ICommand;
import net.minecraft.server.ICommandListener;
import net.minecraft.server.IpBanEntry;
import net.minecraft.server.MinecraftServer;
@Mixin(value = CommandBanIp.class, remap = false)
public abstract class MixinCommandBanIp {
@Overwrite // OBFHELPER: banIp
protected void a(MinecraftServer server, ICommandListener sender, String args, @Nullable String banReason) {
// Akarin start - modify message
boolean hasReason = true;
if (banReason == null) {
banReason = Akari.EMPTY_STRING;
hasReason = false;
}
// Akarin end
IpBanEntry ipbanentry = new IpBanEntry(args, (Date) null, sender.getName(), (Date) null, banReason);
server.getPlayerList().getIPBans().add(ipbanentry);
List<EntityPlayer> withIpPlayers = server.getPlayerList().b(args); // OBFHELPER: getPlayersMatchingAddress
String[] banPlayerNames = new String[withIpPlayers.size()];
for (int i = 0; i < banPlayerNames.length; i++) {
EntityPlayer each = withIpPlayers.get(i);
banPlayerNames[i] = each.getName();
each.playerConnection.disconnect(hasReason ? banReason : AkarinGlobalConfig.messageBanIp); // Akarin
}
if (withIpPlayers.isEmpty()) {
CommandAbstract.a(sender, (ICommand) this, "commands.banip.success", args); // OBFHELPER: notifyCommandListener
} else {
CommandAbstract.a(sender, (ICommand) this, "commands.banip.success.players", args, CommandAbstract.a(banPlayerNames)); // OBFHELPER: notifyCommandListener - joinNiceString
}
}
}

View File

@@ -1,44 +0,0 @@
package io.akarin.server.mixin.core;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.CommandAbstract;
import net.minecraft.server.CommandException;
import net.minecraft.server.CommandKick;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.ExceptionPlayerNotFound;
import net.minecraft.server.ExceptionUsage;
import net.minecraft.server.ICommand;
import net.minecraft.server.ICommandListener;
import net.minecraft.server.MinecraftServer;
@Mixin(value = CommandKick.class, remap = false)
public abstract class MixinCommandKick {
@Overwrite
public void execute(MinecraftServer server, ICommandListener sender, String[] args) throws CommandException {
if (args.length > 0 && args[0].length() > 1) {
EntityPlayer target = server.getPlayerList().getPlayer(args[0]);
if (target == null) {
throw new ExceptionPlayerNotFound("commands.generic.player.notFound", args[0]);
} else {
if (args.length >= 2) {
// Akarin start - use string
String message = "";
for (int i = 2; i < args.length; i++) {
message = message + args[i];
}
target.playerConnection.disconnect(message);
CommandAbstract.a(sender, (ICommand) this, "commands.kick.success.reason", target.getName(), message); // OBFHELPER: notifyCommandListener
// Akarin end
} else {
target.playerConnection.disconnect(AkarinGlobalConfig.messageKick); // Akarin
CommandAbstract.a(sender, (ICommand) this, "commands.kick.success", target.getName()); // OBFHELPER: notifyCommandListener
}
}
} else {
throw new ExceptionUsage("commands.kick.usage");
}
}
}

View File

@@ -20,7 +20,7 @@ public abstract class MixinFileIOThread {
private final Executor executor = Executors.newFixedThreadPool(AkarinGlobalConfig.fileIOThreads, new ThreadFactoryBuilder().setNameFormat("Akarin File IO Thread - %1$d").setPriority(1).build());
private final AtomicInteger queuedChunkCounter = new AtomicInteger(0);
@Shadow(aliases = "e") private volatile boolean isAwaitFinish;
@Shadow(aliases = "f") private volatile boolean isAwaitFinish;
@Overwrite // OBFHELPER: saveChunk
public void a(IAsyncChunkSaver iasyncchunksaver) {

View File

@@ -14,9 +14,21 @@ import net.minecraft.server.MinecraftServer;
@Mixin(value = MCUtil.class, remap = false)
public abstract class MixinMCUtil {
@Overwrite
public static void ensureMain(String reason, Runnable run) {
if (AsyncCatcher.enabled && !Akari.isPrimaryThread()) { // Akarin
if (reason != null) {
new IllegalStateException("Asynchronous " + reason + "!").printStackTrace();
}
MinecraftServer.getServer().processQueue.add(run);
return;
}
run.run();
}
@Overwrite
public static <T> T ensureMain(String reason, Supplier<T> run) {
if (AsyncCatcher.enabled && !Akari.isPrimaryThread()) {
if (AsyncCatcher.enabled && !Akari.isPrimaryThread()) { // Akarin
new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace();
Waitable<T> wait = new Waitable<T>() {
@Override
@@ -32,7 +44,6 @@ public abstract class MixinMCUtil {
}
return null;
}
return run.get();
}
}

View File

@@ -1,19 +1,16 @@
package io.akarin.server.mixin.core;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.function.BooleanSupplier;
import org.apache.commons.lang.WordUtils;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.world.WorldLoadEvent;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
@@ -25,17 +22,15 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import co.aikar.timings.MinecraftTimings;
import io.akarin.api.internal.Akari;
import io.akarin.api.internal.Akari.AssignableFactory;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import io.akarin.server.core.AkarinGlobalConfig;
import io.akarin.server.core.AkarinSlackScheduler;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.CrashReport;
import net.minecraft.server.CustomFunctionData;
import net.minecraft.server.DimensionManager;
import net.minecraft.server.ITickable;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.MojangStatisticsGenerator;
import net.minecraft.server.PlayerList;
import net.minecraft.server.ReportedException;
import net.minecraft.server.ServerConnection;
import net.minecraft.server.SystemUtils;
@@ -45,7 +40,6 @@ import net.minecraft.server.WorldServer;
@Mixin(value = MinecraftServer.class, remap = false)
public abstract class MixinMinecraftServer {
@Shadow @Final public Thread primaryThread;
private boolean tickedPrimaryEntities;
private int cachedWorldSize;
@Overwrite
@@ -55,17 +49,18 @@ public abstract class MixinMinecraftServer {
@Inject(method = "run()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/MinecraftServer.aw()J",
target = "net/minecraft/server/SystemUtils.b()J",
shift = At.Shift.BEFORE
))
private void prerun(CallbackInfo info) {
private void prerun(CallbackInfo info) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
primaryThread.setPriority(AkarinGlobalConfig.primaryThreadPriority < Thread.NORM_PRIORITY ? Thread.NORM_PRIORITY :
(AkarinGlobalConfig.primaryThreadPriority > Thread.MAX_PRIORITY ? 10 : AkarinGlobalConfig.primaryThreadPriority));
Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
Akari.resizeTickExecutors((cachedWorldSize = worldServer.size()));
for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i);
TileEntityHopper.skipHopperEvents = world.paperConfig.disableHopperMoveEvents || InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0;
Field skipHopperEvents = TileEntityHopper.class.getDeclaredField("skipHopperEvents"); // No idea why static but check each world
skipHopperEvents.setAccessible(true);
for (WorldServer world : worldServer.values()) {
skipHopperEvents.set(null, world.paperConfig.disableHopperMoveEvents || InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0);
}
AkarinSlackScheduler.get().boot();
}
@@ -81,76 +76,18 @@ public abstract class MixinMinecraftServer {
@Overwrite
public void a(MojangStatisticsGenerator generator) {}
@Overwrite
public void b(MojangStatisticsGenerator generator) {}
/*
* Parallel spawn chunks generation
*/
@Shadow public abstract boolean isRunning();
@Shadow(aliases = "a_") protected abstract void output(String s, int i);
@Shadow(aliases = "t") protected abstract void enablePluginsPostWorld();
private void prepareChunks(WorldServer world, int index) {
MinecraftServer.LOGGER.info("Preparing start region for level " + index + " (Seed: " + world.getSeed() + ")");
BlockPosition spawnPos = world.getSpawn();
long lastRecord = System.currentTimeMillis();
int preparedChunks = 0;
short radius = world.paperConfig.keepLoadedRange;
for (int skipX = -radius; skipX <= radius && isRunning(); skipX += 16) {
for (int skipZ = -radius; skipZ <= radius && isRunning(); skipZ += 16) {
long now = System.currentTimeMillis();
if (now - lastRecord > 1000L) {
output("Preparing spawn area (level " + index + ") ", preparedChunks * 100 / 625);
lastRecord = now;
}
preparedChunks++;
world.getChunkProviderServer().getChunkAt(spawnPos.getX() + skipX >> 4, spawnPos.getZ() + skipZ >> 4);
}
}
}
@Overwrite
protected void l() throws InterruptedException {
ExecutorCompletionService<?> executor = new ExecutorCompletionService<>(Executors.newFixedThreadPool(worlds.size(), new AssignableFactory("Akarin Parallel Terrain Generation Thread - $")));
for (int index = 0; index < worlds.size(); index++) {
WorldServer world = this.worlds.get(index);
if (!world.getWorld().getKeepSpawnInMemory()) continue;
int fIndex = index;
executor.submit(() -> prepareChunks(world, fIndex), null);
}
for (WorldServer world : this.worlds) {
if (world.getWorld().getKeepSpawnInMemory()) executor.take();
}
if (WorldLoadEvent.getHandlerList().getRegisteredListeners().length != 0) {
for (WorldServer world : this.worlds) {
this.server.getPluginManager().callEvent(new WorldLoadEvent(world.getWorld()));
}
}
enablePluginsPostWorld();
}
/*
* Parallel world ticking
*/
@Shadow public CraftServer server;
@Shadow @Mutable protected Queue<FutureTask<?>> j;
@Shadow @Mutable protected Queue<FutureTask<?>> f;
@Shadow public Queue<Runnable> processQueue;
@Shadow private int ticks;
@Shadow public List<WorldServer> worlds;
@Shadow(aliases = "v") private PlayerList playerList;
@Shadow(aliases = "o") @Final private List<ITickable> tickables;
@Shadow @Final public Map<DimensionManager, WorldServer> worldServer;
@Shadow(aliases = "k") @Final private List<ITickable> tickables;
@Shadow public abstract PlayerList getPlayerList();
@Shadow(aliases = "an") public abstract ServerConnection serverConnection();
@Shadow(aliases = "aL") public abstract CustomFunctionData functionManager();
@Shadow public abstract CustomFunctionData getFunctionData();
@Shadow public abstract ServerConnection getServerConnection();
private boolean tickEntities(WorldServer world) {
try {
@@ -172,10 +109,10 @@ public abstract class MixinMinecraftServer {
return true;
}
private void tickWorld(WorldServer world) {
private void tickWorld(WorldServer world, BooleanSupplier supplier) {
try {
world.timings.doTick.startTiming();
world.doTick();
world.doTick(supplier);
world.timings.doTick.stopTiming();
} catch (Throwable throwable) {
CrashReport crashreport;
@@ -190,24 +127,24 @@ public abstract class MixinMinecraftServer {
}
@Overwrite
public void D() throws InterruptedException, ExecutionException, CancellationException {
public void b(BooleanSupplier supplier) throws InterruptedException, ExecutionException {
Runnable runnable;
Akari.callbackTiming.startTiming();
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
Akari.callbackTiming.stopTiming();
MinecraftTimings.bukkitSchedulerTimer.startTiming();
this.server.getScheduler().mainThreadHeartbeat(this.ticks);
MinecraftTimings.bukkitSchedulerTimer.stopTiming();
MinecraftTimings.minecraftSchedulerTimer.startTiming();
FutureTask<?> task;
int count = j.size();
while (count-- > 0 && (task = j.poll()) != null) {
int count = f.size();
while (count-- > 0 && (task = f.poll()) != null) {
SystemUtils.a(task, MinecraftServer.LOGGER);
}
MinecraftTimings.minecraftSchedulerTimer.stopTiming();
MinecraftTimings.commandFunctionsTimer.startTiming();
getFunctionData().Y_();
MinecraftTimings.commandFunctionsTimer.stopTiming();
MinecraftTimings.processQueueTimer.startTiming();
while ((runnable = processQueue.poll()) != null) runnable.run();
MinecraftTimings.processQueueTimer.stopTiming();
@@ -216,41 +153,44 @@ public abstract class MixinMinecraftServer {
ChunkIOExecutor.tick();
MinecraftTimings.chunkIOTickTimer.stopTiming();
if (cachedWorldSize != worlds.size()) Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
if (cachedWorldSize != worldServer.size()) Akari.resizeTickExecutors((cachedWorldSize = worldServer.size()));
switch (AkarinGlobalConfig.parallelMode) {
case 1:
case 2:
default:
// Never tick one world concurrently!
for (int i = 0; i < cachedWorldSize; i++) {
// Impl Note:
// Entities ticking: index 1 -> ... -> 0 (parallel)
// World ticking: index 0 -> ... (parallel)
int interlace = i + 1;
WorldServer entityWorld = worlds.get(interlace < cachedWorldSize ? interlace : 0);
WorldServer interlacedWorld = null;
for (WorldServer world : worldServer.values()) {
if (interlacedWorld == null) {
interlacedWorld = world;
} else {
Akari.STAGE_TICK.submit(() -> {
synchronized (((IMixinWorldServer) entityWorld).lock()) {
tickEntities(entityWorld);
synchronized (((IMixinWorldServer) world).tickLock()) {
tickEntities(world);
}
}, null);
}
}, null/*new TimingSignal(entityWorld, true)*/);
if (AkarinGlobalConfig.parallelMode != 1) {
int fi = i;
if (AkarinGlobalConfig.parallelMode != 1 /* >= 2 */) {
Akari.STAGE_TICK.submit(() -> {
WorldServer world = worlds.get(fi);
synchronized (((IMixinWorldServer) world).lock()) {
tickWorld(world);
synchronized (((IMixinWorldServer) world).tickLock()) {
tickWorld(world, supplier);
}
}, null);
}
}
WorldServer fInterlacedWorld = interlacedWorld;
Akari.STAGE_TICK.submit(() -> {
synchronized (((IMixinWorldServer) fInterlacedWorld).tickLock()) {
tickEntities(fInterlacedWorld);
}
}, null);
if (AkarinGlobalConfig.parallelMode == 1)
Akari.STAGE_TICK.submit(() -> {
for (int i = 0; i < cachedWorldSize; i++) {
WorldServer world = worlds.get(i);
synchronized (((IMixinWorldServer) world).lock()) {
tickWorld(world);
for (WorldServer world : worldServer.values()) {
synchronized (((IMixinWorldServer) world).tickLock()) {
tickWorld(world, supplier);
}
}
}, null);
@@ -259,29 +199,28 @@ public abstract class MixinMinecraftServer {
Akari.STAGE_TICK.take();
}
/* for (int i = (AkarinGlobalConfig.parallelMode == 1 ? cachedWorldSize : cachedWorldSize * 2); i --> 0 ;) {
long startTiming = System.nanoTime();
TimingSignal signal = Akari.STAGE_TICK.take().get();
IMixinTimingHandler timing = (IMixinTimingHandler) (signal.isEntities ? signal.tickedWorld.timings.tickEntities : signal.tickedWorld.timings.doTick);
timing.stopTiming(startTiming); // The overlap will be ignored
} */
break;
case 0:
Akari.STAGE_TICK.submit(() -> {
for (int i = 1; i <= cachedWorldSize; ++i) {
WorldServer world = worlds.get(i < cachedWorldSize ? i : 0);
synchronized (((IMixinWorldServer) world).lock()) {
WorldServer interlacedWorld_ = null;
for (WorldServer world : worldServer.values()) {
if (interlacedWorld_ == null) {
interlacedWorld_ = world;
continue;
}
synchronized (((IMixinWorldServer) world).tickLock()) {
tickEntities(world);
}
}
synchronized (((IMixinWorldServer) interlacedWorld_).tickLock()) {
tickEntities(interlacedWorld_);
}
}, null);
Akari.STAGE_TICK.submit(() -> {
for (int i = 0; i < cachedWorldSize; ++i) {
WorldServer world = worlds.get(i);
synchronized (((IMixinWorldServer) world).lock()) {
tickWorld(world);
for (WorldServer world : worldServer.values()) {
synchronized (((IMixinWorldServer) world).tickLock()) {
tickWorld(world, supplier);
}
}
}, null);
@@ -290,9 +229,8 @@ public abstract class MixinMinecraftServer {
Akari.STAGE_TICK.take();
break;
case -1:
for (int i = 0; i < cachedWorldSize; ++i) {
WorldServer world = worlds.get(i);
tickWorld(world);
for (WorldServer world : worldServer.values()) {
tickWorld(world, supplier);
tickEntities(world);
}
break;
@@ -303,20 +241,16 @@ public abstract class MixinMinecraftServer {
Akari.callbackTiming.stopTiming();
MinecraftTimings.connectionTimer.startTiming();
serverConnection().c();
getServerConnection().c();
MinecraftTimings.connectionTimer.stopTiming();
Akari.callbackTiming.startTiming();
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
Akari.callbackTiming.stopTiming();
MinecraftTimings.commandFunctionsTimer.startTiming();
functionManager().e();
MinecraftTimings.commandFunctionsTimer.stopTiming();
MinecraftTimings.tickablesTimer.startTiming();
for (int i = 0; i < this.tickables.size(); ++i) {
tickables.get(i).e();
tickables.get(i).Y_();
}
MinecraftTimings.tickablesTimer.stopTiming();
}

View File

@@ -14,7 +14,7 @@ import net.minecraft.server.PlayerConnectionUtils;
@Mixin(value = PlayerConnectionUtils.class, remap = false)
public abstract class MixinPlayerConnectionUtils {
@Overwrite
public static <T extends PacketListener> void ensureMainThread(final Packet<T> packet, final T listener, IAsyncTaskHandler iasynctaskhandler) throws CancelledPacketHandleException {
public static <T extends PacketListener> void ensureMainThread(Packet<T> packet, T listener, IAsyncTaskHandler iasynctaskhandler) throws CancelledPacketHandleException {
if (!iasynctaskhandler.isMainThread()) {
Timing timing = MinecraftTimings.getPacketTiming(packet);
// MinecraftServer#postToMainThread inlined thread check, no twice

View File

@@ -2,62 +2,31 @@ package io.akarin.server.mixin.core;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import co.aikar.timings.Timing;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MinecraftServer;
@Mixin(targets = "co.aikar.timings.TimingHandler", remap = false)
public abstract class MixinTimingHandler {
@Shadow @Final String name;
@Shadow private boolean enabled;
@Shadow private long start;
@Shadow private int timingDepth;
@Shadow private AtomicLong start;
@Shadow private AtomicInteger timingDepth;
@Shadow abstract void addDiff(long diff);
@Shadow public abstract Timing startTiming();
@Overwrite
public Timing startTimingIfSync() {
startTiming();
return (Timing) this;
}
@Overwrite
public void stopTimingIfSync() {
if (Akari.isPrimaryThread(false)) {
stopTiming(true); // Avoid twice thread check
}
}
@Overwrite
public void stopTiming() {
stopTiming(false);
}
public void stopTiming(long start) {
if (enabled) addDiff(System.nanoTime() - start);
}
public void stopTiming(boolean alreadySync) {
if (!enabled || --timingDepth != 0 || start == 0) return;
if (!alreadySync) {
Thread curThread = Thread.currentThread();
if (curThread != MinecraftServer.getServer().primaryThread) {
start = 0;
return;
}
}
// Safety ensured
addDiff(System.nanoTime() - start);
start = 0;
if (!enabled || start.get() == 0 || timingDepth.decrementAndGet() != 0) return;
long prev = start.getAndSet(0); // Akarin
addDiff(System.nanoTime() - prev); // Akarin
}
}

View File

@@ -1,58 +1,21 @@
package io.akarin.server.mixin.core;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.defaults.VersionCommand;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import com.google.common.base.Charsets;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MCUtil;
@Mixin(value = VersionCommand.class, remap = false)
public abstract class MixinVersionCommand {
@Overwrite
private static int getFromRepo(String repo, String hash) {
try {
HttpURLConnection connection = (HttpURLConnection) new URL("https://api.github.com/repos/" + repo + "/compare/ver/1.12.2..." + hash).openConnection();
connection.connect();
if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) return -2; // Unknown commit
try (
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charsets.UTF_8))
) {
JSONObject obj = (JSONObject) new JSONParser().parse(reader);
String status = (String) obj.get("status");
switch (status) {
case "identical":
return 0;
case "behind":
return ((Number) obj.get("behind_by")).intValue();
default:
return -1;
}
} catch (ParseException | NumberFormatException e) {
e.printStackTrace();
return -1;
}
} catch (IOException e) {
e.printStackTrace();
return -1;
}
}
@Shadow private static int getFromRepo(String repo, String branch, String hash) { return 0; }
/**
* Match current version with repository and calculate the distance
@@ -63,7 +26,7 @@ public abstract class MixinVersionCommand {
@Overwrite
private static int getDistance(String repo, String verInfo) {
verInfo = verInfo.replace("\"", "");
return getFromRepo("Akarin-project/Akarin", verInfo);
return getFromRepo("Akarin-project/Akarin", "ver/1.13", verInfo);
}
/**
@@ -74,7 +37,7 @@ public abstract class MixinVersionCommand {
@Overwrite
private static int getFromJenkins(int currentVer) {
String[] parts = Bukkit.getVersion().substring("git-Akarin-".length()).split("[-\\s]");
return getFromRepo("Akarin-project/Akarin", parts[0]);
return getFromRepo("Akarin-project/Akarin","ver/1.13", parts[0]);
}
@Shadow private boolean hasVersion;
@@ -119,7 +82,7 @@ public abstract class MixinVersionCommand {
}
}
if (!hasVersion) {
obtainVersion(sender);
obtainVersionAsync(sender);
if (AkarinGlobalConfig.legacyVersioningCompat) currentSender = sender;
}
}
@@ -127,7 +90,7 @@ public abstract class MixinVersionCommand {
@Overwrite
private void obtainVersion() {
if (AkarinGlobalConfig.legacyVersioningCompat) {
obtainVersion(currentSender);
obtainVersionAsync(currentSender);
currentSender = null; // try release
} else {
Akari.logger.warn("A legacy version lookup was caught, legacy-versioning-compat enabled forcely!");
@@ -136,7 +99,7 @@ public abstract class MixinVersionCommand {
}
}
private void obtainVersion(CommandSender sender) {
private void obtainVersionAsync(CommandSender sender) {
// We post all things because a custom version is rare (expiring is not rare),
// and we'd better post this task as early as we can, since it's a will (horrible destiny).
MCUtil.scheduleAsyncTask(() -> {

View File

@@ -1,36 +0,0 @@
package io.akarin.server.mixin.core;
import java.util.List;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.server.AxisAlignedBB;
import net.minecraft.server.Entity;
import net.minecraft.server.World;
/**
* Fixes MC-103516(https://bugs.mojang.com/browse/MC-103516)
*/
@Mixin(value = World.class, remap = false)
public abstract class MixinWorld {
@Shadow public abstract List<Entity> getEntities(@Nullable Entity entity, AxisAlignedBB box);
/**
* Returns true if there are no solid, live entities in the specified AxisAlignedBB, excluding the given entity
*/
@Overwrite
public boolean a(AxisAlignedBB box, @Nullable Entity target) { // OBFHELPER: checkNoEntityCollision
List<Entity> list = this.getEntities(null, box);
for (Entity each : list) {
if (!each.dead && each.i && each != target && (target == null || !each.x(target))) { // OBFHELPER: preventEntitySpawning - isRidingSameEntity
return false;
}
}
return true;
}
}

View File

@@ -5,6 +5,7 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import net.minecraft.server.Entity;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.WorldManager;
@@ -16,9 +17,9 @@ public abstract class MixinWorldManager {
@Overwrite
public void a(Entity entity) {
this.world.getTracker().entriesLock.writeLock().lock(); // Akarin
synchronized (((IMixinWorldServer) this.world).trackLock()) { // Akarin
this.world.getTracker().track(entity);
this.world.getTracker().entriesLock.writeLock().unlock(); // Akarin
}
if (entity instanceof EntityPlayer) {
this.world.worldProvider.a((EntityPlayer) entity);

View File

@@ -1,9 +1,9 @@
package io.akarin.server.mixin.core;
import java.util.Random;
import org.apache.logging.log4j.LogManager;
import org.spongepowered.asm.mixin.Mixin;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import net.minecraft.server.WorldServer;
@@ -12,7 +12,7 @@ public abstract class MixinWorldServer implements IMixinWorldServer {
private final Object tickLock = new Object();
@Override
public Object lock() {
public Object tickLock() {
return tickLock;
}
@@ -34,4 +34,11 @@ public abstract class MixinWorldServer implements IMixinWorldServer {
public Random rand() {
return sharedRandom;
}
public final Object trackLock = new Object();
@Override
public Object trackLock() {
return trackLock;
}
}

View File

@@ -1,84 +0,0 @@
package io.akarin.server.mixin.cps;
import org.spigotmc.SlackActivityAccountant;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import net.minecraft.server.Chunk;
import net.minecraft.server.ChunkProviderServer;
import net.minecraft.server.IChunkLoader;
import net.minecraft.server.WorldServer;
@Mixin(value = ChunkProviderServer.class, remap = false)
public abstract class MixinChunkProviderServer {
@Shadow @Final public WorldServer world;
@Shadow public Long2ObjectOpenHashMap<Chunk> chunks;
public void unload(Chunk chunk) {
if (this.world.worldProvider.c(chunk.locX, chunk.locZ)) {
// Akarin - avoid using the queue and simply check the unloaded flag during unloads
// this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)));
chunk.setShouldUnload(true);
}
}
@Shadow public abstract boolean unloadChunk(Chunk chunk, boolean save);
@Shadow @Final private IChunkLoader chunkLoader;
@Shadow @Final private static double UNLOAD_QUEUE_RESIZE_FACTOR;
@Overwrite
public boolean unloadChunks() {
if (!this.world.savingDisabled) {
long now = System.currentTimeMillis();
long unloadAfter = world.paperConfig.delayChunkUnloadsBy;
SlackActivityAccountant activityAccountant = world.getMinecraftServer().slackActivityAccountant;
activityAccountant.startActivity(0.5);
ObjectIterator<Entry<Chunk>> it = chunks.long2ObjectEntrySet().fastIterator();
int remainingChunks = chunks.size();
int targetSize = Math.min(remainingChunks - 100, (int) (remainingChunks * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
while (it.hasNext()) {
Entry<Chunk> entry = it.next();
Chunk chunk = entry.getValue();
if (chunk != null && chunk.isUnloading()) {
if (chunk.scheduledForUnload != null) {
if (now - chunk.scheduledForUnload <= unloadAfter) continue;
}
if (unloadChunk(chunk, true)) {
it.remove();
}
chunk.setShouldUnload(false);
chunk.scheduledForUnload = null;
if (--remainingChunks <= targetSize && activityAccountant.activityTimeIsExhausted()) break;
}
}
activityAccountant.endActivity();
this.chunkLoader.b(); // OBFHELPER: chunkTick
}
return false;
}
@Redirect(method = "unloadChunk", at = @At(
value = "INVOKE",
target = "it/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap.remove(J)Ljava/lang/Object;"
))
private Object remove(Long2ObjectOpenHashMap<Chunk> chunks, long chunkHash) {
return null;
}
@Overwrite
public String getName() {
return "ServerChunkCache: " + chunks.size();
}
}

View File

@@ -1,39 +0,0 @@
package io.akarin.server.mixin.cps;
import java.util.Set;
import org.bukkit.craftbukkit.CraftWorld;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import net.minecraft.server.Chunk;
import net.minecraft.server.WorldServer;
@Mixin(value = CraftWorld.class, remap = false)
public abstract class MixinCraftWorld {
@Shadow @Final private WorldServer world;
@Redirect(method = "processChunkGC()V", at = @At(
value = "INVOKE",
target = "java/util/Set.contains(Ljava/lang/Object;)Z",
opcode = Opcodes.INVOKEINTERFACE
))
public boolean checkUnloading(Set<Long> set, Object chunkHash) {
return false;
}
@Redirect(method = "regenerateChunk", at = @At(
value = "INVOKE",
target = "java/util/Set.remove(Ljava/lang/Object;)Z",
opcode = Opcodes.INVOKEINTERFACE
))
public boolean regenChunk(Set<Long> set, Object chunkHash) {
Chunk chunk = world.getChunkProviderServer().chunks.get(chunkHash);
if (chunk != null) chunk.setShouldUnload(false);
return true;
}
}

View File

@@ -32,8 +32,6 @@ import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.minecraft.server.ChatComponentText;
import net.minecraft.server.EnumProtocolDirection;
import net.minecraft.server.HandshakeListener;
@@ -54,16 +52,16 @@ public abstract class NonblockingServerConnection {
/**
* Contains all endpoints added to this NetworkSystem
*/
@Shadow(aliases = "g") @Mutable @Final private List<ChannelFuture> endPoints;
@Shadow(aliases = "f") @Mutable @Final private List<ChannelFuture> endPoints;
/**
* A list containing all NetworkManager instances of all endpoints
*/
@Shadow(aliases = "h") @Mutable @Final private List<NetworkManager> networkManagers;
@Shadow(aliases = "g") @Mutable @Final private List<NetworkManager> networkManagers;
@Overwrite
private void addPending() {} // just keep compatibility
@Shadow(aliases = "f") @Final private MinecraftServer server;
@Shadow(aliases = "e") @Final private MinecraftServer server;
/**
* Adds channels (endpoint) that listens on publicly accessible network ports
@@ -77,13 +75,13 @@ public abstract class NonblockingServerConnection {
Class<? extends ServerChannel> channelClass;
EventLoopGroup loopGroup;
if (Epoll.isAvailable() && this.server.af()) { // OBFHELPER: MinecraftServer::useNativeTransport
if (Epoll.isAvailable() && this.server.V()) { // OBFHELPER: MinecraftServer::useNativeTransport
channelClass = EpollServerSocketChannel.class;
loopGroup = ServerConnection.b.c();
loopGroup = ServerConnection.b.a();
logger.info("Using epoll channel type");
} else {
channelClass = NioServerSocketChannel.class;
loopGroup = ServerConnection.a.c();
loopGroup = ServerConnection.a.a();
logger.info("Using nio channel type");
}
@@ -122,12 +120,12 @@ public abstract class NonblockingServerConnection {
}
}
@Shadow public volatile boolean d; // OBFHELPER: neverTerminate
@Shadow public volatile boolean c; // OBFHELPER: neverTerminate
/**
* Shuts down all open endpoints
*/
public void b() {
this.d = false;
this.c = false;
try {
synchronized (endPoints) { // safe fixes
for (ChannelFuture channel : endPoints) channel.channel().close().sync();
@@ -144,12 +142,7 @@ public abstract class NonblockingServerConnection {
logger.warn("Failed to handle packet for {}", manager.getSocketAddress(), ex);
final ChatComponentText message = new ChatComponentText("Internal server error");
manager.sendPacket(new PacketPlayOutKickDisconnect(message), new GenericFutureListener<Future<? super Void>>() {
@Override
public void operationComplete(Future<? super Void> future) throws Exception {
manager.close(message);
}
}, new GenericFutureListener[0]);
manager.sendPacket(new PacketPlayOutKickDisconnect(message), (future) -> manager.close(message));
manager.stopReading();
}
}

View File

@@ -24,13 +24,12 @@ public abstract class OptimisticNetworkManager {
@Shadow(aliases = "j") @Final private ReentrantReadWriteUpdateLock queueLock;
@Shadow public abstract Queue<NetworkManager.QueuedPacket> getPacketQueue();
@Shadow public abstract void dispatchPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>>[] genericFutureListeners);
@Shadow public abstract void dispatchPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> genericFutureListener);
@SuppressWarnings("unchecked")
private static final QueuedPacket SIGNAL_PACKET = new QueuedPacket(null);
private static final QueuedPacket SIGNAL_PACKET = new QueuedPacket(null, null);
@Overwrite // OBFHELPER: trySendQueue
private boolean m() {
private boolean o() {
if (this.channel != null && this.channel.isOpen()) {
if (this.packets.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all
return true;
@@ -47,7 +46,7 @@ public abstract class OptimisticNetworkManager {
if (packet == SIGNAL_PACKET) {
return false; // Return false if the peeked packet is a chunk packet which is not ready
} else {
dispatchPacket(packet.getPacket(), packet.getGenericFutureListeners()); // dispatch the packet
dispatchPacket(packet.getPacket(), packet.getGenericFutureListener()); // dispatch the packet
}
}
}

View File

@@ -19,7 +19,7 @@ public abstract class MixinEntity {
private int lastLavaCheck = Integer.MIN_VALUE;
@Overwrite // OBFHELPER: isInLava
public boolean au() {
public boolean ax() {
/*
* This originally comes from Migot (https://github.com/Poweruser/Migot/commit/cafbf1707107d2a3aa6232879f305975bb1f0285)
* Thanks @Poweruser
@@ -27,7 +27,7 @@ public abstract class MixinEntity {
int currentTick = MinecraftServer.currentTick;
if (this.lastLavaCheck != currentTick) {
this.lastLavaCheck = currentTick;
this.isInLava = this.world.a(this.getBoundingBox().grow(-0.10000000149011612D, -0.4000000059604645D, -0.10000000149011612D), Material.LAVA);
this.isInLava = this.world.a(this.getBoundingBox().f(0.10000000149011612D, 0.4000000059604645D, 0.10000000149011612D), Material.LAVA);
}
return this.isInLava;
}

View File

@@ -24,6 +24,7 @@
*/
package io.akarin.server.mixin.optimization;
import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -33,16 +34,15 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import com.google.common.base.Optional;
import net.minecraft.server.DataWatcherObject;
import net.minecraft.server.Entity;
import net.minecraft.server.EntityHorseAbstract;
import net.minecraft.server.EntityTypes;
import net.minecraft.server.World;
@Mixin(value = EntityHorseAbstract.class, remap = false)
public abstract class MixinEntityHorseAbstract extends Entity {
@Shadow(aliases = "bJ") @Final private static DataWatcherObject<Optional<UUID>> OWNER_UNIQUE_ID;
@Shadow(aliases = "bO") @Final private static DataWatcherObject<Optional<UUID>> OWNER_UNIQUE_ID;
@Nullable private Optional<UUID> cachedOwnerId;
@@ -50,12 +50,12 @@ public abstract class MixinEntityHorseAbstract extends Entity {
@Overwrite
public UUID getOwnerUUID() {
if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(OWNER_UNIQUE_ID);
return cachedOwnerId.orNull();
return cachedOwnerId.orElse(null);
}
@Overwrite
public void setOwnerUUID(@Nullable UUID uuid) {
cachedOwnerId = Optional.fromNullable(uuid);
cachedOwnerId = Optional.ofNullable(uuid);
datawatcher.set(OWNER_UNIQUE_ID, cachedOwnerId);
}
@@ -63,7 +63,7 @@ public abstract class MixinEntityHorseAbstract extends Entity {
* Extends from superclass
* @param world
*/
public MixinEntityHorseAbstract(World world) {
super(world);
public MixinEntityHorseAbstract(EntityTypes<?> entitytypes, World world) {
super(entitytypes, world);
}
}

View File

@@ -24,6 +24,7 @@
*/
package io.akarin.server.mixin.optimization;
import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -33,37 +34,36 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import com.google.common.base.Optional;
import net.minecraft.server.DataWatcherObject;
import net.minecraft.server.Entity;
import net.minecraft.server.EntityTameableAnimal;
import net.minecraft.server.EntityTypes;
import net.minecraft.server.World;
@Mixin(value = EntityTameableAnimal.class, remap = false)
public abstract class MixinEntityTameableAnimal extends Entity {
@Shadow @Final protected static DataWatcherObject<Optional<UUID>> by;
@Shadow @Final protected static DataWatcherObject<Optional<UUID>> bD;
@Nullable private Optional<UUID> cachedOwnerId;
@Nullable
@Overwrite
public UUID getOwnerUUID() {
if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(by);
return cachedOwnerId.orNull();
if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(bD);
return cachedOwnerId.orElse(null);
}
@Overwrite
public void setOwnerUUID(@Nullable UUID uuid) {
cachedOwnerId = Optional.fromNullable(uuid);
datawatcher.set(by, cachedOwnerId);
cachedOwnerId = Optional.ofNullable(uuid);
datawatcher.set(bD, cachedOwnerId);
}
/**
* Extends from superclass
* @param world
*/
public MixinEntityTameableAnimal(World world) {
super(world);
public MixinEntityTameableAnimal(EntityTypes<?> entitytypes, World world) {
super(entitytypes, world);
}
}

View File

@@ -1,47 +0,0 @@
package io.akarin.server.mixin.optimization;
import java.io.File;
import java.io.FileOutputStream;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import com.destroystokyo.paper.exception.ServerInternalException;
import net.minecraft.server.IDataManager;
import net.minecraft.server.MCUtil;
import net.minecraft.server.NBTCompressedStreamTools;
import net.minecraft.server.NBTTagCompound;
import net.minecraft.server.PersistentBase;
import net.minecraft.server.PersistentCollection;
@Mixin(value = PersistentCollection.class, remap = false)
public abstract class MixinPersistentCollection {
@Shadow(aliases = "b") @Final private IDataManager dataManager;
@Overwrite
private void a(PersistentBase persistentbase) {
if (this.dataManager == null) return;
File file = this.dataManager.getDataFile(persistentbase.id);
if (file == null) return;
NBTTagCompound nbttagcompound = new NBTTagCompound();
nbttagcompound.set("data", persistentbase.b(new NBTTagCompound()));
// Akarin start
MCUtil.scheduleAsyncTask(() -> {
try {
FileOutputStream fileoutputstream = new FileOutputStream(file);
NBTCompressedStreamTools.a(nbttagcompound, fileoutputstream);
fileoutputstream.close();
} catch (Exception exception) {
exception.printStackTrace();
ServerInternalException.reportInternalException(exception); // Paper
}
});
// Akarin end
}
}

View File

@@ -7,5 +7,5 @@ import net.minecraft.server.TileEntityEnchantTable;
@Mixin(value = TileEntityEnchantTable.class, remap = false)
public abstract class MixinTileEntityEnchantTable {
@Overwrite
public void e() {} // No tickable
public void Y_() {} // No tickable
}

View File

@@ -1,510 +0,0 @@
package io.akarin.server.mixin.optimization;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import org.apache.commons.lang3.ArrayUtils;
import org.bukkit.event.block.BlockRedstoneEvent;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import net.minecraft.server.BaseBlockPosition;
import net.minecraft.server.Block;
import net.minecraft.server.BlockDiodeAbstract;
import net.minecraft.server.BlockObserver;
import net.minecraft.server.BlockPiston;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.BlockRedstoneComparator;
import net.minecraft.server.BlockRedstoneTorch;
import net.minecraft.server.BlockRedstoneWire;
import net.minecraft.server.BlockRepeater;
import net.minecraft.server.Blocks;
import net.minecraft.server.EnumDirection;
import net.minecraft.server.IBlockAccess;
import net.minecraft.server.IBlockData;
import net.minecraft.server.Material;
import net.minecraft.server.World;
@Mixin(value = BlockRedstoneWire.class, remap = false)
public abstract class PandaRedstoneWire extends Block {
protected PandaRedstoneWire(Material material) {
super(material);
}
/** Positions that need to be turned off **/
private List<BlockPosition> turnOff = Lists.newArrayList();
/** Positions that need to be checked to be turned on **/
private List<BlockPosition> turnOn = Lists.newArrayList();
/** Positions of wire that was updated already (Ordering determines update order and is therefore required!) **/
private final Set<BlockPosition> updatedRedstoneWire = Sets.newLinkedHashSet();
/** Ordered arrays of the facings; Needed for the update order.
* I went with a vertical-first order here, but vertical last would work to.
* However it should be avoided to update the vertical axis between the horizontal ones as this would cause unneeded directional behavior. **/
private static final EnumDirection[] facingsHorizontal = {EnumDirection.WEST, EnumDirection.EAST, EnumDirection.NORTH, EnumDirection.SOUTH};
private static final EnumDirection[] facingsVertical = {EnumDirection.DOWN, EnumDirection.UP};
private static final EnumDirection[] facings = ArrayUtils.addAll(facingsVertical, facingsHorizontal);
/** Offsets for all surrounding blocks that need to receive updates **/
private static final BaseBlockPosition[] surroundingBlocksOffset;
static {
Set<BaseBlockPosition> set = Sets.newLinkedHashSet();
for (EnumDirection facing : facings) {
set.add(facing.getDirectionPosition());
}
for (EnumDirection facing1 : facings) {
BaseBlockPosition v1 = facing1.getDirectionPosition();
for (EnumDirection facing2 : facings) {
BaseBlockPosition v2 = facing2.getDirectionPosition();
set.add(new BaseBlockPosition(v1.getX() + v2.getX(), v1.getY() + v2.getY(), v1.getZ() + v2.getZ()));
}
}
set.remove(BaseBlockPosition.ZERO);
surroundingBlocksOffset = set.toArray(new BaseBlockPosition[set.size()]);
}
@Shadow(aliases = "g") private boolean canProvidePower;
@Shadow public abstract int getPower(World world, BlockPosition pos, int strength);
@Shadow(aliases = "b") public abstract boolean isPowerSourceAt(IBlockAccess worldIn, BlockPosition pos, EnumDirection side);
@Inject(method = "e", at = @At("HEAD"), cancellable = true)
private void onUpdateSurroundingRedstone(World worldIn, BlockPosition pos, IBlockData state, CallbackInfoReturnable<IBlockData> cir) {
this.updateSurroundingRedstone(worldIn, pos);
cir.setReturnValue(state);
}
@Inject(method = "a*", at = @At("HEAD"), cancellable = true)
private void onCalculateCurrentChanges(World worldIn, BlockPosition pos1, BlockPosition pos2, IBlockData state, CallbackInfoReturnable<IBlockData> cir) {
this.calculateCurrentChanges(worldIn, pos1);
cir.setReturnValue(state);
}
/**
* Recalculates all surrounding wires and causes all needed updates
*
* @author panda
*
* @param world World
* @param pos Position that needs updating
*/
private void updateSurroundingRedstone(World world, BlockPosition pos) {
// Recalculate the connected wires
this.calculateCurrentChanges(world, pos);
// Set to collect all the updates, to only execute them once. Ordering required.
Set<BlockPosition> blocksNeedingUpdate = Sets.newLinkedHashSet();
// Add the needed updates
for (BlockPosition posi : this.updatedRedstoneWire) {
this.addBlocksNeedingUpdate(world, posi, blocksNeedingUpdate);
}
// Add all other updates to keep known behaviors
// They are added in a backwards order because it preserves a commonly used behavior with the update order
Iterator<BlockPosition> it = Lists.newLinkedList(this.updatedRedstoneWire).descendingIterator();
while (it.hasNext()) {
this.addAllSurroundingBlocks(it.next(), blocksNeedingUpdate);
}
// Remove updates on the wires as they just were updated
blocksNeedingUpdate.removeAll(this.updatedRedstoneWire);
/*
* Avoid unnecessary updates on the just updated wires A huge scale test
* showed about 40% more ticks per second It's probably less in normal
* usage but likely still worth it
*/
this.updatedRedstoneWire.clear();
// Execute updates
for (BlockPosition posi : blocksNeedingUpdate) {
world.applyPhysics(posi, (BlockRedstoneWire) (Object) this, false);
}
}
/**
* Turns on or off all connected wires
*
* @param worldIn World
* @param position Position of the wire that received the update
*/
private void calculateCurrentChanges(World worldIn, BlockPosition position) {
// Turn off all connected wires first if needed
if (worldIn.getType(position).getBlock() == (BlockRedstoneWire) (Object) this) {
turnOff.add(position);
} else {
// In case this wire was removed, check the surrounding wires
this.checkSurroundingWires(worldIn, position);
}
while (!turnOff.isEmpty()) {
BlockPosition pos = turnOff.remove(0);
if (pos == null) continue; // Akarin
IBlockData state = worldIn.getType(pos);
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
this.canProvidePower = false;
int blockPower = worldIn.z(pos); // OBFHELPER: isBlockIndirectlyGettingPowered
this.canProvidePower = true;
int wirePower = this.getSurroundingWirePower(worldIn, pos);
// Lower the strength as it moved a block
wirePower--;
int newPower = Math.max(blockPower, wirePower);
// Akarin start - BlockRedstoneEvent
if (oldPower != newPower) {
BlockRedstoneEvent event = new BlockRedstoneEvent(worldIn.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), oldPower, newPower);
worldIn.getServer().getPluginManager().callEvent(event);
newPower = event.getNewCurrent();
}
// Akarin end
// Power lowered?
if (newPower < oldPower) {
// If it's still powered by a direct source (but weaker) mark for turn on
if (blockPower > 0 && !this.turnOn.contains(pos)) {
this.turnOn.add(pos);
}
// Set all the way to off for now, because wires that were powered by this need to update first
setWireState(worldIn, pos, state, 0);
// Power rose?
} else if (newPower > oldPower) {
// Set new Power
this.setWireState(worldIn, pos, state, newPower);
}
// Check if surrounding wires need to change based on the current/new state and add them to the lists
this.checkSurroundingWires(worldIn, pos);
}
// Now all needed wires are turned off. Time to turn them on again if there is a power source.
while (!this.turnOn.isEmpty()) {
BlockPosition pos = this.turnOn.remove(0);
if (pos == null) continue; // Akarin
IBlockData state = worldIn.getType(pos);
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
this.canProvidePower = false;
int blockPower = worldIn.z(pos); // OBFHELPER: isBlockIndirectlyGettingPowered
this.canProvidePower = true;
int wirePower = this.getSurroundingWirePower(worldIn, pos);
// Lower the strength as it moved a block
wirePower--;
int newPower = Math.max(blockPower, wirePower);
// Akarin start - BlockRedstoneEvent
BlockRedstoneEvent event = new BlockRedstoneEvent(worldIn.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), oldPower, newPower);
worldIn.getServer().getPluginManager().callEvent(event);
newPower = event.getNewCurrent();
// Akarin end
if (newPower > oldPower) {
setWireState(worldIn, pos, state, newPower);
} else if (newPower < oldPower) {
// Add warning
}
// Check if surrounding wires need to change based on the current/new state and add them to the lists
this.checkSurroundingWires(worldIn, pos);
}
this.turnOff.clear();
this.turnOn.clear();
}
/**
* Checks if an wire needs to be marked for update depending on the power next to it
*
* @author panda
*
* @param worldIn World
* @param pos Position of the wire that might need to change
* @param otherPower Power of the wire next to it
*/
private void addWireToList(World worldIn, BlockPosition pos, int otherPower) {
IBlockData state = worldIn.getType(pos);
if (state.getBlock() == (BlockRedstoneWire) (Object) this) {
int power = state.get(BlockRedstoneWire.POWER).intValue();
// Could get powered stronger by the neighbor?
if (power < (otherPower - 1) && !this.turnOn.contains(pos)) {
// Mark for turn on check.
this.turnOn.add(pos);
}
// Should have powered the neighbor? Probably was powered by it and is in turn off phase.
if (power > otherPower && !this.turnOff.contains(pos)) {
// Mark for turn off check.
this.turnOff.add(pos);
}
}
}
/**
* Checks if the wires around need to get updated depending on this wires state.
* Checks all wires below before the same layer before on top to keep
* some more rotational symmetry around the y-axis.
*
* @author panda
*
* @param worldIn World
* @param pos Position of the wire
*/
private void checkSurroundingWires(World worldIn, BlockPosition pos) {
IBlockData state = worldIn.getType(pos);
int ownPower = 0;
if (state.getBlock() == (BlockRedstoneWire) (Object) this) {
ownPower = state.get(BlockRedstoneWire.POWER).intValue();
}
// Check wires on the same layer first as they appear closer to the wire
for (EnumDirection facing : facingsHorizontal) {
BlockPosition offsetPos = pos.shift(facing);
if (facing.getAxis().isHorizontal()) {
this.addWireToList(worldIn, offsetPos, ownPower);
}
}
for (EnumDirection facingVertical : facingsVertical) {
BlockPosition offsetPos = pos.shift(facingVertical);
boolean solidBlock = worldIn.getType(offsetPos).k(); // OBFHELPER: isBlockNormalCube
for (EnumDirection facingHorizontal : facingsHorizontal) {
// wire can travel upwards if the block on top doesn't cut the wire (is non-solid)
// it can travel down if the block below is solid and the block "diagonal" doesn't cut off the wire (is non-solid)
if ((facingVertical == EnumDirection.UP && !solidBlock) || (facingVertical == EnumDirection.DOWN && solidBlock && !worldIn.getType(offsetPos.shift(facingHorizontal)).k())) { // OBFHELPER: isBlockNormalCube
this.addWireToList(worldIn, offsetPos.shift(facingHorizontal), ownPower);
}
}
}
}
/**
* Gets the maximum power of the surrounding wires
*
* @author panda
*
* @param worldIn World
* @param pos Position of the asking wire
* @return The maximum power of the wires that could power the wire at pos
*/
private int getSurroundingWirePower(World worldIn, BlockPosition pos) {
int wirePower = 0;
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
BlockPosition offsetPos = pos.shift(enumfacing);
// Wires on the same layer
wirePower = this.getPower(worldIn, offsetPos, wirePower);
// Block below the wire need to be solid (Upwards diode of slabs/stairs/glowstone) and no block should cut the wire
if(worldIn.getType(offsetPos).l() && !worldIn.getType(pos.up()).l()) { // OBFHELPER: isNormalCube
wirePower = this.getPower(worldIn, offsetPos.up(), wirePower);
// Only get from power below if no block is cutting the wire
} else if (!worldIn.getType(offsetPos).l()) { // OBFHELPER: isNormalCube
wirePower = this.getPower(worldIn, offsetPos.down(), wirePower);
}
}
return wirePower;
}
/**
* Adds all blocks that need to receive an update from a redstone change in this position.
* This means only blocks that actually could change.
*
* @author panda
*
* @param worldIn World
* @param pos Position of the wire
* @param set Set to add the update positions too
*/
private void addBlocksNeedingUpdate(World worldIn, BlockPosition pos, Set<BlockPosition> set) {
List<EnumDirection> connectedSides = this.getSidesToPower(worldIn, pos);
// Add the blocks next to the wire first (closest first order)
for (EnumDirection facing : facings) {
BlockPosition offsetPos = pos.shift(facing);
// canConnectTo() is not the nicest solution here as it returns true for e.g. the front of a repeater
// canBlockBePowereFromSide catches these cases
if (!connectedSides.contains(facing.opposite()) && facing != EnumDirection.DOWN
&& (!facing.getAxis().isHorizontal() || canConnectToBlock(worldIn.getType(offsetPos), facing))) continue;
if (this.canBlockBePoweredFromSide(worldIn.getType(offsetPos), facing, true))
set.add(offsetPos);
}
// Later add blocks around the surrounding blocks that get powered
for (EnumDirection facing : facings) {
BlockPosition offsetPos = pos.shift(facing);
if (!connectedSides.contains(facing.opposite()) && facing != EnumDirection.DOWN || !worldIn.getType(offsetPos).l()) continue; // OBFHELPER: isNormalCube
for (EnumDirection facing1 : facings) {
if (this.canBlockBePoweredFromSide(worldIn.getType(offsetPos.shift(facing1)), facing1, false))
set.add(offsetPos.shift(facing1));
}
}
}
/**
* Checks if a block can get powered from a side.
* This behavior would better be implemented per block type as follows:
* - return false as default. (blocks that are not affected by redstone don't need to be updated, it doesn't really hurt if they are either)
* - return true for all blocks that can get powered from all side and change based on it (doors, fence gates, trap doors, note blocks, lamps, dropper, hopper, TNT, rails, possibly more)
* - implement own logic for pistons, repeaters, comparators and redstone torches
* The current implementation was chosen to keep everything in one class.
*
* Why is this extra check needed?
* 1. It makes sure that many old behaviors still work (QC + Pistons).
* 2. It prevents updates from "jumping".
* Or rather it prevents this wire to update a block that would get powered by the next one of the same line.
* This is to prefer as it makes understanding the update order of the wire really easy. The signal "travels" from the power source.
*
* @author panda
*
* @param state State of the block
* @param side Side from which it gets powered
* @param isWire True if it's powered by a wire directly, False if through a block
* @return True if the block can change based on the power level it gets on the given side, false otherwise
*/
private boolean canBlockBePoweredFromSide(IBlockData state, EnumDirection side, boolean isWire) {
if (state.getBlock() instanceof BlockPiston && state.get(BlockPiston.FACING) == side.opposite()) {
return false;
}
if (state.getBlock() instanceof BlockDiodeAbstract && state.get(BlockDiodeAbstract.FACING) != side.opposite()) {
if (isWire && state.getBlock() instanceof BlockRedstoneComparator
&& state.get(BlockRedstoneComparator.FACING).k() != side.getAxis() && side.getAxis().isHorizontal()) {
return true;
}
return false;
}
if (state.getBlock() instanceof BlockRedstoneTorch) {
if (isWire || state.get(BlockRedstoneTorch.FACING) != side) {
return false;
}
}
return true;
}
/**
* Creates a list of all horizontal sides that can get powered by a wire.
* The list is ordered the same as the facingsHorizontal.
*
* @param worldIn World
* @param pos Position of the wire
* @return List of all facings that can get powered by this wire
*/
private List<EnumDirection> getSidesToPower(World worldIn, BlockPosition pos) {
List<EnumDirection> retval = Lists.newArrayList();
for (EnumDirection facing : facingsHorizontal) {
if (isPowerSourceAt(worldIn, pos, facing))
retval.add(facing);
}
if (retval.isEmpty()) return Lists.newArrayList(facingsHorizontal);
boolean northsouth = retval.contains(EnumDirection.NORTH) || retval.contains(EnumDirection.SOUTH);
boolean eastwest = retval.contains(EnumDirection.EAST) || retval.contains(EnumDirection.WEST);
if (northsouth) {
retval.remove(EnumDirection.EAST);
retval.remove(EnumDirection.WEST);
}
if (eastwest) {
retval.remove(EnumDirection.NORTH);
retval.remove(EnumDirection.SOUTH);
}
return retval;
}
/**
* Adds all surrounding positions to a set.
* This is the neighbor blocks, as well as their neighbors
*
* @param pos
* @param set
*/
private void addAllSurroundingBlocks(BlockPosition pos, Set<BlockPosition> set) {
for (BaseBlockPosition vect : surroundingBlocksOffset) {
set.add(pos.a(vect)); // OBFHELPER: add
}
}
/**
* Sets the block state of a wire with a new power level and marks for updates
*
* @author panda
*
* @param worldIn World
* @param pos Position at which the state needs to be set
* @param state Old state
* @param power Power it should get set to
*/
private void setWireState(World worldIn, BlockPosition pos, IBlockData state, int power) {
state = state.set(BlockRedstoneWire.POWER, Integer.valueOf(power));
worldIn.setTypeAndData(pos, state, 2);
updatedRedstoneWire.add(pos);
}
/**
* @author panda
* @reason Uses local surrounding block offset list for notifications.
*
* @param world The world
* @param pos The position
* @param state The block state
*/
@Override
@Overwrite
public void onPlace(World world, BlockPosition pos, IBlockData state) {
this.updateSurroundingRedstone(world, pos);
for (BaseBlockPosition vec : surroundingBlocksOffset) {
world.applyPhysics(pos.a(vec), this, false); // OBFHELPER: add
}
}
/**
* @author panda
* @reason Uses local surrounding block offset list for notifications.
*
* @param world The world
* @param pos The position
*/
@Override
@Overwrite
public void remove(World world, BlockPosition pos, IBlockData state) {
super.remove(world, pos, state);
this.updateSurroundingRedstone(world, pos);
for (BaseBlockPosition vec : surroundingBlocksOffset) {
world.applyPhysics(pos.a(vec), this, false); // OBFHELPER: add
}
}
/**
* @author panda
* @reason Changed to use getSidesToPower() to avoid duplicate implementation.
*
* @param blockState The block state
* @param blockAccess The block access
* @param pos The position
* @param side The side
*/
@Override
@Overwrite
public int b(IBlockData blockState, IBlockAccess blockAccess, BlockPosition pos, EnumDirection side) { // OBFHELPER: getWeakPower
if (!this.canProvidePower) {
return 0;
} else {
if (side == EnumDirection.UP || this.getSidesToPower((World) blockAccess, pos).contains(side)) {
return blockState.get(BlockRedstoneWire.POWER).intValue();
} else {
return 0;
}
}
}
private static boolean canConnectToBlock(IBlockData blockState, @Nullable EnumDirection side) {
Block block = blockState.getBlock();
if (block == Blocks.REDSTONE_WIRE) {
return true;
} else if (Blocks.UNPOWERED_REPEATER.D(blockState)) { // OBFHELPER: isSameDiode
EnumDirection enumdirection1 = blockState.get(BlockRepeater.FACING);
return enumdirection1 == side || enumdirection1.opposite() == side;
} else if (Blocks.dk == blockState.getBlock()) {
return side == blockState.get(BlockObserver.FACING); // OBFHELPER: OBSERVER
} else {
return blockState.m() && side != null; // OBFHELPER: canProvidePower
}
}
}

View File

@@ -1,26 +0,0 @@
package io.akarin.server.mixin.optimization;
import java.util.Random;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.World;
import net.minecraft.server.WorldGenBigTree;
/**
* Fixes MC-128547(https://bugs.mojang.com/browse/MC-128547)
*/
@Mixin(value = WorldGenBigTree.class, remap = false)
public abstract class WeakBigTree {
@Shadow(aliases = "l") private World worldReference;
@Inject(method = "generate", at = @At("RETURN"))
private void clearWorldRef(World world, Random random, BlockPosition pos, CallbackInfoReturnable<?> info) {
world = null; // Akarin - remove references to world objects to avoid memory leaks
}
}

View File

@@ -1,95 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.optimization;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.server.DamageSource;
import net.minecraft.server.EnchantmentManager;
import net.minecraft.server.Entity;
import net.minecraft.server.EntityHuman;
import net.minecraft.server.EntityLiving;
import net.minecraft.server.ItemStack;
/**
* Fixes MC-128547(https://bugs.mojang.com/browse/MC-128547)
*/
@Mixin(value = EnchantmentManager.class, remap = false)
public abstract class WeakEnchantmentManager {
@Shadow(aliases = "a") @Final private static EnchantmentManager.EnchantmentModifierProtection protection;
@Shadow(aliases = "c") @Final private static EnchantmentManager.EnchantmentModifierThorns thorns;
@Shadow(aliases = "d") @Final private static EnchantmentManager.EnchantmentModifierArthropods arthropods;
@Shadow private static void a(EnchantmentManager.EnchantmentModifier modifier, Iterable<ItemStack> iterable) {}
@Shadow private static void a(EnchantmentManager.EnchantmentModifier modifier, ItemStack itemstack) {}
@Overwrite
public static int a(Iterable<ItemStack> iterable, DamageSource damageSource) {
protection.a = 0; // OBFHELPER: damageModifier
protection.b = damageSource;
a(protection, iterable); // OBFHELPER: applyEnchantmentModifierArray
protection.b = null; // Akarin - Remove reference to Damagesource
return protection.a;
}
@Overwrite
public static void a(EntityLiving user, Entity attacker) { // OBFHELPER: applyThornEnchantments
thorns.b = attacker;
thorns.a = user;
if (user != null) {
a(thorns, user.aQ()); // OBFHELPER: applyEnchantmentModifierArray - getEquipmentAndArmor
}
if (attacker instanceof EntityHuman) {
a(thorns, user.getItemInMainHand()); // OBFHELPER: applyEnchantmentModifier
}
// Akarin Start - remove references to entity objects to avoid memory leaks
thorns.b = null;
thorns.a = null;
// Akarin end
}
@Overwrite
public static void b(EntityLiving user, Entity target) { // OBFHELPER: applyArthropodEnchantments
arthropods.a = user;
arthropods.b = target;
if (user != null) {
a(arthropods, user.aQ()); // OBFHELPER: applyEnchantmentModifierArray - getEquipmentAndArmor
}
if (user instanceof EntityHuman) {
a(arthropods, user.getItemInMainHand()); // OBFHELPER: applyEnchantmentModifier
}
// Akarin Start - remove references to entity objects to avoid memory leaks
arthropods.a = null;
arthropods.b = null;
// Akarin end
}
}

View File

@@ -1,56 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.Entity;
import net.minecraft.server.World;
@Mixin(value = Entity.class, remap = false, priority = 1001)
public abstract class MixinEntity {
private static final String ENTITY_RIDABLE_COOLDOWN_FIELD = "Lnet/minecraft/entity/Entity;j:I"; // PUTFIELD: rideCooldown
private static final String ENTITY_PORTAL_COUNTER_FIELD = "Lnet/minecraft/entity/Entity;al:I"; // PUTFIELD: portalCounter
@Shadow protected int j;
@Shadow protected int al;
@Shadow public World world;
// OBFHELPER: onEntityUpdate
@Redirect(method = "Y()V", at = @At(value = "FIELD", target = ENTITY_RIDABLE_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupEntityCooldown(Entity self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.world).getRealTimeTicks();
this.j = Math.max(0, this.j - ticks); // OBFHELPER: rideCooldown
}
@Redirect(method = "Y()V", at = @At(value = "FIELD", target = ENTITY_PORTAL_COUNTER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupPortalCounter(Entity self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.world).getRealTimeTicks();
this.al += ticks; // OBFHELPER: portalCounter
}
}

View File

@@ -1,51 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.EntityAgeable;
@Mixin(value = EntityAgeable.class, remap = false)
public abstract class MixinEntityAgeable {
private static final String ENTITY_AGEABLE_SET_GROWING_AGE_METHOD = "Lnet/minecraft/entity/EntityAgeable;setAgeRaw(I)V";
// OBFHELPER: onLivingUpdate
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = ENTITY_AGEABLE_SET_GROWING_AGE_METHOD, ordinal = 0))
public void fixupGrowingUp(EntityAgeable self, int age) {
// Subtract the one the original update method added
int diff = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks() - 1;
self.setAgeRaw(Math.min(0, age + diff));
}
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = ENTITY_AGEABLE_SET_GROWING_AGE_METHOD, ordinal = 1))
public void fixupBreedingCooldown(EntityAgeable self, int age) {
// Subtract the one the original update method added
int diff = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks() - 1;
self.setAgeRaw(Math.max(0, age - diff));
}
}

View File

@@ -1,55 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.EntityExperienceOrb;
@Mixin(value = EntityExperienceOrb.class, remap = false)
public abstract class MixinEntityExperienceOrb {
private static final String ENTITY_XP_DELAY_PICKUP_FIELD = "Lnet/minecraft/entity/item/EntityExperienceOrb;c:I"; // PUTFIELD: delayBeforeCanPickup
private static final String ENTITY_XP_AGE_FIELD = "Lnet/minecraft/entity/item/EntityExperienceOrb;b:I"; // PUTFIELD: xpOrbAge
@Shadow public int c; // OBFHELPER: delayBeforeCanPickup
@Shadow public int b; // OBFHELPER: xpOrbAge
// OBFHELPER: onUpdate
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_XP_DELAY_PICKUP_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupPickupDelay(EntityExperienceOrb self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.c = Math.max(0, this.c - ticks); // OBFHELPER: delayBeforeCanPickup
}
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_XP_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupAge(EntityExperienceOrb self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.b += ticks; // OBFHELPER: xpOrbAge
}
}

View File

@@ -1,61 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.EntityHuman;
@Mixin(value = EntityHuman.class, remap = false)
public abstract class MixinEntityHuman {
private static final String ENTITY_PLAYER_XP_COOLDOWN_FIELD = "Lnet/minecraft/entity/player/EntityHuman;bD:I"; // PUTFIELD: xpCooldown
private static final String ENTITY_PLAYER_SLEEP_TIMER_FIELD = "Lnet/minecraft/entity/player/EntityHuman;sleepTicks:I";
@Shadow public int bD;
@Shadow private int sleepTicks;
// OBFHELPER: onUpdate
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_XP_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupXpCooldown(EntityHuman self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.bD = Math.max(0, this.bD - ticks); // OBFHELPER: xpCooldown
}
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_SLEEP_TIMER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupSleepTimer(EntityHuman self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.sleepTicks += ticks;
}
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_SLEEP_TIMER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 2))
public void fixupWakeTimer(EntityHuman self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.sleepTicks += ticks;
}
}

View File

@@ -1,50 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.EntityInsentient;
import net.minecraft.server.EntityLiving;
import net.minecraft.server.World;
@Mixin(value = EntityInsentient.class, remap = false)
public abstract class MixinEntityInsentient extends EntityLiving {
private static final String ENTITY_LIVING_AGE_FIELD = "Lnet/minecraft/entity/EntityInsentient;ticksFarFromPlayer:I";
public MixinEntityInsentient(World world) {
super(world);
}
@Redirect(method = "doTick()V", at = @At(value = "FIELD", target = ENTITY_LIVING_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupEntityDespawnAge(EntityInsentient self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.ticksFarFromPlayer += ticks;
}
}

View File

@@ -1,55 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.EntityItem;
@Mixin(value = EntityItem.class, remap = false)
public abstract class MixinEntityItem {
private static final String ENTITY_ITEM_DELAY_PICKUP_FIELD = "Lnet/minecraft/entity/item/EntityItem;pickupDelay:I";
private static final String ENTITY_ITEM_AGE_FIELD = "Lnet/minecraft/entity/item/EntityItem;age:I";
@Shadow public int age;
@Shadow private int pickupDelay;
// OBFHELPER: onUpdate
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_ITEM_DELAY_PICKUP_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupPickupDelay(EntityItem self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.pickupDelay = Math.max(0, this.pickupDelay - ticks);
}
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_ITEM_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupAge(EntityItem self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.age += ticks;
}
}

View File

@@ -1,51 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.Entity;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.World;
@Mixin(value = EntityPlayer.class, remap = false)
public abstract class MixinEntityPlayer extends Entity {
private static final String ENTITY_PLAYER_MP_PORTAL_COOLDOWN_FIELD = "Lnet/minecraft/entity/player/EntityPlayer;portalCooldown:I";
public MixinEntityPlayer(World worldIn) {
super(worldIn);
}
// OBFHELPER: decrementTimeUntilPortal
@Redirect(method = "I()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_MP_PORTAL_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupPortalCooldown(EntityPlayer self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.portalCooldown = Math.max(0, this.portalCooldown - ticks);
}
}

View File

@@ -1,47 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.EntityZombieVillager;
@Mixin(value = EntityZombieVillager.class, remap = false)
public abstract class MixinEntityZombieVillager {
private static final String ENTITY_ZOMBIE_GET_CONVERSION_BOOST_METHOD = "Lnet/minecraft/entity/monster/EntityZombieVillager;du()I"; // INVOKE: getConversionProgress
@Shadow(aliases = "du") protected abstract int getConversionProgress();
// OBFHELPER: onUpdate
@Redirect(method = "B_()V", at = @At(value = "INVOKE", target = ENTITY_ZOMBIE_GET_CONVERSION_BOOST_METHOD, ordinal = 0))
public int fixupConversionTimeBoost(EntityZombieVillager self) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
return this.getConversionProgress() * ticks;
}
}

View File

@@ -1,30 +0,0 @@
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.MinecraftServer;
@Mixin(value = MinecraftServer.class, remap = false, priority = 1001)
public abstract class MixinMinecraftServer implements IMixinRealTimeTicking {
private static long lastTickNanos = System.nanoTime();
private static long realTimeTicks = 1;
@Inject(method = "C()V", at = @At("HEAD")) // OBFHELPER: fullTick
public void onTickUpdateRealTimeTicks(CallbackInfo ci) {
long currentNanos = System.nanoTime();
realTimeTicks = (currentNanos - lastTickNanos) / 50000000;
if (realTimeTicks < 1) {
realTimeTicks = 1;
}
lastTickNanos = currentNanos;
}
@Override
public long getRealTimeTicks() {
return realTimeTicks;
}
}

View File

@@ -1,58 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerConnection;
@Mixin(value = PlayerConnection.class, remap = false)
public abstract class MixinPlayerConnection {
private static final String NET_HANDLER_PLAY_CHAT_SPAM_FIELD = "Lnet/minecraft/network/PlayerConnection;chatThrottle:I";
private static final String NET_HANDLER_PLAY_DROP_SPAM_FIELD = "Lnet/minecraft/network/PlayerConnection;itemDropThreshold:I";
@Shadow private volatile int chatThrottle;
@Shadow(aliases = "j") private int itemDropThreshold;
@Shadow @Final private MinecraftServer minecraftServer;
// OBFHELPER: update
@Redirect(method = "e()V", at = @At(value = "FIELD", target = NET_HANDLER_PLAY_CHAT_SPAM_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupChatSpamCheck(PlayerConnection self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.minecraftServer).getRealTimeTicks();
this.chatThrottle = Math.max(0, this.chatThrottle - ticks);
}
@Redirect(method = "e()V", at = @At(value = "FIELD", target = NET_HANDLER_PLAY_DROP_SPAM_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupDropSpamCheck(PlayerConnection self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.minecraftServer).getRealTimeTicks();
this.itemDropThreshold = Math.max(0, this.itemDropThreshold - ticks);
}
}

View File

@@ -1,49 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.PlayerInteractManager;
import net.minecraft.server.World;
@Mixin(value = PlayerInteractManager.class, remap = false)
public abstract class MixinPlayerInteractManager {
private static final String PLAYER_INTERACTION_BLOCK_DAMAGE_FIELD = "Lnet/minecraft/server/management/PlayerInteractManager;currentTick:I";
@Shadow public World world;
@Shadow private int currentTick;
// OBFHELPER: updateBlockRemoving
@Redirect(method = "a()V", at = @At(value = "FIELD", target = PLAYER_INTERACTION_BLOCK_DAMAGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupDiggingTime(PlayerInteractManager self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.world.getMinecraftServer()).getRealTimeTicks();
this.currentTick += ticks;
}
}

View File

@@ -1,48 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.TileEntity;
import net.minecraft.server.TileEntityBrewingStand;
@Mixin(value = TileEntityBrewingStand.class, remap = false)
public abstract class MixinTileEntityBrewingStand extends TileEntity {
private static final String BREWING_STAND_BREW_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityBrewingStand;brewTime:I";
@Shadow private int brewTime;
// OBFHELPER: update
@Redirect(method = "e()V", at = @At(value = "FIELD", target = BREWING_STAND_BREW_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupBrewTime(TileEntityBrewingStand self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
this.brewTime = Math.max(0, this.brewTime - ticks);
}
}

View File

@@ -1,64 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.MathHelper;
import net.minecraft.server.TileEntity;
import net.minecraft.server.TileEntityFurnace;
@Mixin(value = TileEntityFurnace.class, remap = false)
public abstract class MixinTileEntityFurnace extends TileEntity {
private static final String FURNACE_BURN_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityFurnace;burnTime:I";
private static final String FURNACE_COOK_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityFurnace;cookTime:I";
@Shadow private int burnTime;
@Shadow private int cookTime;
@Shadow private int cookTimeTotal;
// OBFHELPER: update
@Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_BURN_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupBurnTime(TileEntityFurnace self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
this.burnTime = Math.max(0, this.burnTime - ticks);
}
@Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_COOK_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupCookTime(TileEntityFurnace self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
this.cookTime = Math.min(this.cookTimeTotal, this.cookTime + ticks);
}
@Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_COOK_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 3))
public void fixupCookTimeCooldown(TileEntityFurnace self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
this.cookTime = MathHelper.clamp(this.cookTime - (2 * ticks), 0, this.cookTimeTotal);
}
}

View File

@@ -1,46 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.World;
@Mixin(value = World.class, remap = false, priority = 1001)
public abstract class MixinWorld implements IMixinRealTimeTicking {
@Shadow @Nullable public abstract MinecraftServer getMinecraftServer();
@Override
public long getRealTimeTicks() {
if (this.getMinecraftServer() != null) {
return ((IMixinRealTimeTicking) this.getMinecraftServer()).getRealTimeTicks();
}
return 1;
}
}

View File

@@ -1,61 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.bukkit.World.Environment;
import org.bukkit.generator.ChunkGenerator;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.IDataManager;
import net.minecraft.server.MethodProfiler;
import net.minecraft.server.World;
import net.minecraft.server.WorldData;
import net.minecraft.server.WorldProvider;
import net.minecraft.server.WorldServer;
@Mixin(value = WorldServer.class, remap = false, priority = 1001)
public abstract class MixinWorldServer extends World implements IMixinRealTimeTicking {
protected MixinWorldServer(IDataManager idatamanager, WorldData worlddata, WorldProvider worldprovider, MethodProfiler methodprofiler, boolean flag, ChunkGenerator gen, Environment env) {
super(idatamanager, worlddata, worldprovider, methodprofiler, flag, gen, env);
}
@Inject(method = "doTick()V", at = @At("HEAD"))
public void fixTimeOfDay(CallbackInfo ci) {
if (this.getGameRules().getBoolean("doDaylightCycle")) {
// Subtract the one the original tick method is going to add
long diff = this.getRealTimeTicks() - 1;
// Don't set if we're not changing it as other mods might be listening for changes
if (diff > 0) {
this.worldData.setDayTime(this.worldData.getDayTime() + diff);
}
}
}
}

View File

@@ -0,0 +1,500 @@
package net.minecraft.server;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.Nullable;
import com.destroystokyo.paper.exception.ServerInternalException;
import io.akarin.api.internal.Akari;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// CraftBukkit start
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
import org.bukkit.event.world.ChunkUnloadEvent;
// CraftBukkit end
/**
* Akarin Changes Note
* 1) Lock for event (safety issue)
*/
public class ChunkProviderServer implements IChunkProvider {
private static final Logger a = LogManager.getLogger();
public final LongSet unloadQueue = new LongOpenHashSet();
public final ChunkGenerator<?> chunkGenerator;
public final IChunkLoader chunkLoader; // PAIL
// Paper start - chunk save stats
private long lastQueuedSaves = 0L; // Paper
private long lastProcessedSaves = 0L; // Paper
private long lastSaveStatPrinted = System.currentTimeMillis();
// Paper end
public final Long2ObjectMap<Chunk> chunks = Long2ObjectMaps.synchronize(new ChunkMap(8192));
private Chunk lastChunk;
private final ChunkTaskScheduler chunkScheduler;
final SchedulerBatch<ChunkCoordIntPair, ChunkStatus, ProtoChunk> batchScheduler; // Paper
public final WorldServer world;
final IAsyncTaskHandler asyncTaskHandler; // Paper
public ChunkProviderServer(WorldServer worldserver, IChunkLoader ichunkloader, ChunkGenerator<?> chunkgenerator, IAsyncTaskHandler iasynctaskhandler) {
this.world = worldserver;
this.chunkLoader = ichunkloader;
this.chunkGenerator = chunkgenerator;
this.asyncTaskHandler = iasynctaskhandler;
this.chunkScheduler = new ChunkTaskScheduler(0, worldserver, chunkgenerator, ichunkloader, iasynctaskhandler); // CraftBukkit - very buggy, broken in lots of __subtle__ ways. Same goes for async chunk loading. Also Bukkit API / plugins can't handle async events at all anyway.
this.batchScheduler = new SchedulerBatch(this.chunkScheduler);
}
public Collection<Chunk> a() {
return this.chunks.values();
}
public void unload(Chunk chunk) {
if (this.world.worldProvider.a(chunk.locX, chunk.locZ)) {
this.unloadQueue.add(ChunkCoordIntPair.a(chunk.locX, chunk.locZ));
}
}
public void b() {
ObjectIterator objectiterator = this.chunks.values().iterator();
while (objectiterator.hasNext()) {
Chunk chunk = (Chunk) objectiterator.next();
this.unload(chunk);
}
}
public void a(int i, int j) {
this.unloadQueue.remove(ChunkCoordIntPair.a(i, j));
}
// Paper start - defaults if Async Chunks is not enabled
boolean chunkGoingToExists(int x, int z) {
final long k = ChunkCoordIntPair.asLong(x, z);
return chunkScheduler.progressCache.containsKey(k);
}
public void bumpPriority(ChunkCoordIntPair coords) {
// do nothing, override in async
}
public List<ChunkCoordIntPair> getSpiralOutChunks(BlockPosition blockposition, int radius) {
List<ChunkCoordIntPair> list = com.google.common.collect.Lists.newArrayList();
for (int r = 1; r <= radius; r++) {
int x = -r;
int z = r;
list.add(new ChunkCoordIntPair(blockposition.getX(), blockposition.getZ()));
// Iterates the edge of half of the box; then negates for other half.
while (x <= r && z > -r) {
list.add(new ChunkCoordIntPair(blockposition.getX() + x, blockposition.getZ() + z));
list.add(new ChunkCoordIntPair(blockposition.getX() - x, blockposition.getZ() - z));
if (x < r) {
x++;
} else {
z--;
}
}
}
return list;
}
public Chunk getChunkAt(int x, int z, boolean load, boolean gen, Consumer<Chunk> consumer) {
return getChunkAt(x, z, load, gen, false, consumer);
}
public Chunk getChunkAt(int x, int z, boolean load, boolean gen, boolean priority, Consumer<Chunk> consumer) {
Chunk chunk = getChunkAt(x, z, load, gen);
if (consumer != null) {
consumer.accept(chunk);
}
return chunk;
}
// Paper end
@Nullable
public Chunk getChunkAt(int i, int j, boolean flag, boolean flag1) {
IChunkLoader ichunkloader = this.chunkLoader;
Chunk chunk;
// Paper start - do already loaded checks before synchronize
long k = ChunkCoordIntPair.a(i, j);
chunk = (Chunk) this.chunks.get(k);
if (chunk != null) {
//this.lastChunk = chunk; // Paper remove vanilla lastChunk
return chunk;
}
// Paper end
synchronized (this.chunkLoader) {
// Paper start - remove vanilla lastChunk, we do it more accurately
/* if (this.lastChunk != null && this.lastChunk.locX == i && this.lastChunk.locZ == j) {
return this.lastChunk;
}*/ // Paper end
// Paper start - move up
//long k = ChunkCoordIntPair.a(i, j);
/*chunk = (Chunk) this.chunks.get(k);
if (chunk != null) {
//this.lastChunk = chunk; // Paper remove vanilla lastChunk
return chunk;
}*/
// Paper end
if (flag) {
try (co.aikar.timings.Timing timing = world.timings.syncChunkLoadTimer.startTiming()) { // Paper
// CraftBukkit - decompile error
chunk = this.chunkLoader.a(this.world, i, j, (chunk1) -> {
chunk1.setLastSaved(this.world.getTime());
this.chunks.put(ChunkCoordIntPair.a(i, j), chunk1);
});
} catch (Exception exception) {
ChunkProviderServer.a.error("Couldn\'t load chunk", exception);
}
}
}
if (chunk != null) {
this.asyncTaskHandler.postToMainThread(chunk::addEntities);
return chunk;
} else if (flag1) {
try (co.aikar.timings.Timing timing = world.timings.chunkGeneration.startTiming()) { // Paper
this.batchScheduler.b();
this.batchScheduler.a(new ChunkCoordIntPair(i, j));
CompletableFuture<ProtoChunk> completablefuture = this.batchScheduler.c(); // CraftBukkit - decompile error
return (Chunk) completablefuture.thenApply(this::a).join();
} catch (RuntimeException runtimeexception) {
throw this.a(i, j, (Throwable) runtimeexception);
}
// finally { world.timings.syncChunkLoadTimer.stopTiming(); } // Spigot // Paper
} else {
return null;
}
}
// CraftBukkit start
public Chunk generateChunk(int x, int z) {
try {
this.batchScheduler.b();
ChunkCoordIntPair pos = new ChunkCoordIntPair(x, z);
this.chunkScheduler.forcePolluteCache(pos);
this.batchScheduler.a(pos);
CompletableFuture<ProtoChunk> completablefuture = this.batchScheduler.c();
return (Chunk) completablefuture.thenApply(this::a).join();
} catch (RuntimeException runtimeexception) {
throw this.a(x, z, (Throwable) runtimeexception);
}
}
// CraftBukkit end
public IChunkAccess a(int i, int j, boolean flag) {
Chunk chunk = this.getChunkAt(i, j, true, false);
return (IChunkAccess) (chunk != null ? chunk : (IChunkAccess) this.chunkScheduler.b(new ChunkCoordIntPair(i, j), flag));
}
public CompletableFuture<Void> loadAllChunks(Iterable<ChunkCoordIntPair> iterable, Consumer<Chunk> consumer) { return a(iterable, consumer).thenCompose(protoChunk -> null); } // Paper - overriden in async chunk provider
private CompletableFuture<ProtoChunk> a(Iterable<ChunkCoordIntPair> iterable, Consumer<Chunk> consumer) { // Paper - mark private, use above method
this.batchScheduler.b();
Iterator iterator = iterable.iterator();
while (iterator.hasNext()) {
ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) iterator.next();
Chunk chunk = this.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z, true, false);
if (chunk != null) {
consumer.accept(chunk);
} else {
this.batchScheduler.a(chunkcoordintpair).thenApply(this::a).thenAccept(consumer);
}
}
return this.batchScheduler.c();
}
ReportedException generateChunkError(int i, int j, Throwable throwable) { return a(i, j, throwable); } // Paper - OBFHELPER
private ReportedException a(int i, int j, Throwable throwable) {
CrashReport crashreport = CrashReport.a(throwable, "Exception generating new chunk");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated");
crashreportsystemdetails.a("Location", (Object) String.format("%d,%d", new Object[] { Integer.valueOf(i), Integer.valueOf(j)}));
crashreportsystemdetails.a("Position hash", (Object) Long.valueOf(ChunkCoordIntPair.a(i, j)));
crashreportsystemdetails.a("Generator", (Object) this.chunkGenerator);
return new ReportedException(crashreport);
}
private Chunk a(IChunkAccess ichunkaccess) {
ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos();
int i = chunkcoordintpair.x;
int j = chunkcoordintpair.z;
long k = ChunkCoordIntPair.a(i, j);
Long2ObjectMap long2objectmap = this.chunks;
Chunk chunk;
Akari.eventLock.lock(); // Akarin
try { // Akarin
synchronized (this.chunks) {
Chunk chunk1 = (Chunk) this.chunks.get(k);
if (chunk1 != null) {
return chunk1;
}
if (ichunkaccess instanceof Chunk) {
chunk = (Chunk) ichunkaccess;
} else {
if (!(ichunkaccess instanceof ProtoChunk)) {
throw new IllegalStateException();
}
chunk = new Chunk(this.world, (ProtoChunk) ichunkaccess, i, j);
}
this.chunks.put(k, chunk);
//this.lastChunk = chunk; // Paper
}
} finally { Akari.eventLock.unlock(); } // Akarin
this.asyncTaskHandler.postToMainThread(chunk::addEntities);
return chunk;
}
public void saveChunk(IChunkAccess ichunkaccess, boolean unloaded) { // Spigot
try (co.aikar.timings.Timing timed = world.timings.chunkSaveData.startTiming()) { // Paper - Timings
ichunkaccess.setLastSaved(this.world.getTime());
this.chunkLoader.saveChunk(this.world, ichunkaccess, unloaded); // Spigot
} catch (IOException ioexception) {
// Paper start
String msg = "Couldn\'t save chunk";
ChunkProviderServer.a.error(msg, ioexception);
ServerInternalException.reportInternalException(ioexception);
} catch (ExceptionWorldConflict exceptionworldconflict) {
String msg = "Couldn\'t save chunk; already in use by another instance of Minecraft?";
ChunkProviderServer.a.error(msg, exceptionworldconflict);
ServerInternalException.reportInternalException(exceptionworldconflict);
}
}
public boolean a(boolean flag) {
int i = 0;
this.chunkScheduler.a(() -> {
return true;
});
IChunkLoader ichunkloader = this.chunkLoader;
synchronized (this.chunkLoader) {
ObjectIterator objectiterator = this.chunks.values().iterator();
// Paper start
final ChunkRegionLoader chunkLoader = (ChunkRegionLoader) world.getChunkProviderServer().chunkLoader;
final int queueSize = chunkLoader.getQueueSize();
final long now = System.currentTimeMillis();
final long timeSince = (now - lastSaveStatPrinted) / 1000;
final Integer printRateSecs = Integer.getInteger("printSaveStats");
if (printRateSecs != null && timeSince >= printRateSecs) {
final String timeStr = "/" + timeSince +"s";
final long queuedSaves = chunkLoader.getQueuedSaves();
long queuedDiff = queuedSaves - lastQueuedSaves;
lastQueuedSaves = queuedSaves;
final long processedSaves = chunkLoader.getProcessedSaves();
long processedDiff = processedSaves - lastProcessedSaves;
lastProcessedSaves = processedSaves;
lastSaveStatPrinted = now;
if (processedDiff > 0 || queueSize > 0 || queuedDiff > 0) {
System.out.println("[Chunk Save Stats] " + world.worldData.getName() +
" - Current: " + queueSize +
" - Queued: " + queuedDiff + timeStr +
" - Processed: " +processedDiff + timeStr
);
}
}
if (queueSize > world.paperConfig.queueSizeAutoSaveThreshold){
return false;
}
// Paper end
while (objectiterator.hasNext()) {
Chunk chunk = (Chunk) objectiterator.next();
if (chunk.c(flag)) {
this.saveChunk(chunk, false); // Spigot
chunk.a(false);
++i;
if (!flag && i >= world.paperConfig.maxAutoSaveChunksPerTick) { // Spigot - // Paper - Incremental Auto Save - cap max
return false;
}
}
}
return true;
}
}
public void close() {
// Paper start - we do not need to wait for chunk generations to finish on close
/*try {
this.batchScheduler.a();
} catch (InterruptedException interruptedexception) {
ChunkProviderServer.a.error("Couldn\'t stop taskManager", interruptedexception);
}*/
// Paper end
}
public void c() {
IChunkLoader ichunkloader = this.chunkLoader;
synchronized (this.chunkLoader) {
this.chunkLoader.b();
}
}
private static final double UNLOAD_QUEUE_RESIZE_FACTOR = 0.96; // Spigot
public boolean unloadChunks(BooleanSupplier booleansupplier) {
if (!this.world.savingDisabled) {
if (!this.unloadQueue.isEmpty()) {
// Spigot start
org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant;
activityAccountant.startActivity(0.5);
int targetSize = Math.min(this.unloadQueue.size() - 100, (int) (this.unloadQueue.size() * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
// Spigot end
LongIterator longiterator = this.unloadQueue.iterator();
while (longiterator.hasNext()) { // Spigot
Long olong = (Long) longiterator.next();
longiterator.remove(); // Spigot
IChunkLoader ichunkloader = this.chunkLoader;
synchronized (this.chunkLoader) {
Chunk chunk = (Chunk) this.chunks.get(olong);
if (chunk != null) {
// CraftBukkit start - move unload logic to own method
if (!unloadChunk(chunk, true)) {
continue;
}
// CraftBukkit end
// Spigot start
if (!booleansupplier.getAsBoolean() && this.unloadQueue.size() <= targetSize && activityAccountant.activityTimeIsExhausted()) {
break;
}
// Spigot end
}
}
}
activityAccountant.endActivity(); // Spigot
}
// Paper start - delayed chunk unloads
long now = System.currentTimeMillis();
long unloadAfter = world.paperConfig.delayChunkUnloadsBy;
if (unloadAfter > 0) {
//noinspection Convert2streamapi
for (Chunk chunk : chunks.values()) {
if (chunk.scheduledForUnload != null && now - chunk.scheduledForUnload > unloadAfter) {
chunk.scheduledForUnload = null;
unload(chunk);
}
}
}
// Paper end
this.chunkScheduler.a(booleansupplier);
}
return false;
}
// CraftBukkit start
public boolean unloadChunk(Chunk chunk, boolean save) {
ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk, save);
this.world.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
}
save = event.isSaveChunk();
chunk.lightingQueue.processUnload(); // Paper
// Update neighbor counts
for (int x = -2; x < 3; x++) {
for (int z = -2; z < 3; z++) {
if (x == 0 && z == 0) {
continue;
}
Chunk neighbor = this.chunks.get(chunk.chunkKey); // Paper
if (neighbor != null) {
neighbor.setNeighborUnloaded(-x, -z);
chunk.setNeighborUnloaded(x, z);
}
}
}
// Moved from unloadChunks above
synchronized (this.chunkLoader) {
chunk.removeEntities();
if (save) {
this.saveChunk(chunk, true); // Spigot
}
this.chunks.remove(chunk.chunkKey);
// this.lastChunk = null; // Paper
}
return true;
}
// CraftBukkit end
public boolean d() {
return !this.world.savingDisabled;
}
public String getName() {
return "ServerChunkCache: " + this.chunks.size() + " Drop: " + this.unloadQueue.size();
}
public List<BiomeBase.BiomeMeta> a(EnumCreatureType enumcreaturetype, BlockPosition blockposition) {
return this.chunkGenerator.getMobsFor(enumcreaturetype, blockposition);
}
public int a(World world, boolean flag, boolean flag1) {
return this.chunkGenerator.a(world, flag, flag1);
}
@Nullable
public BlockPosition a(World world, String s, BlockPosition blockposition, int i, boolean flag) {
return this.chunkGenerator.findNearestMapFeature(world, s, blockposition, i, flag);
}
public ChunkGenerator<?> getChunkGenerator() {
return this.chunkGenerator;
}
public int g() {
return this.chunks.size();
}
public boolean isLoaded(int i, int j) {
return this.chunks.containsKey(ChunkCoordIntPair.a(i, j));
}
}

View File

@@ -1,623 +0,0 @@
package net.minecraft.server;
import com.google.common.collect.Maps;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import java.util.concurrent.ConcurrentLinkedQueue; // Paper
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// Spigot start
import java.util.function.Supplier;
import org.spigotmc.SupplierUtils;
// Spigot end
/**
* Akarin Changes Note
* 1) Removes unneed synchronization (performance)
*/
public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
private ConcurrentLinkedQueue<QueuedChunk> queue = new ConcurrentLinkedQueue<>(); // Paper - Chunk queue improvements
private final Object lock = new Object(); // Paper - Chunk queue improvements
private static final Logger a = LogManager.getLogger();
private final Map<ChunkCoordIntPair, Supplier<NBTTagCompound>> b = Maps.newConcurrentMap(); // Spigot
// CraftBukkit
// private final Set<ChunkCoordIntPair> c = Collections.newSetFromMap(Maps.newConcurrentMap());
private final File d;
private final DataConverterManager e;
// private boolean f;
// CraftBukkit
private static final double SAVE_QUEUE_TARGET_SIZE = 625; // Spigot
public ChunkRegionLoader(File file, DataConverterManager dataconvertermanager) {
this.d = file;
this.e = dataconvertermanager;
}
// Paper start
private long queuedSaves = 0;
private final java.util.concurrent.atomic.AtomicLong processedSaves = new java.util.concurrent.atomic.AtomicLong(0L);
public int getQueueSize() { return queue.size(); }
public long getQueuedSaves() { return queuedSaves; }
public long getProcessedSaves() { return processedSaves.longValue(); }
// Paper end
// CraftBukkit start - Add async variant, provide compatibility
@Nullable
public Chunk a(World world, int i, int j) throws IOException {
world.timings.syncChunkLoadDataTimer.startTiming(); // Spigot
Object[] data = loadChunk(world, i, j);
world.timings.syncChunkLoadDataTimer.stopTiming(); // Spigot
if (data != null) {
Chunk chunk = (Chunk) data[0];
NBTTagCompound nbttagcompound = (NBTTagCompound) data[1];
loadEntities(chunk, nbttagcompound.getCompound("Level"), world);
return chunk;
}
return null;
}
public Object[] loadChunk(World world, int i, int j) throws IOException {
// CraftBukkit end
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j);
NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(this.b.get(chunkcoordintpair)); // Spigot
if (nbttagcompound == null) {
// CraftBukkit start
nbttagcompound = RegionFileCache.d(this.d, i, j);
if (nbttagcompound == null) {
return null;
}
nbttagcompound = this.e.a((DataConverterType) DataConverterTypes.CHUNK, nbttagcompound);
// CraftBukkit end
}
return this.a(world, i, j, nbttagcompound);
}
public boolean chunkExists(int i, int j) {
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j);
Supplier<NBTTagCompound> nbttagcompound = this.b.get(chunkcoordintpair); // Spigot
return nbttagcompound != null ? true : RegionFileCache.chunkExists(this.d, i, j);
}
@Nullable
protected Object[] a(World world, int i, int j, NBTTagCompound nbttagcompound) { // CraftBukkit - return Chunk -> Object[]
if (!nbttagcompound.hasKeyOfType("Level", 10)) {
ChunkRegionLoader.a.error("Chunk file at {},{} is missing level data, skipping", Integer.valueOf(i), Integer.valueOf(j));
return null;
} else {
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Level");
if (!nbttagcompound1.hasKeyOfType("Sections", 9)) {
ChunkRegionLoader.a.error("Chunk file at {},{} is missing block data, skipping", Integer.valueOf(i), Integer.valueOf(j));
return null;
} else {
Chunk chunk = this.a(world, nbttagcompound1);
if (!chunk.a(i, j)) {
ChunkRegionLoader.a.error("Chunk file at {},{} is in the wrong location; relocating. (Expected {}, {}, got {}, {})", Integer.valueOf(i), Integer.valueOf(j), Integer.valueOf(i), Integer.valueOf(j), Integer.valueOf(chunk.locX), Integer.valueOf(chunk.locZ));
nbttagcompound1.setInt("xPos", i);
nbttagcompound1.setInt("zPos", j);
// CraftBukkit start - Have to move tile entities since we don't load them at this stage
NBTTagList tileEntities = nbttagcompound.getCompound("Level").getList("TileEntities", 10);
if (tileEntities != null) {
for (int te = 0; te < tileEntities.size(); te++) {
NBTTagCompound tileEntity = (NBTTagCompound) tileEntities.get(te);
int x = tileEntity.getInt("x") - chunk.locX * 16;
int z = tileEntity.getInt("z") - chunk.locZ * 16;
tileEntity.setInt("x", i * 16 + x);
tileEntity.setInt("z", j * 16 + z);
}
}
// CraftBukkit end
chunk = this.a(world, nbttagcompound1);
}
// CraftBukkit start
Object[] data = new Object[2];
data[0] = chunk;
data[1] = nbttagcompound;
return data;
// CraftBukkit end
}
}
}
public void saveChunk(World world, Chunk chunk, boolean unloaded) throws IOException, ExceptionWorldConflict { // Spigot
world.checkSession();
try {
NBTTagCompound nbttagcompound = new NBTTagCompound();
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
nbttagcompound.set("Level", nbttagcompound1);
nbttagcompound.setInt("DataVersion", 1343);
// Spigot start
final long worldTime = world.getTime();
final boolean worldHasSkyLight = world.worldProvider.m();
saveEntities(nbttagcompound1, chunk, world);
Supplier<NBTTagCompound> completion = new Supplier<NBTTagCompound>() {
public NBTTagCompound get() {
saveBody(nbttagcompound1, chunk, worldTime, worldHasSkyLight);
return nbttagcompound;
}
};
this.a(chunk.k(), SupplierUtils.createUnivaluedSupplier(completion, unloaded && this.b.size() < SAVE_QUEUE_TARGET_SIZE));
// Spigot end
} catch (Exception exception) {
ChunkRegionLoader.a.error("Failed to save chunk", exception);
}
}
protected void a(ChunkCoordIntPair chunkcoordintpair, Supplier<NBTTagCompound> nbttagcompound) { // Spigot
// CraftBukkit
// if (!this.c.contains(chunkcoordintpair))
synchronized (lock) { // Paper - Chunk queue improvements
this.b.put(chunkcoordintpair, nbttagcompound);
}
queuedSaves++; // Paper
queue.add(new QueuedChunk(chunkcoordintpair, nbttagcompound)); // Paper - Chunk queue improvements
FileIOThread.a().a(this);
}
public boolean a() {
// CraftBukkit start
return this.processSaveQueueEntry(false);
}
private /*synchronized*/ boolean processSaveQueueEntry(boolean logCompletion) { // Akarin - remove synchronization
// CraftBukkit start
// Paper start - Chunk queue improvements
QueuedChunk chunk = queue.poll();
if (chunk == null) {
// Paper - end
if (logCompletion) {
// CraftBukkit end
ChunkRegionLoader.a.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", this.d.getName());
}
return false;
} else {
ChunkCoordIntPair chunkcoordintpair = chunk.coords; // Paper - Chunk queue improvements
processedSaves.incrementAndGet(); // Paper
boolean flag;
try {
// this.c.add(chunkcoordintpair);
NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(chunk.compoundSupplier); // Spigot // Paper
// CraftBukkit
if (nbttagcompound != null) {
int attempts = 0; Exception laste = null; while (attempts++ < 5) { // Paper
try {
this.b(chunkcoordintpair, nbttagcompound);
laste = null; break; // Paper
} catch (Exception exception) {
//ChunkRegionLoader.a.error("Failed to save chunk", exception); // Paper
laste = exception; // Paper
}
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();} } // Paper
if (laste != null) { com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(laste); MinecraftServer.LOGGER.error("Failed to save chunk", laste); } // Paper
}
synchronized (lock) { if (this.b.get(chunkcoordintpair) == chunk.compoundSupplier) { this.b.remove(chunkcoordintpair); } }// Paper - This will not equal if a newer version is still pending
flag = true;
} finally {
//this.b.remove(chunkcoordintpair, value); // CraftBukkit // Spigot // Paper
}
return flag;
}
}
private void b(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) throws IOException {
// CraftBukkit start
RegionFileCache.e(this.d, chunkcoordintpair.x, chunkcoordintpair.z, nbttagcompound);
/*
NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream);
dataoutputstream.close();
*/
// CraftBukkit end
}
public void b(World world, Chunk chunk) throws IOException {}
public void b() {}
public void c() {
try {
// this.f = true; // CraftBukkit
while (true) {
if (this.processSaveQueueEntry(true)) { // CraftBukkit
continue;
}
break; // CraftBukkit - Fix infinite loop when saving chunks
}
} finally {
// this.f = false; // CraftBukkit
}
}
public static void a(DataConverterManager dataconvertermanager) {
dataconvertermanager.a(DataConverterTypes.CHUNK, new DataInspector() {
public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) {
if (nbttagcompound.hasKeyOfType("Level", 10)) {
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Level");
NBTTagList nbttaglist;
int j;
if (nbttagcompound1.hasKeyOfType("Entities", 9)) {
nbttaglist = nbttagcompound1.getList("Entities", 10);
for (j = 0; j < nbttaglist.size(); ++j) {
nbttaglist.a(j, dataconverter.a(DataConverterTypes.ENTITY, (NBTTagCompound) nbttaglist.i(j), i));
}
}
if (nbttagcompound1.hasKeyOfType("TileEntities", 9)) {
nbttaglist = nbttagcompound1.getList("TileEntities", 10);
for (j = 0; j < nbttaglist.size(); ++j) {
nbttaglist.a(j, dataconverter.a(DataConverterTypes.BLOCK_ENTITY, (NBTTagCompound) nbttaglist.i(j), i));
}
}
}
return nbttagcompound;
}
});
}
private static void saveBody(NBTTagCompound nbttagcompound, Chunk chunk, long worldTime, boolean worldHasSkyLight) { // Spigot
nbttagcompound.setInt("xPos", chunk.locX);
nbttagcompound.setInt("zPos", chunk.locZ);
nbttagcompound.setLong("LastUpdate", worldTime); // Spigot
nbttagcompound.setIntArray("HeightMap", chunk.r());
nbttagcompound.setBoolean("TerrainPopulated", chunk.isDone());
nbttagcompound.setBoolean("LightPopulated", chunk.v());
nbttagcompound.setLong("InhabitedTime", chunk.x());
ChunkSection[] achunksection = chunk.getSections();
NBTTagList nbttaglist = new NBTTagList();
boolean flag = worldHasSkyLight; // Spigot
ChunkSection[] achunksection1 = achunksection;
int i = achunksection.length;
NBTTagCompound nbttagcompound1;
for (int j = 0; j < i; ++j) {
ChunkSection chunksection = achunksection1[j];
if (chunksection != Chunk.a) {
nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.setByte("Y", (byte) (chunksection.getYPosition() >> 4 & 255));
byte[] abyte = new byte[4096];
NibbleArray nibblearray = new NibbleArray();
NibbleArray nibblearray1 = chunksection.getBlocks().exportData(abyte, nibblearray);
nbttagcompound1.setByteArray("Blocks", abyte);
nbttagcompound1.setByteArray("Data", nibblearray.asBytes());
if (nibblearray1 != null) {
nbttagcompound1.setByteArray("Add", nibblearray1.asBytes());
}
nbttagcompound1.setByteArray("BlockLight", chunksection.getEmittedLightArray().asBytes());
if (flag) {
nbttagcompound1.setByteArray("SkyLight", chunksection.getSkyLightArray().asBytes());
} else {
nbttagcompound1.setByteArray("SkyLight", new byte[chunksection.getEmittedLightArray().asBytes().length]);
}
nbttaglist.add(nbttagcompound1);
}
}
nbttagcompound.set("Sections", nbttaglist);
nbttagcompound.setByteArray("Biomes", chunk.getBiomeIndex());
// Spigot start - End this method here and split off entity saving to another method
}
private static void saveEntities(NBTTagCompound nbttagcompound, Chunk chunk, World world) {
int i;
NBTTagCompound nbttagcompound1;
// Spigot end
chunk.g(false);
NBTTagList nbttaglist1 = new NBTTagList();
Iterator iterator;
List<Entity> toUpdate = new java.util.ArrayList<>(); // Paper
for (i = 0; i < chunk.getEntitySlices().length; ++i) {
iterator = chunk.getEntitySlices()[i].iterator();
while (iterator.hasNext()) {
Entity entity = (Entity) iterator.next();
// Paper start
if ((int)Math.floor(entity.locX) >> 4 != chunk.locX || (int)Math.floor(entity.locZ) >> 4 != chunk.locZ) {
LogManager.getLogger().warn(entity + " is not in this chunk, skipping save. This a bug fix to a vanilla bug. Do not report this to PaperMC please.");
toUpdate.add(entity);
continue;
}
if (entity.dead) {
continue;
}
// Paper end
nbttagcompound1 = new NBTTagCompound();
if (entity.d(nbttagcompound1)) {
chunk.g(true);
nbttaglist1.add(nbttagcompound1);
}
}
}
// Paper start - move entities to the correct chunk
for (Entity entity : toUpdate) {
world.entityJoinedWorld(entity, false);
}
// Paper end
nbttagcompound.set("Entities", nbttaglist1);
NBTTagList nbttaglist2 = new NBTTagList();
iterator = chunk.getTileEntities().values().iterator();
while (iterator.hasNext()) {
TileEntity tileentity = (TileEntity) iterator.next();
nbttagcompound1 = tileentity.save(new NBTTagCompound());
nbttaglist2.add(nbttagcompound1);
}
nbttagcompound.set("TileEntities", nbttaglist2);
List list = world.a(chunk, false);
if (list != null) {
long k = world.getTime();
NBTTagList nbttaglist3 = new NBTTagList();
Iterator iterator1 = list.iterator();
while (iterator1.hasNext()) {
NextTickListEntry nextticklistentry = (NextTickListEntry) iterator1.next();
NBTTagCompound nbttagcompound2 = new NBTTagCompound();
MinecraftKey minecraftkey = (MinecraftKey) Block.REGISTRY.b(nextticklistentry.a());
nbttagcompound2.setString("i", minecraftkey == null ? "" : minecraftkey.toString());
nbttagcompound2.setInt("x", nextticklistentry.a.getX());
nbttagcompound2.setInt("y", nextticklistentry.a.getY());
nbttagcompound2.setInt("z", nextticklistentry.a.getZ());
nbttagcompound2.setInt("t", (int) (nextticklistentry.b - k));
nbttagcompound2.setInt("p", nextticklistentry.c);
nbttaglist3.add(nbttagcompound2);
}
nbttagcompound.set("TileTicks", nbttaglist3);
}
}
private Chunk a(World world, NBTTagCompound nbttagcompound) {
int i = nbttagcompound.getInt("xPos");
int j = nbttagcompound.getInt("zPos");
Chunk chunk = new Chunk(world, i, j);
chunk.a(nbttagcompound.getIntArray("HeightMap"));
chunk.d(nbttagcompound.getBoolean("TerrainPopulated"));
chunk.e(nbttagcompound.getBoolean("LightPopulated"));
chunk.c(nbttagcompound.getLong("InhabitedTime"));
NBTTagList nbttaglist = nbttagcompound.getList("Sections", 10);
boolean flag = true;
ChunkSection[] achunksection = new ChunkSection[16];
boolean flag1 = world.worldProvider.m();
for (int k = 0; k < nbttaglist.size(); ++k) {
NBTTagCompound nbttagcompound1 = nbttaglist.get(k);
byte b0 = nbttagcompound1.getByte("Y");
ChunkSection chunksection = new ChunkSection(b0 << 4, flag1, world.chunkPacketBlockController.getPredefinedBlockData(chunk, b0)); // Paper - Anti-Xray - Add predefined block data
byte[] abyte = nbttagcompound1.getByteArray("Blocks");
NibbleArray nibblearray = new NibbleArray(nbttagcompound1.getByteArray("Data"));
NibbleArray nibblearray1 = nbttagcompound1.hasKeyOfType("Add", 7) ? new NibbleArray(nbttagcompound1.getByteArray("Add")) : null;
chunksection.getBlocks().a(abyte, nibblearray, nibblearray1);
chunksection.a(new NibbleArray(nbttagcompound1.getByteArray("BlockLight")));
if (flag1) {
chunksection.b(new NibbleArray(nbttagcompound1.getByteArray("SkyLight")));
}
chunksection.recalcBlockCounts();
achunksection[b0] = chunksection;
}
chunk.a(achunksection);
if (nbttagcompound.hasKeyOfType("Biomes", 7)) {
chunk.a(nbttagcompound.getByteArray("Biomes"));
}
// CraftBukkit start - End this method here and split off entity loading to another method
return chunk;
}
public void loadEntities(Chunk chunk, NBTTagCompound nbttagcompound, World world) {
// CraftBukkit end
world.timings.syncChunkLoadNBTTimer.startTiming(); // Spigot
NBTTagList nbttaglist1 = nbttagcompound.getList("Entities", 10);
for (int l = 0; l < nbttaglist1.size(); ++l) {
NBTTagCompound nbttagcompound2 = nbttaglist1.get(l);
a(nbttagcompound2, world, chunk);
chunk.g(true);
}
NBTTagList nbttaglist2 = nbttagcompound.getList("TileEntities", 10);
for (int i1 = 0; i1 < nbttaglist2.size(); ++i1) {
NBTTagCompound nbttagcompound3 = nbttaglist2.get(i1);
TileEntity tileentity = TileEntity.create(world, nbttagcompound3);
if (tileentity != null) {
chunk.a(tileentity);
}
}
if (nbttagcompound.hasKeyOfType("TileTicks", 9)) {
NBTTagList nbttaglist3 = nbttagcompound.getList("TileTicks", 10);
for (int j1 = 0; j1 < nbttaglist3.size(); ++j1) {
NBTTagCompound nbttagcompound4 = nbttaglist3.get(j1);
Block block;
if (nbttagcompound4.hasKeyOfType("i", 8)) {
block = Block.getByName(nbttagcompound4.getString("i"));
} else {
block = Block.getById(nbttagcompound4.getInt("i"));
}
world.b(new BlockPosition(nbttagcompound4.getInt("x"), nbttagcompound4.getInt("y"), nbttagcompound4.getInt("z")), block, nbttagcompound4.getInt("t"), nbttagcompound4.getInt("p"));
}
}
world.timings.syncChunkLoadNBTTimer.stopTiming(); // Spigot
// return chunk; // CraftBukkit
}
@Nullable
public static Entity a(NBTTagCompound nbttagcompound, World world, Chunk chunk) {
Entity entity = a(nbttagcompound, world);
if (entity == null) {
return null;
} else {
chunk.a(entity);
if (nbttagcompound.hasKeyOfType("Passengers", 9)) {
NBTTagList nbttaglist = nbttagcompound.getList("Passengers", 10);
for (int i = 0; i < nbttaglist.size(); ++i) {
Entity entity1 = a(nbttaglist.get(i), world, chunk);
if (entity1 != null) {
entity1.a(entity, true);
}
}
}
return entity;
}
}
@Nullable
// CraftBukkit start
public static Entity a(NBTTagCompound nbttagcompound, World world, double d0, double d1, double d2, boolean flag) {
return spawnEntity(nbttagcompound, world, d0, d1, d2, flag, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
}
public static Entity spawnEntity(NBTTagCompound nbttagcompound, World world, double d0, double d1, double d2, boolean flag, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {
// CraftBukkit end
Entity entity = a(nbttagcompound, world);
if (entity == null) {
return null;
} else {
entity.setPositionRotation(d0, d1, d2, entity.yaw, entity.pitch);
if (flag && !world.addEntity(entity, spawnReason)) { // CraftBukkit
return null;
} else {
if (nbttagcompound.hasKeyOfType("Passengers", 9)) {
NBTTagList nbttaglist = nbttagcompound.getList("Passengers", 10);
for (int i = 0; i < nbttaglist.size(); ++i) {
Entity entity1 = a(nbttaglist.get(i), world, d0, d1, d2, flag);
if (entity1 != null) {
entity1.a(entity, true);
}
}
}
return entity;
}
}
}
@Nullable
protected static Entity a(NBTTagCompound nbttagcompound, World world) {
try {
return EntityTypes.a(nbttagcompound, world);
} catch (RuntimeException runtimeexception) {
return null;
}
}
// CraftBukkit start
public static void a(Entity entity, World world) {
a(entity, world, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
}
public static void a(Entity entity, World world, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
if (!entity.valid && world.addEntity(entity, reason) && entity.isVehicle()) { // Paper
// CraftBukkit end
Iterator iterator = entity.bF().iterator();
while (iterator.hasNext()) {
Entity entity1 = (Entity) iterator.next();
a(entity1, world);
}
}
}
@Nullable
public static Entity a(NBTTagCompound nbttagcompound, World world, boolean flag) {
Entity entity = a(nbttagcompound, world);
if (entity == null) {
return null;
} else if (flag && !world.addEntity(entity)) {
return null;
} else {
if (nbttagcompound.hasKeyOfType("Passengers", 9)) {
NBTTagList nbttaglist = nbttagcompound.getList("Passengers", 10);
for (int i = 0; i < nbttaglist.size(); ++i) {
Entity entity1 = a(nbttaglist.get(i), world, flag);
if (entity1 != null) {
entity1.a(entity, true);
}
}
}
return entity;
}
}
// Paper start - Chunk queue improvements
private static class QueuedChunk {
public ChunkCoordIntPair coords;
public Supplier<NBTTagCompound> compoundSupplier;
public QueuedChunk(ChunkCoordIntPair coords, Supplier<NBTTagCompound> compoundSupplier) {
this.coords = coords;
this.compoundSupplier = compoundSupplier;
}
}
// Paper end
}

View File

@@ -1,432 +0,0 @@
package net.minecraft.server;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Map.Entry;
/**
* Akarin Changes Note
* 1) Expose private members (cause mixin errors)
*/
public class EnchantmentManager {
private static final EnchantmentManager.EnchantmentModifierProtection a = new EnchantmentManager.EnchantmentModifierProtection(null);
private static final EnchantmentManager.EnchantmentModifierDamage b = new EnchantmentManager.EnchantmentModifierDamage(null);
private static final EnchantmentManager.EnchantmentModifierThorns c = new EnchantmentManager.EnchantmentModifierThorns(null);
private static final EnchantmentManager.EnchantmentModifierArthropods d = new EnchantmentManager.EnchantmentModifierArthropods(null);
public static int getEnchantmentLevel(Enchantment enchantment, ItemStack itemstack) {
if (itemstack.isEmpty()) {
return 0;
} else {
NBTTagList nbttaglist = itemstack.getEnchantments();
for (int i = 0; i < nbttaglist.size(); ++i) {
NBTTagCompound nbttagcompound = nbttaglist.get(i);
Enchantment enchantment1 = Enchantment.c(nbttagcompound.getShort("id"));
short short0 = nbttagcompound.getShort("lvl");
if (enchantment1 == enchantment) {
return short0;
}
}
return 0;
}
}
public static Map<Enchantment, Integer> a(ItemStack itemstack) {
LinkedHashMap linkedhashmap = Maps.newLinkedHashMap();
NBTTagList nbttaglist = itemstack.getItem() == Items.ENCHANTED_BOOK ? ItemEnchantedBook.h(itemstack) : itemstack.getEnchantments();
for (int i = 0; i < nbttaglist.size(); ++i) {
NBTTagCompound nbttagcompound = nbttaglist.get(i);
Enchantment enchantment = Enchantment.c(nbttagcompound.getShort("id"));
short short0 = nbttagcompound.getShort("lvl");
linkedhashmap.put(enchantment, Integer.valueOf(short0));
}
return linkedhashmap;
}
public static void a(Map<Enchantment, Integer> map, ItemStack itemstack) {
NBTTagList nbttaglist = new NBTTagList();
Iterator iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Entry entry = (Entry) iterator.next();
Enchantment enchantment = (Enchantment) entry.getKey();
if (enchantment != null) {
int i = ((Integer) entry.getValue()).intValue();
NBTTagCompound nbttagcompound = new NBTTagCompound();
nbttagcompound.setShort("id", (short) Enchantment.getId(enchantment));
nbttagcompound.setShort("lvl", (short) i);
nbttaglist.add(nbttagcompound);
if (itemstack.getItem() == Items.ENCHANTED_BOOK) {
ItemEnchantedBook.a(itemstack, new WeightedRandomEnchant(enchantment, i));
}
}
}
if (nbttaglist.isEmpty()) {
if (itemstack.hasTag()) {
itemstack.getTag().remove("ench");
}
} else if (itemstack.getItem() != Items.ENCHANTED_BOOK) {
itemstack.a("ench", nbttaglist);
}
}
private static void a(EnchantmentManager.EnchantmentModifier enchantmentmanager_enchantmentmodifier, ItemStack itemstack) {
if (!itemstack.isEmpty()) {
NBTTagList nbttaglist = itemstack.getEnchantments();
for (int i = 0; i < nbttaglist.size(); ++i) {
short short0 = nbttaglist.get(i).getShort("id");
short short1 = nbttaglist.get(i).getShort("lvl");
if (Enchantment.c(short0) != null) {
enchantmentmanager_enchantmentmodifier.a(Enchantment.c(short0), short1);
}
}
}
}
private static void a(EnchantmentManager.EnchantmentModifier enchantmentmanager_enchantmentmodifier, Iterable<ItemStack> iterable) {
Iterator iterator = iterable.iterator();
while (iterator.hasNext()) {
ItemStack itemstack = (ItemStack) iterator.next();
a(enchantmentmanager_enchantmentmodifier, itemstack);
}
}
public static int a(Iterable<ItemStack> iterable, DamageSource damagesource) {
EnchantmentManager.a.a = 0;
EnchantmentManager.a.b = damagesource;
a(EnchantmentManager.a, iterable);
return EnchantmentManager.a.a;
}
public static float a(ItemStack itemstack, EnumMonsterType enummonstertype) {
EnchantmentManager.b.a = 0.0F;
EnchantmentManager.b.b = enummonstertype;
a(EnchantmentManager.b, itemstack);
return EnchantmentManager.b.a;
}
public static float a(EntityLiving entityliving) {
int i = a(Enchantments.r, entityliving);
return i > 0 ? EnchantmentSweeping.e(i) : 0.0F;
}
public static void a(EntityLiving entityliving, Entity entity) {
EnchantmentManager.c.b = entity;
EnchantmentManager.c.a = entityliving;
if (entityliving != null) {
a(EnchantmentManager.c, entityliving.aQ());
}
if (entity instanceof EntityHuman) {
a(EnchantmentManager.c, entityliving.getItemInMainHand());
}
}
public static void b(EntityLiving entityliving, Entity entity) {
EnchantmentManager.d.a = entityliving;
EnchantmentManager.d.b = entity;
if (entityliving != null) {
a(EnchantmentManager.d, entityliving.aQ());
}
if (entityliving instanceof EntityHuman) {
a(EnchantmentManager.d, entityliving.getItemInMainHand());
}
}
public static int a(Enchantment enchantment, EntityLiving entityliving) {
List list = enchantment.a(entityliving);
if (list == null) {
return 0;
} else {
int i = 0;
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
ItemStack itemstack = (ItemStack) iterator.next();
int j = getEnchantmentLevel(enchantment, itemstack);
if (j > i) {
i = j;
}
}
return i;
}
}
public static int b(EntityLiving entityliving) {
return a(Enchantments.KNOCKBACK, entityliving);
}
public static int getFireAspectEnchantmentLevel(EntityLiving entityliving) {
return a(Enchantments.FIRE_ASPECT, entityliving);
}
public static int getOxygenEnchantmentLevel(EntityLiving entityliving) {
return a(Enchantments.OXYGEN, entityliving);
}
public static int e(EntityLiving entityliving) {
return a(Enchantments.DEPTH_STRIDER, entityliving);
}
public static int getDigSpeedEnchantmentLevel(EntityLiving entityliving) {
return a(Enchantments.DIG_SPEED, entityliving);
}
public static int b(ItemStack itemstack) {
return getEnchantmentLevel(Enchantments.LUCK, itemstack);
}
public static int c(ItemStack itemstack) {
return getEnchantmentLevel(Enchantments.LURE, itemstack);
}
public static int g(EntityLiving entityliving) {
return a(Enchantments.LOOT_BONUS_MOBS, entityliving);
}
public static boolean h(EntityLiving entityliving) {
return a(Enchantments.WATER_WORKER, entityliving) > 0;
}
public static boolean i(EntityLiving entityliving) {
return a(Enchantments.j, entityliving) > 0;
}
public static boolean d(ItemStack itemstack) {
return getEnchantmentLevel(Enchantments.k, itemstack) > 0;
}
public static boolean shouldNotDrop(ItemStack itemstack) {
return getEnchantmentLevel(Enchantments.D, itemstack) > 0;
}
public static ItemStack getRandomEquippedItemWithEnchant(Enchantment enchantment, EntityLiving entityliving) { return b(enchantment, entityliving); } // Paper - OBFHELPER
public static ItemStack b(Enchantment enchantment, EntityLiving entityliving) {
List list = enchantment.a(entityliving);
if (list.isEmpty()) {
return ItemStack.a;
} else {
ArrayList arraylist = Lists.newArrayList();
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
ItemStack itemstack = (ItemStack) iterator.next();
if (!itemstack.isEmpty() && getEnchantmentLevel(enchantment, itemstack) > 0) {
arraylist.add(itemstack);
}
}
return arraylist.isEmpty() ? ItemStack.a : (ItemStack) arraylist.get(entityliving.getRandom().nextInt(arraylist.size()));
}
}
public static int a(Random random, int i, int j, ItemStack itemstack) {
Item item = itemstack.getItem();
int k = item.c();
if (k <= 0) {
return 0;
} else {
if (j > 15) {
j = 15;
}
int l = random.nextInt(8) + 1 + (j >> 1) + random.nextInt(j + 1);
return i == 0 ? Math.max(l / 3, 1) : (i == 1 ? l * 2 / 3 + 1 : Math.max(l, j * 2));
}
}
public static ItemStack a(Random random, ItemStack itemstack, int i, boolean flag) {
List list = b(random, itemstack, i, flag);
boolean flag1 = itemstack.getItem() == Items.BOOK;
if (flag1) {
itemstack = new ItemStack(Items.ENCHANTED_BOOK);
}
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
WeightedRandomEnchant weightedrandomenchant = (WeightedRandomEnchant) iterator.next();
if (flag1) {
ItemEnchantedBook.a(itemstack, weightedrandomenchant);
} else {
itemstack.addEnchantment(weightedrandomenchant.enchantment, weightedrandomenchant.level);
}
}
return itemstack;
}
public static List<WeightedRandomEnchant> b(Random random, ItemStack itemstack, int i, boolean flag) {
ArrayList arraylist = Lists.newArrayList();
Item item = itemstack.getItem();
int j = item.c();
if (j <= 0) {
return arraylist;
} else {
i += 1 + random.nextInt(j / 4 + 1) + random.nextInt(j / 4 + 1);
float f = (random.nextFloat() + random.nextFloat() - 1.0F) * 0.15F;
i = MathHelper.clamp(Math.round(i + i * f), 1, Integer.MAX_VALUE);
List list = a(i, itemstack, flag);
if (!list.isEmpty()) {
arraylist.add(WeightedRandom.a(random, list));
while (random.nextInt(50) <= i) {
a(list, (WeightedRandomEnchant) SystemUtils.a(arraylist));
if (list.isEmpty()) {
break;
}
arraylist.add(WeightedRandom.a(random, list));
i /= 2;
}
}
return arraylist;
}
}
public static void a(List<WeightedRandomEnchant> list, WeightedRandomEnchant weightedrandomenchant) {
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
if (!weightedrandomenchant.enchantment.c(((WeightedRandomEnchant) iterator.next()).enchantment)) {
iterator.remove();
}
}
}
public static List<WeightedRandomEnchant> a(int i, ItemStack itemstack, boolean flag) {
ArrayList arraylist = Lists.newArrayList();
Item item = itemstack.getItem();
boolean flag1 = itemstack.getItem() == Items.BOOK;
Iterator iterator = Enchantment.enchantments.iterator();
while (iterator.hasNext()) {
Enchantment enchantment = (Enchantment) iterator.next();
if ((!enchantment.isTreasure() || flag) && (enchantment.itemTarget.canEnchant(item) || flag1)) {
for (int j = enchantment.getMaxLevel(); j > enchantment.getStartLevel() - 1; --j) {
if (i >= enchantment.a(j) && i <= enchantment.b(j)) {
arraylist.add(new WeightedRandomEnchant(enchantment, j));
break;
}
}
}
}
return arraylist;
}
public static final class EnchantmentModifierArthropods implements EnchantmentManager.EnchantmentModifier { // Akarin - private -> public
public EntityLiving a;
public Entity b;
private EnchantmentModifierArthropods() {}
@Override
public void a(Enchantment enchantment, int i) {
enchantment.a(this.a, this.b, i);
}
EnchantmentModifierArthropods(Object object) {
this();
}
}
public static final class EnchantmentModifierThorns implements EnchantmentManager.EnchantmentModifier { // Akarin - private -> public
public EntityLiving a;
public Entity b;
private EnchantmentModifierThorns() {}
@Override
public void a(Enchantment enchantment, int i) {
enchantment.b(this.a, this.b, i);
}
EnchantmentModifierThorns(Object object) {
this();
}
}
static final class EnchantmentModifierDamage implements EnchantmentManager.EnchantmentModifier {
public float a;
public EnumMonsterType b;
private EnchantmentModifierDamage() {}
@Override
public void a(Enchantment enchantment, int i) {
this.a += enchantment.a(i, this.b);
}
EnchantmentModifierDamage(Object object) {
this();
}
}
public static final class EnchantmentModifierProtection implements EnchantmentManager.EnchantmentModifier { // Akarin - private -> public
public int a;
public DamageSource b;
private EnchantmentModifierProtection() {}
@Override
public void a(Enchantment enchantment, int i) {
this.a += enchantment.a(i, this.b);
}
EnchantmentModifierProtection(Object object) {
this();
}
}
public interface EnchantmentModifier { // Akarin - private -> public
void a(Enchantment enchantment, int i);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@ package net.minecraft.server;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import java.util.ArrayList;
import java.util.Iterator;
@@ -15,15 +15,13 @@ import org.apache.logging.log4j.Logger;
/**
* Akarin Changes Note
* 1) Made collections and entry access thread-safe (safety issue)
* 1) Made collections and entry accesses thread-safe (safety issue)
*/
@ThreadSafe // Akarin
public class EntityTracker {
private static final Logger a = LogManager.getLogger();
private final WorldServer world;
private final Set<EntityTrackerEntry> c = Sets.newHashSet();
public final ReentrantReadWriteUpdateLock entriesLock = new ReentrantReadWriteUpdateLock(); // Akarin - add lock
public final IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap();
private int e;
@@ -117,9 +115,9 @@ public class EntityTracker {
this.addEntity(entity, i, j, false);
}
public void addEntity(Entity entity, int i, final int j, boolean flag) {
public void addEntity(Entity entity, int originalRange, int j, boolean flag) { // Spigot
org.spigotmc.AsyncCatcher.catchOp( "entity track"); // Spigot
i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot
int i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, originalRange); // Spigot
try {
// entriesLock.writeLock().lock(); // Akarin - locked from track method
if (this.trackedEntities.b(entity.getId())) {
@@ -129,7 +127,6 @@ public class EntityTracker {
EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, this.e, j, flag);
this.c.add(entitytrackerentry);
this.trackedEntities.a(entity.getId(), entitytrackerentry);
entitytrackerentry.scanPlayers(this.world.players);
// entriesLock.writeLock().unlock(); // Akarin - locked from track method
@@ -137,26 +134,18 @@ public class EntityTracker {
CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track");
crashreportsystemdetails.a("Tracking range", i + " blocks");
final int finalI = i; // CraftBukkit - fix decompile error
crashreportsystemdetails.a("Update interval", new CrashReportCallable() {
public String a() throws Exception {
String s = "Once per " + finalI + " ticks"; // CraftBukkit
crashreportsystemdetails.a("Tracking range", (Object) (i + " blocks"));
crashreportsystemdetails.a("Update interval", () -> {
String s = "Once per " + i + " ticks";
if (finalI == Integer.MAX_VALUE) { // CraftBukkit
if (i == Integer.MAX_VALUE) {
s = "Maximum (" + s + ")";
}
return s;
}
@Override
public Object call() throws Exception {
return this.a();
}
});
entity.appendEntityCrashDetails(crashreportsystemdetails);
this.trackedEntities.get(entity.getId()).b().appendEntityCrashDetails(crashreport.a("Entity That Is Already Tracked"));
((EntityTrackerEntry) this.trackedEntities.get(entity.getId())).b().appendEntityCrashDetails(crashreport.a("Entity That Is Already Tracked"));
try {
throw new ReportedException(crashreport);
@@ -169,7 +158,7 @@ public class EntityTracker {
public void untrackEntity(Entity entity) {
org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot
entriesLock.writeLock().lock(); // Akarin
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
if (entity instanceof EntityPlayer) {
EntityPlayer entityplayer = (EntityPlayer) entity;
Iterator iterator = this.c.iterator();
@@ -181,20 +170,21 @@ public class EntityTracker {
}
}
EntityTrackerEntry entitytrackerentry1 = this.trackedEntities.d(entity.getId());
EntityTrackerEntry entitytrackerentry1 = (EntityTrackerEntry) this.trackedEntities.d(entity.getId());
if (entitytrackerentry1 != null) {
this.c.remove(entitytrackerentry1);
entitytrackerentry1.a();
}
entriesLock.writeLock().unlock(); // Akarin
} // Akarin
}
public void updatePlayers() {
ArrayList arraylist = Lists.newArrayList();
Iterator iterator = this.c.iterator();
world.timings.tracker1.startTiming(); // Spigot
entriesLock.writeLock().lock(); // Akarin
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -203,7 +193,7 @@ public class EntityTracker {
Entity entity = entitytrackerentry.b();
if (entity instanceof EntityPlayer) {
arraylist.add(entity);
arraylist.add((EntityPlayer) entity);
}
}
}
@@ -222,14 +212,15 @@ public class EntityTracker {
}
}
}
entriesLock.writeLock().unlock(); // Akarin
} // Akarin
world.timings.tracker2.stopTiming(); // Spigot
}
public void updatePlayer(EntityPlayer entityplayer) { a(entityplayer); } // Paper - OBFHELPER
public void a(EntityPlayer entityplayer) {
Iterator iterator = this.c.iterator();
entriesLock.writeLock().lock(); // Akarin
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -240,13 +231,15 @@ public class EntityTracker {
entitytrackerentry.updatePlayer(entityplayer);
}
}
entriesLock.writeLock().unlock(); // Akarin
} // Akarin
}
public void a(Entity entity, Packet<?> packet) {
entriesLock.readLock().lock(); // Akarin
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
entriesLock.readLock().unlock(); // Akarin
EntityTrackerEntry entitytrackerentry; // Akarin
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
entitytrackerentry = (EntityTrackerEntry) this.trackedEntities.get(entity.getId());
} // Akarin
if (entitytrackerentry != null) {
entitytrackerentry.broadcast(packet);
@@ -255,9 +248,10 @@ public class EntityTracker {
}
public void sendPacketToEntity(Entity entity, Packet<?> packet) {
entriesLock.readLock().lock(); // Akarin
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
entriesLock.readLock().unlock(); // Akarin
EntityTrackerEntry entitytrackerentry; // Akarin
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
entitytrackerentry = (EntityTrackerEntry) this.trackedEntities.get(entity.getId());
} // Akarin
if (entitytrackerentry != null) {
entitytrackerentry.broadcastIncludingSelf(packet);
@@ -267,38 +261,39 @@ public class EntityTracker {
public void untrackPlayer(EntityPlayer entityplayer) {
Iterator iterator = this.c.iterator();
entriesLock.writeLock().lock();
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
entitytrackerentry.clear(entityplayer);
}
entriesLock.writeLock().unlock();
} // Akarin
}
public void a(EntityPlayer entityplayer, Chunk chunk) {
ArrayList arraylist = Lists.newArrayList();
ArrayList arraylist1 = Lists.newArrayList();
Iterator iterator = this.c.iterator();
entriesLock.writeLock().lock(); // Akarin
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
Entity entity = entitytrackerentry.b();
if (entity != entityplayer && entity.ab == chunk.locX && entity.ad == chunk.locZ) {
if (entity != entityplayer && entity.ae == chunk.locX && entity.ag == chunk.locZ) {
entitytrackerentry.updatePlayer(entityplayer);
if (entity instanceof EntityInsentient && ((EntityInsentient) entity).getLeashHolder() != null) {
arraylist.add(entity);
}
if (!entity.bF().isEmpty()) {
if (!entity.bP().isEmpty()) {
arraylist1.add(entity);
}
}
}
entriesLock.writeLock().unlock(); // Akarin
} // Akarin
Entity entity1;
@@ -325,13 +320,14 @@ public class EntityTracker {
public void a(int i) {
this.e = (i - 1) * 16;
Iterator iterator = this.c.iterator();
entriesLock.readLock().lock(); // Akarin
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
entitytrackerentry.a(this.e);
}
entriesLock.readLock().unlock(); // Akarin
} // Akarin
}
}

View File

@@ -1,317 +0,0 @@
package net.minecraft.server;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import com.google.common.collect.Maps;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import javax.annotation.Nullable;
/**
* Akarin Changes Note
* 1) Add OBFHELPER (panda redstone)
*/
public enum EnumDirection implements INamable {
DOWN(0, 1, -1, "down", EnumDirection.EnumAxisDirection.NEGATIVE, EnumDirection.EnumAxis.Y, new BaseBlockPosition(0, -1, 0)), UP(1, 0, -1, "up", EnumDirection.EnumAxisDirection.POSITIVE, EnumDirection.EnumAxis.Y, new BaseBlockPosition(0, 1, 0)), NORTH(2, 3, 2, "north", EnumDirection.EnumAxisDirection.NEGATIVE, EnumDirection.EnumAxis.Z, new BaseBlockPosition(0, 0, -1)), SOUTH(3, 2, 0, "south", EnumDirection.EnumAxisDirection.POSITIVE, EnumDirection.EnumAxis.Z, new BaseBlockPosition(0, 0, 1)), WEST(4, 5, 1, "west", EnumDirection.EnumAxisDirection.NEGATIVE, EnumDirection.EnumAxis.X, new BaseBlockPosition(-1, 0, 0)), EAST(5, 4, 3, "east", EnumDirection.EnumAxisDirection.POSITIVE, EnumDirection.EnumAxis.X, new BaseBlockPosition(1, 0, 0));
private final int g;
private final int h;
private final int i;
private final String j;
private final EnumDirection.EnumAxis k;
private final EnumDirection.EnumAxisDirection l;
private final BaseBlockPosition m; public BaseBlockPosition getDirectionPosition() { return m; } // Akarin - OBFHELPER
private static final EnumDirection[] n = new EnumDirection[6];
private static final EnumDirection[] o = new EnumDirection[4];
private static final Map<String, EnumDirection> p = Maps.newHashMap();
private EnumDirection(int i, int j, int k, String s, EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection, EnumDirection.EnumAxis enumdirection_enumaxis, BaseBlockPosition baseblockposition) {
this.g = i;
this.i = k;
this.h = j;
this.j = s;
this.k = enumdirection_enumaxis;
this.l = enumdirection_enumaxisdirection;
this.m = baseblockposition;
}
public int a() {
return this.g;
}
public int get2DRotationValue() {
return this.i;
}
public EnumDirection.EnumAxisDirection c() {
return this.l;
}
public EnumDirection opposite() {
return fromType1(this.h);
}
public EnumDirection e() {
switch (this) {
case NORTH:
return EnumDirection.EAST;
case EAST:
return EnumDirection.SOUTH;
case SOUTH:
return EnumDirection.WEST;
case WEST:
return EnumDirection.NORTH;
default:
throw new IllegalStateException("Unable to get Y-rotated facing of " + this);
}
}
public EnumDirection f() {
switch (this) {
case NORTH:
return EnumDirection.WEST;
case EAST:
return EnumDirection.NORTH;
case SOUTH:
return EnumDirection.EAST;
case WEST:
return EnumDirection.SOUTH;
default:
throw new IllegalStateException("Unable to get CCW facing of " + this);
}
}
public int getAdjacentX() {
return this.k == EnumDirection.EnumAxis.X ? this.l.a() : 0;
}
public int getAdjacentY() {
return this.k == EnumDirection.EnumAxis.Y ? this.l.a() : 0;
}
public int getAdjacentZ() {
return this.k == EnumDirection.EnumAxis.Z ? this.l.a() : 0;
}
public String j() {
return this.j;
}
public EnumDirection.EnumAxis getAxis() { return k(); } // Akarin - OBFHELPER
public EnumDirection.EnumAxis k() {
return this.k;
}
public static EnumDirection fromType1(int i) {
return EnumDirection.n[MathHelper.a(i % EnumDirection.n.length)];
}
public static EnumDirection fromType2(int i) {
return EnumDirection.o[MathHelper.a(i % EnumDirection.o.length)];
}
public static EnumDirection fromAngle(double d0) {
return fromType2(MathHelper.floor(d0 / 90.0D + 0.5D) & 3);
}
public float l() {
return (this.i & 3) * 90;
}
public static EnumDirection a(Random random) {
return values()[random.nextInt(values().length)];
}
@Override
public String toString() {
return this.j;
}
@Override
public String getName() {
return this.j;
}
public static EnumDirection a(EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection, EnumDirection.EnumAxis enumdirection_enumaxis) {
EnumDirection[] aenumdirection = values();
int i = aenumdirection.length;
for (int j = 0; j < i; ++j) {
EnumDirection enumdirection = aenumdirection[j];
if (enumdirection.c() == enumdirection_enumaxisdirection && enumdirection.k() == enumdirection_enumaxis) {
return enumdirection;
}
}
throw new IllegalArgumentException("No such direction: " + enumdirection_enumaxisdirection + " " + enumdirection_enumaxis);
}
public static EnumDirection a(BlockPosition blockposition, EntityLiving entityliving) {
if (Math.abs(entityliving.locX - (blockposition.getX() + 0.5F)) < 2.0D && Math.abs(entityliving.locZ - (blockposition.getZ() + 0.5F)) < 2.0D) {
double d0 = entityliving.locY + entityliving.getHeadHeight();
if (d0 - blockposition.getY() > 2.0D) {
return EnumDirection.UP;
}
if (blockposition.getY() - d0 > 0.0D) {
return EnumDirection.DOWN;
}
}
return entityliving.getDirection().opposite();
}
static {
EnumDirection[] aenumdirection = values();
int i = aenumdirection.length;
for (int j = 0; j < i; ++j) {
EnumDirection enumdirection = aenumdirection[j];
EnumDirection.n[enumdirection.g] = enumdirection;
if (enumdirection.k().c()) {
EnumDirection.o[enumdirection.i] = enumdirection;
}
EnumDirection.p.put(enumdirection.j().toLowerCase(Locale.ROOT), enumdirection);
}
}
public static enum EnumDirectionLimit implements Predicate<EnumDirection>, Iterable<EnumDirection> {
HORIZONTAL, VERTICAL;
private EnumDirectionLimit() {}
public EnumDirection[] a() {
switch (this) {
case HORIZONTAL:
return new EnumDirection[] { EnumDirection.NORTH, EnumDirection.EAST, EnumDirection.SOUTH, EnumDirection.WEST};
case VERTICAL:
return new EnumDirection[] { EnumDirection.UP, EnumDirection.DOWN};
default:
throw new Error("Someone\'s been tampering with the universe!");
}
}
public EnumDirection a(Random random) {
EnumDirection[] aenumdirection = this.a();
return aenumdirection[random.nextInt(aenumdirection.length)];
}
public boolean a(@Nullable EnumDirection enumdirection) {
return enumdirection != null && enumdirection.k().d() == this;
}
@Override
public Iterator<EnumDirection> iterator() {
return Iterators.forArray(this.a());
}
@Override
public boolean apply(@Nullable EnumDirection object) {
return this.a(object);
}
}
public static enum EnumAxisDirection {
POSITIVE(1, "Towards positive"), NEGATIVE(-1, "Towards negative");
private final int c;
private final String d;
private EnumAxisDirection(int i, String s) {
this.c = i;
this.d = s;
}
public int a() {
return this.c;
}
@Override
public String toString() {
return this.d;
}
}
public static enum EnumAxis implements Predicate<EnumDirection>, INamable {
X("x", EnumDirection.EnumDirectionLimit.HORIZONTAL), Y("y", EnumDirection.EnumDirectionLimit.VERTICAL), Z("z", EnumDirection.EnumDirectionLimit.HORIZONTAL);
private static final Map<String, EnumDirection.EnumAxis> d = Maps.newHashMap();
private final String e;
private final EnumDirection.EnumDirectionLimit f;
private EnumAxis(String s, EnumDirection.EnumDirectionLimit enumdirection_enumdirectionlimit) {
this.e = s;
this.f = enumdirection_enumdirectionlimit;
}
public String a() {
return this.e;
}
public boolean b() {
return this.f == EnumDirection.EnumDirectionLimit.VERTICAL;
}
public boolean isHorizontal() { return c(); } // Akarin - OBFHELPER
public boolean c() {
return this.f == EnumDirection.EnumDirectionLimit.HORIZONTAL;
}
@Override
public String toString() {
return this.e;
}
public boolean a(@Nullable EnumDirection enumdirection) {
return enumdirection != null && enumdirection.k() == this;
}
public EnumDirection.EnumDirectionLimit d() {
return this.f;
}
@Override
public String getName() {
return this.e;
}
@Override
public boolean apply(@Nullable EnumDirection object) {
return this.a(object);
}
static {
EnumDirection.EnumAxis[] aenumdirection_enumaxis = values();
int i = aenumdirection_enumaxis.length;
for (int j = 0; j < i; ++j) {
EnumDirection.EnumAxis enumdirection_enumaxis = aenumdirection_enumaxis[j];
EnumDirection.EnumAxis.d.put(enumdirection_enumaxis.a().toLowerCase(Locale.ROOT), enumdirection_enumaxis);
}
}
}
}

View File

@@ -1,6 +1,8 @@
package net.minecraft.server;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Akarin Changes Note
@@ -8,21 +10,23 @@ import java.util.List;
*/
public class FileIOThread implements Runnable {
private static final FileIOThread a = new FileIOThread();
private final List<IAsyncChunkSaver> b = /*Collections.synchronizedList(Lists.newArrayList())*/ null; // Akarin - I don't think any plugin rely on this
private volatile long c;
private static final Logger a = LogManager.getLogger();
private static final FileIOThread b = new FileIOThread();
private final List<IAsyncChunkSaver> c = /*Collections.synchronizedList(Lists.newArrayList())*/ null; // Akarin - I don't think any plugin rely on this
private volatile long d;
private volatile boolean e;
private volatile long e;
private volatile boolean f;
private FileIOThread() {
// Thread thread = new Thread(this, "File IO Thread"); // Akarin
// thread.setUncaughtExceptionHandler(new ThreadNamedUncaughtExceptionHandler(FileIOThread.a)); // Akarin
// thread.setPriority(1); // Akarin
// thread.start(); // Akarin
}
public static FileIOThread a() {
return FileIOThread.a;
return FileIOThread.b;
}
public void run() {
@@ -32,27 +36,24 @@ public class FileIOThread implements Runnable {
}
private void c() {
for (int i = 0; i < this.b.size(); ++i) {
IAsyncChunkSaver iasyncchunksaver = (IAsyncChunkSaver) this.b.get(i);
for (int i = 0; i < this.c.size(); ++i) {
IAsyncChunkSaver iasyncchunksaver = (IAsyncChunkSaver) this.c.get(i);
boolean flag = iasyncchunksaver.a();
if (!flag) {
this.b.remove(i--);
++this.d;
this.c.remove(i--);
++this.e;
}
// Paper start - Add toggle
if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) {
if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) { // Paper
try {
Thread.sleep(this.e ? 0L : 2L);
Thread.sleep(this.f ? 0L : 1L); // Paper
} catch (InterruptedException interruptedexception) {
interruptedexception.printStackTrace();
}
}
// Paper end
}} // Paper
}
if (this.b.isEmpty()) {
if (this.c.isEmpty()) {
try {
Thread.sleep(25L);
} catch (InterruptedException interruptedexception1) {
@@ -63,19 +64,19 @@ public class FileIOThread implements Runnable {
}
public void a(IAsyncChunkSaver iasyncchunksaver) {
if (!this.b.contains(iasyncchunksaver)) {
++this.c;
this.b.add(iasyncchunksaver);
if (!this.c.contains(iasyncchunksaver)) {
++this.d;
this.c.add(iasyncchunksaver);
}
}
public void b() throws InterruptedException {
this.e = true;
this.f = true;
while (this.c != this.d) {
while (this.d != this.e) {
Thread.sleep(10L);
}
this.e = false;
this.f = false;
}
}

View File

@@ -8,37 +8,40 @@ import io.akarin.server.core.AkarinGlobalConfig;
*/
public class ItemEnderEye extends Item {
public ItemEnderEye() {
this.b(CreativeModeTab.f);
public ItemEnderEye(Item.Info item_info) {
super(item_info);
}
@Override
public EnumInteractionResult a(EntityHuman entityhuman, World world, BlockPosition blockposition, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
public EnumInteractionResult a(ItemActionContext itemactioncontext) {
World world = itemactioncontext.getWorld();
BlockPosition blockposition = itemactioncontext.getClickPosition();
IBlockData iblockdata = world.getType(blockposition);
ItemStack itemstack = entityhuman.b(enumhand);
if (entityhuman.a(blockposition.shift(enumdirection), enumdirection, itemstack) && iblockdata.getBlock() == Blocks.END_PORTAL_FRAME && !iblockdata.get(BlockEnderPortalFrame.EYE).booleanValue()) {
if (iblockdata.getBlock() == Blocks.END_PORTAL_FRAME && !((Boolean) iblockdata.get(BlockEnderPortalFrame.EYE)).booleanValue()) {
if (world.isClientSide) {
return EnumInteractionResult.SUCCESS;
} else {
world.setTypeAndData(blockposition, iblockdata.set(BlockEnderPortalFrame.EYE, Boolean.valueOf(true)), 2);
IBlockData iblockdata1 = (IBlockData) iblockdata.set(BlockEnderPortalFrame.EYE, Boolean.valueOf(true));
Block.a(iblockdata, iblockdata1, world, blockposition);
world.setTypeAndData(blockposition, iblockdata1, 2);
world.updateAdjacentComparators(blockposition, Blocks.END_PORTAL_FRAME);
itemstack.subtract(1);
itemactioncontext.getItemStack().subtract(1);
for (int i = 0; i < 16; ++i) {
double d0 = blockposition.getX() + (5.0F + ItemEnderEye.j.nextFloat() * 6.0F) / 16.0F;
double d1 = blockposition.getY() + 0.8125F;
double d2 = blockposition.getZ() + (5.0F + ItemEnderEye.j.nextFloat() * 6.0F) / 16.0F;
double d0 = (double) ((float) blockposition.getX() + (5.0F + ItemEnderEye.i.nextFloat() * 6.0F) / 16.0F);
double d1 = (double) ((float) blockposition.getY() + 0.8125F);
double d2 = (double) ((float) blockposition.getZ() + (5.0F + ItemEnderEye.i.nextFloat() * 6.0F) / 16.0F);
double d3 = 0.0D;
double d4 = 0.0D;
double d5 = 0.0D;
world.addParticle(EnumParticle.SMOKE_NORMAL, d0, d1, d2, 0.0D, 0.0D, 0.0D, new int[0]);
world.addParticle(Particles.M, d0, d1, d2, 0.0D, 0.0D, 0.0D);
}
world.a((EntityHuman) null, blockposition, SoundEffects.bp, SoundCategory.BLOCKS, 1.0F, 1.0F);
world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_END_PORTAL_FRAME_FILL, SoundCategory.BLOCKS, 1.0F, 1.0F);
if (AkarinGlobalConfig.disableEndPortalCreate) return EnumInteractionResult.SUCCESS; // Akarin
ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = BlockEnderPortalFrame.e().a(world, blockposition);
ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = BlockEnderPortalFrame.d().a(world, blockposition);
if (shapedetector_shapedetectorcollection != null) {
BlockPosition blockposition1 = shapedetector_shapedetectorcollection.a().a(-3, 0, -3);
@@ -55,11 +58,10 @@ public class ItemEnderEye extends Item {
return EnumInteractionResult.SUCCESS;
}
} else {
return EnumInteractionResult.FAIL;
return EnumInteractionResult.PASS;
}
}
@Override
public InteractionResultWrapper<ItemStack> a(World world, EntityHuman entityhuman, EnumHand enumhand) {
ItemStack itemstack = entityhuman.b(enumhand);
MovingObjectPosition movingobjectposition = this.a(world, entityhuman, false);
@@ -69,24 +71,24 @@ public class ItemEnderEye extends Item {
} else {
entityhuman.c(enumhand);
if (!world.isClientSide) {
BlockPosition blockposition = ((WorldServer) world).getChunkProviderServer().a(world, "Stronghold", new BlockPosition(entityhuman), false);
BlockPosition blockposition = ((WorldServer) world).getChunkProviderServer().a(world, "Stronghold", new BlockPosition(entityhuman), 100, false);
if (blockposition != null) {
EntityEnderSignal entityendersignal = new EntityEnderSignal(world, entityhuman.locX, entityhuman.locY + entityhuman.length / 2.0F, entityhuman.locZ);
EntityEnderSignal entityendersignal = new EntityEnderSignal(world, entityhuman.locX, entityhuman.locY + (double) (entityhuman.length / 2.0F), entityhuman.locZ);
entityendersignal.a(blockposition);
world.addEntity(entityendersignal);
if (entityhuman instanceof EntityPlayer) {
CriterionTriggers.l.a((EntityPlayer) entityhuman, blockposition);
CriterionTriggers.m.a((EntityPlayer) entityhuman, blockposition);
}
world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.bc, SoundCategory.NEUTRAL, 0.5F, 0.4F / (ItemEnderEye.j.nextFloat() * 0.4F + 0.8F));
world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_ENDER_EYE_LAUNCH, SoundCategory.NEUTRAL, 0.5F, 0.4F / (ItemEnderEye.i.nextFloat() * 0.4F + 0.8F));
world.a((EntityHuman) null, 1003, new BlockPosition(entityhuman), 0);
if (!entityhuman.abilities.canInstantlyBuild) {
itemstack.subtract(1);
}
entityhuman.b(StatisticList.b(this));
entityhuman.b(StatisticList.ITEM_USED.b(this));
return new InteractionResultWrapper(EnumInteractionResult.SUCCESS, itemstack);
}
}

View File

@@ -1,118 +1,78 @@
package net.minecraft.server;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import io.akarin.server.core.AkarinGlobalConfig;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nullable;
/**
* Akarin Changes Note
* 1) Restricted spawner modify (feature)
*/
public class ItemMonsterEgg extends Item {
public ItemMonsterEgg() {
this.b(CreativeModeTab.f);
private static final Map<EntityTypes<?>, ItemMonsterEgg> a = Maps.newIdentityHashMap();
private final int b;
private final int c;
private final EntityTypes<?> d;
public ItemMonsterEgg(EntityTypes<?> entitytypes, int i, int j, Item.Info item_info) {
super(item_info);
this.d = entitytypes;
this.b = i;
this.c = j;
ItemMonsterEgg.a.put(entitytypes, this);
}
public String b(ItemStack itemstack) {
String s = ("" + LocaleI18n.get(this.getName() + ".name")).trim();
String s1 = EntityTypes.a(h(itemstack));
if (s1 != null) {
s = s + " " + LocaleI18n.get("entity." + s1 + ".name");
}
return s;
}
public EnumInteractionResult a(EntityHuman entityhuman, World world, BlockPosition blockposition, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
ItemStack itemstack = entityhuman.b(enumhand);
public EnumInteractionResult a(ItemActionContext itemactioncontext) {
World world = itemactioncontext.getWorld();
if (world.isClientSide) {
return EnumInteractionResult.SUCCESS;
} else if (!entityhuman.a(blockposition.shift(enumdirection), enumdirection, itemstack)) {
return EnumInteractionResult.FAIL;
} else {
ItemStack itemstack = itemactioncontext.getItemStack();
BlockPosition blockposition = itemactioncontext.getClickPosition();
EnumDirection enumdirection = itemactioncontext.getClickedFace();
IBlockData iblockdata = world.getType(blockposition);
Block block = iblockdata.getBlock();
if (block == Blocks.MOB_SPAWNER && (AkarinGlobalConfig.allowSpawnerModify || entityhuman.isCreativeAndOp())) { // Akarin
if (block == Blocks.SPAWNER && (AkarinGlobalConfig.allowSpawnerModify || itemactioncontext.getEntity().isCreativeAndOp())) { // Akarin
TileEntity tileentity = world.getTileEntity(blockposition);
if (tileentity instanceof TileEntityMobSpawner) {
MobSpawnerAbstract mobspawnerabstract = ((TileEntityMobSpawner) tileentity).getSpawner();
EntityTypes entitytypes = this.b(itemstack.getTag());
mobspawnerabstract.setMobName(h(itemstack));
if (entitytypes != null) {
mobspawnerabstract.setMobName(entitytypes);
tileentity.update();
world.notify(blockposition, iblockdata, iblockdata, 3);
if (!entityhuman.abilities.canInstantlyBuild) {
itemstack.subtract(1);
}
itemstack.subtract(1);
return EnumInteractionResult.SUCCESS;
}
}
BlockPosition blockposition1 = blockposition.shift(enumdirection);
double d0 = this.a(world, blockposition1);
Entity entity = a(world, h(itemstack), (double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + d0, (double) blockposition1.getZ() + 0.5D);
BlockPosition blockposition1;
if (entity != null) {
if (entity instanceof EntityLiving && itemstack.hasName()) {
entity.setCustomName(itemstack.getName());
}
a(world, entityhuman, itemstack, entity);
if (!entityhuman.abilities.canInstantlyBuild) {
itemstack.subtract(1);
}
}
return EnumInteractionResult.SUCCESS;
}
}
protected double a(World world, BlockPosition blockposition) {
AxisAlignedBB axisalignedbb = (new AxisAlignedBB(blockposition)).b(0.0D, -1.0D, 0.0D);
List list = world.getCubes((Entity) null, axisalignedbb);
if (list.isEmpty()) {
return 0.0D;
if (iblockdata.h(world, blockposition).b()) {
blockposition1 = blockposition;
} else {
double d0 = axisalignedbb.b;
AxisAlignedBB axisalignedbb1;
for (Iterator iterator = list.iterator(); iterator.hasNext(); d0 = Math.max(axisalignedbb1.e, d0)) {
axisalignedbb1 = (AxisAlignedBB) iterator.next();
blockposition1 = blockposition.shift(enumdirection);
}
return d0 - (double) blockposition.getY();
}
}
public static void a(World world, @Nullable EntityHuman entityhuman, ItemStack itemstack, @Nullable Entity entity) {
MinecraftServer minecraftserver = world.getMinecraftServer();
if (minecraftserver != null && entity != null) {
NBTTagCompound nbttagcompound = itemstack.getTag();
if (nbttagcompound != null && nbttagcompound.hasKeyOfType("EntityTag", 10)) {
if (!world.isClientSide && entity.bC() && (entityhuman == null || !minecraftserver.getPlayerList().isOp(entityhuman.getProfile()))) {
return;
}
NBTTagCompound nbttagcompound1 = entity.save(new NBTTagCompound());
UUID uuid = entity.getUniqueID();
nbttagcompound1.a(nbttagcompound.getCompound("EntityTag"));
entity.a(uuid);
entity.f(nbttagcompound1);
EntityTypes entitytypes1 = this.b(itemstack.getTag());
if (entitytypes1 == null || entitytypes1.a(world, itemstack, itemactioncontext.getEntity(), blockposition1, true, !Objects.equals(blockposition, blockposition1) && enumdirection == EnumDirection.UP) != null) {
itemstack.subtract(1);
}
return EnumInteractionResult.SUCCESS;
}
}
@@ -130,22 +90,17 @@ public class ItemMonsterEgg extends Item {
if (!(world.getType(blockposition).getBlock() instanceof BlockFluids)) {
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
} else if (world.a(entityhuman, blockposition) && entityhuman.a(blockposition, movingobjectposition.direction, itemstack)) {
Entity entity = a(world, h(itemstack), (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D);
EntityTypes entitytypes = this.b(itemstack.getTag());
if (entity == null) {
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
} else {
if (entity instanceof EntityLiving && itemstack.hasName()) {
entity.setCustomName(itemstack.getName());
}
a(world, entityhuman, itemstack, entity);
if (entitytypes != null && entitytypes.a(world, itemstack, entityhuman, blockposition, false, false) != null) {
if (!entityhuman.abilities.canInstantlyBuild) {
itemstack.subtract(1);
}
entityhuman.b(StatisticList.b((Item) this));
entityhuman.b(StatisticList.ITEM_USED.b(this));
return new InteractionResultWrapper(EnumInteractionResult.SUCCESS, itemstack);
} else {
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
}
} else {
return new InteractionResultWrapper(EnumInteractionResult.FAIL, itemstack);
@@ -156,88 +111,24 @@ public class ItemMonsterEgg extends Item {
}
}
@Nullable
public static Entity a(World world, @Nullable MinecraftKey minecraftkey, double d0, double d1, double d2) {
return spawnCreature(world, minecraftkey, d0, d1, d2, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG);
public boolean a(@Nullable NBTTagCompound nbttagcompound, EntityTypes<?> entitytypes) {
return Objects.equals(this.b(nbttagcompound), entitytypes);
}
public static Iterable<ItemMonsterEgg> d() {
return Iterables.unmodifiableIterable(ItemMonsterEgg.a.values());
}
@Nullable
public static Entity spawnCreature(World world, @Nullable MinecraftKey minecraftkey, double d0, double d1, double d2, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {
if (minecraftkey != null && EntityTypes.eggInfo.containsKey(minecraftkey)) {
Entity entity = null;
for (int i = 0; i < 1; ++i) {
entity = EntityTypes.a(minecraftkey, world);
if (entity instanceof EntityInsentient) {
EntityInsentient entityinsentient = (EntityInsentient) entity;
entity.setPositionRotation(d0, d1, d2, MathHelper.g(world.random.nextFloat() * 360.0F), 0.0F);
entityinsentient.aP = entityinsentient.yaw;
entityinsentient.aN = entityinsentient.yaw;
entityinsentient.prepare(world.D(new BlockPosition(entityinsentient)), (GroupDataEntity) null);
// CraftBukkit start - don't return an entity when CreatureSpawnEvent is canceled
if (!world.addEntity(entity, spawnReason)) {
entity = null;
} else {
entityinsentient.D();
}
// CraftBukkit end
}
}
return entity;
} else {
return null;
}
}
public void a(CreativeModeTab creativemodetab, NonNullList<ItemStack> nonnulllist) {
if (this.a(creativemodetab)) {
Iterator iterator = EntityTypes.eggInfo.values().iterator();
while (iterator.hasNext()) {
EntityTypes.MonsterEggInfo entitytypes_monsteregginfo = (EntityTypes.MonsterEggInfo) iterator.next();
ItemStack itemstack = new ItemStack(this, 1);
a(itemstack, entitytypes_monsteregginfo.a);
nonnulllist.add(itemstack);
}
}
}
public static void a(ItemStack itemstack, MinecraftKey minecraftkey) {
NBTTagCompound nbttagcompound = itemstack.hasTag() ? itemstack.getTag() : new NBTTagCompound();
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.setString("id", minecraftkey.toString());
nbttagcompound.set("EntityTag", nbttagcompound1);
itemstack.setTag(nbttagcompound);
}
@Nullable
public static MinecraftKey h(ItemStack itemstack) {
NBTTagCompound nbttagcompound = itemstack.getTag();
if (nbttagcompound == null) {
return null;
} else if (!nbttagcompound.hasKeyOfType("EntityTag", 10)) {
return null;
} else {
public EntityTypes<?> b(@Nullable NBTTagCompound nbttagcompound) {
if (nbttagcompound != null && nbttagcompound.hasKeyOfType("EntityTag", 10)) {
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("EntityTag");
if (!nbttagcompound1.hasKeyOfType("id", 8)) {
return null;
} else {
String s = nbttagcompound1.getString("id");
MinecraftKey minecraftkey = new MinecraftKey(s);
if (!s.contains(":")) {
nbttagcompound1.setString("id", minecraftkey.toString());
if (nbttagcompound1.hasKeyOfType("id", 8)) {
return EntityTypes.a(nbttagcompound1.getString("id"));
}
}
return minecraftkey;
}
}
return this.d;
}
}

View File

@@ -3,27 +3,24 @@ package net.minecraft.server;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
import io.akarin.api.internal.utils.CheckedConcurrentLinkedQueue;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.DefaultEventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalEventLoopGroup;
import io.netty.channel.local.LocalServerChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.timeout.TimeoutException;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import java.net.SocketAddress;
import java.util.Queue;
import javax.annotation.Nullable;
import javax.crypto.SecretKey;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -32,9 +29,7 @@ import org.apache.logging.log4j.MarkerManager;
/**
* Akarin Changes Note
* 2) Expose private members (nsc)
* 3) Changes lock type to updatable lock (compatibility)
* 4) Removes unneed array creation (performance)
* 1) Changes lock type to updatable lock (compatibility)
*/
public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
@@ -42,38 +37,17 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
public static final Marker a = MarkerManager.getMarker("NETWORK");
public static final Marker b = MarkerManager.getMarker("NETWORK_PACKETS", NetworkManager.a);
public static final AttributeKey<EnumProtocol> c = AttributeKey.valueOf("protocol");
public static final LazyInitVar<NioEventLoopGroup> d = new LazyInitVar() {
protected NioEventLoopGroup a() {
public static final LazyInitVar<NioEventLoopGroup> d = new LazyInitVar(() -> {
return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build());
}
@Override
protected Object init() {
return this.a();
}
};
public static final LazyInitVar<EpollEventLoopGroup> e = new LazyInitVar() {
protected EpollEventLoopGroup a() {
});
public static final LazyInitVar<EpollEventLoopGroup> e = new LazyInitVar(() -> {
return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).build());
}
@Override
protected Object init() {
return this.a();
}
};
public static final LazyInitVar<LocalEventLoopGroup> f = new LazyInitVar() {
protected LocalEventLoopGroup a() {
return new LocalEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build());
}
@Override
protected Object init() {
return this.a();
}
};
});
public static final LazyInitVar<DefaultEventLoopGroup> f = new LazyInitVar(() -> {
return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build());
});
private final EnumProtocolDirection h;
private final Queue<NetworkManager.QueuedPacket> i = new CheckedConcurrentLinkedQueue<NetworkManager.QueuedPacket>(); private final Queue<NetworkManager.QueuedPacket> getPacketQueue() { return this.i; } // Paper - Anti-Xray - OBFHELPER // Akarin
private final Queue<NetworkManager.QueuedPacket> i = new io.akarin.api.internal.utils.CheckedConcurrentLinkedQueue<>(); private final Queue<NetworkManager.QueuedPacket> getPacketQueue() { return this.i; } // Paper - OBFHELPER // Akarin
private final ReentrantReadWriteUpdateLock j = new ReentrantReadWriteUpdateLock(); // Akarin - use update lock
public Channel channel;
// Spigot Start // PAIL
@@ -86,6 +60,12 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
private IChatBaseComponent n;
private boolean o;
private boolean p;
private int q;
private int r;
private float s;
private float t;
private int u;
private boolean v;
// Paper start - NetworkClient implementation
public int protocolVersion;
public java.net.InetSocketAddress virtualHost;
@@ -96,7 +76,6 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
this.h = enumprotocoldirection;
}
@Override
public void channelActive(ChannelHandlerContext channelhandlercontext) throws Exception {
super.channelActive(channelhandlercontext);
this.channel = channelhandlercontext.channel();
@@ -119,37 +98,58 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
NetworkManager.g.debug("Enabled auto read");
}
@Override
public void channelInactive(ChannelHandlerContext channelhandlercontext) throws Exception {
this.close(new ChatMessage("disconnect.endOfStream", new Object[0]));
}
@Override
public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) throws Exception {
ChatMessage chatmessage;
if (throwable instanceof TimeoutException) {
chatmessage = new ChatMessage("disconnect.timeout", new Object[0]);
public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) {
if (throwable instanceof SkipEncodeException) {
NetworkManager.g.debug("Skipping packet due to errors", throwable.getCause());
} else {
chatmessage = new ChatMessage("disconnect.genericReason", new Object[] { "Internal Exception: " + throwable});
boolean flag = !this.v;
this.v = true;
if (this.channel.isOpen()) {
if (throwable instanceof TimeoutException) {
NetworkManager.g.debug("Timeout", throwable);
this.close(new ChatMessage("disconnect.timeout", new Object[0]));
} else {
ChatMessage chatmessage = new ChatMessage("disconnect.genericReason", new Object[] { "Internal Exception: " + throwable});
if (flag) {
NetworkManager.g.debug("Failed to sent packet", throwable);
this.sendPacket(new PacketPlayOutKickDisconnect(chatmessage), (future) -> {
this.close(chatmessage); // CraftBukkit - decompile error
});
this.stopReading();
} else {
NetworkManager.g.debug("Double fault", throwable);
this.close(chatmessage);
}
}
NetworkManager.g.debug(chatmessage.toPlainText(), throwable);
this.close(chatmessage);
}
}
if (MinecraftServer.getServer().isDebugging()) throwable.printStackTrace(); // Spigot
}
protected void a(ChannelHandlerContext channelhandlercontext, Packet<?> packet) throws Exception {
if (this.channel.isOpen()) {
try {
((Packet) packet).a(this.m); // CraftBukkit - decompile error
a(packet, this.m);
} catch (CancelledPacketHandleException cancelledpackethandleexception) {
;
}
++this.q;
}
}
private static <T extends PacketListener> void a(Packet<T> packet, PacketListener packetlistener) {
packet.a((T) packetlistener); // CraftBukkit - decompile error
}
public void setPacketListener(PacketListener packetlistener) {
Validate.notNull(packetlistener, "packetListener", new Object[0]);
NetworkManager.g.debug("Set listener of {} to {}", this, packetlistener);
@@ -157,14 +157,18 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
}
public void sendPacket(Packet<?> packet) {
if (this.isConnected() && this.trySendQueue() && !(packet instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) packet).isReady())) { // Paper - Async-Anti-Xray - Add chunk packets which are not ready or all packets if the queue contains chunk packets which are not ready to the queue and send the packets later in the right order
//this.m(); // Paper - Async-Anti-Xray - Move to if statement (this.trySendQueue())
this.a(packet, (GenericFutureListener[]) null);
this.sendPacket(packet, (GenericFutureListener) null);
}
public void sendPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
if (this.isConnected() && this.sendPacketQueue() && !(packet instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) packet).isReady())) { // Paper - Async-Anti-Xray - Add chunk packets which are not ready or all packets if the packet queue contains chunk packets which are not ready to the packet queue and send the packets later in the right order
//this.o(); // Paper - Async-Anti-Xray - Move to if statement (this.sendPacketQueue())
this.b(packet, genericfuturelistener);
} else {
this.j.writeLock().lock();
try {
this.i.add(new NetworkManager.QueuedPacket(packet)); // Akarin - remove fake listener creation
this.i.add(new NetworkManager.QueuedPacket(packet, genericfuturelistener));
} finally {
this.j.writeLock().unlock();
}
@@ -172,27 +176,12 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
}
public void sendPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> genericfuturelistener, GenericFutureListener<? extends Future<? super Void>>... agenericfuturelistener) {
if (this.isConnected() && this.trySendQueue() && !(packet instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) packet).isReady())) { // Paper - Async-Anti-Xray - Add chunk packets which are not ready or all packets if the queue contains chunk packets which are not ready to the queue and send the packets later in the right order
//this.m(); // Paper - Async-Anti-Xray - Move to if statement (this.trySendQueue())
this.a(packet, ArrayUtils.add(agenericfuturelistener, 0, genericfuturelistener));
} else {
this.j.writeLock().lock();
try {
this.i.add(new NetworkManager.QueuedPacket(packet, ArrayUtils.add(agenericfuturelistener, 0, genericfuturelistener)));
} finally {
this.j.writeLock().unlock();
}
}
}
private void dispatchPacket(final Packet<?> packet, @Nullable final GenericFutureListener<? extends Future<? super Void>>[] genericFutureListeners) { this.a(packet, genericFutureListeners); } // Paper - Anti-Xray - OBFHELPER
private void a(final Packet<?> packet, @Nullable final GenericFutureListener<? extends Future<? super Void>>[] agenericfuturelistener) {
final EnumProtocol enumprotocol = EnumProtocol.a(packet);
final EnumProtocol enumprotocol1 = this.channel.attr(NetworkManager.c).get();
private void dispatchPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> genericFutureListener) { this.b(packet, genericFutureListener); } // Paper - OBFHELPER
private void b(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
EnumProtocol enumprotocol = EnumProtocol.a(packet);
EnumProtocol enumprotocol1 = (EnumProtocol) this.channel.attr(NetworkManager.c).get();
++this.r;
if (enumprotocol1 != enumprotocol) {
NetworkManager.g.debug("Disabled auto read");
this.channel.config().setAutoRead(false);
@@ -205,35 +194,32 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
ChannelFuture channelfuture = this.channel.writeAndFlush(packet);
if (agenericfuturelistener != null) {
channelfuture.addListeners(agenericfuturelistener);
if (genericfuturelistener != null) {
channelfuture.addListener(genericfuturelistener);
}
channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
} else {
this.channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
this.channel.eventLoop().execute(() -> {
if (enumprotocol != enumprotocol1) {
NetworkManager.this.setProtocol(enumprotocol);
this.setProtocol(enumprotocol);
}
ChannelFuture channelfuture = NetworkManager.this.channel.writeAndFlush(packet);
ChannelFuture channelfuture = this.channel.writeAndFlush(packet);
if (agenericfuturelistener != null) {
channelfuture.addListeners(agenericfuturelistener);
if (genericfuturelistener != null) {
channelfuture.addListener(genericfuturelistener);
}
channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
});
}
}
// Paper start - Async-Anti-Xray - Stop dispatching further packets and return false if the peeked packet is a chunk packet which is not ready
private boolean trySendQueue() { return this.m(); } // OBFHELPER
private boolean m() { // void -> boolean
private boolean sendPacketQueue() { return this.o(); } // OBFHELPER // void -> boolean
private boolean o() { // void -> boolean
if (this.channel != null && this.channel.isOpen()) {
if (this.i.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all
return true;
@@ -243,14 +229,14 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
try {
while (!this.i.isEmpty()) {
NetworkManager.QueuedPacket networkmanager_queuedpacket = this.getPacketQueue().peek(); // poll -> peek
NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.getPacketQueue().peek(); // poll -> peek
if (networkmanager_queuedpacket != null) { // Fix NPE (Spigot bug caused by handleDisconnection())
if (networkmanager_queuedpacket.getPacket() instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) networkmanager_queuedpacket.getPacket()).isReady()) { // Check if the peeked packet is a chunk packet which is not ready
return false; // Return false if the peeked packet is a chunk packet which is not ready
} else {
this.getPacketQueue().poll(); // poll here
this.dispatchPacket(networkmanager_queuedpacket.getPacket(), networkmanager_queuedpacket.getGenericFutureListeners()); // dispatch the packet
this.dispatchPacket(networkmanager_queuedpacket.getPacket(), networkmanager_queuedpacket.getGenericFutureListener()); // dispatch the packet
}
}
}
@@ -265,15 +251,22 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
// Paper end
public void a() {
this.m();
this.o();
if (this.m instanceof ITickable) {
((ITickable) this.m).e();
((ITickable) this.m).Y_();
}
if (this.channel != null) {
if (enableExplicitFlush) this.channel.eventLoop().execute(() -> this.channel.flush()); // Paper - we don't need to explicit flush here, but allow opt in incase issues are found to a better version
}
if (this.u++ % 20 == 0) {
this.t = this.t * 0.75F + (float) this.r * 0.25F;
this.s = this.s * 0.75F + (float) this.q * 0.25F;
this.r = 0;
this.q = 0;
}
}
public SocketAddress getSocketAddress() {
@@ -313,6 +306,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
return this.m;
}
@Nullable
public IChatBaseComponent j() {
return this.n;
}
@@ -363,19 +357,19 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
}
}
@Override
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet object) throws Exception { // CraftBukkit - fix decompile error
this.a(channelhandlercontext, object);
this.a(channelhandlercontext, (Packet) object);
}
public static class QueuedPacket { // Akarin - default -> public
public static class QueuedPacket { // Akarin
private final Packet<?> a; public final Packet<?> getPacket() { return this.a; } // Paper - Anti-Xray - OBFHELPER // Akarin - private -> public
private final GenericFutureListener<? extends Future<? super Void>>[] b; public final GenericFutureListener<? extends Future<? super Void>>[] getGenericFutureListeners() { return this.b; } // Paper - Anti-Xray - OBFHELPER // Akarin - private -> public
private final Packet<?> a; public final Packet<?> getPacket() { return this.a; } // Paper - OBFHELPER // Akarin
@Nullable
private final GenericFutureListener<? extends Future<? super Void>> b; public final GenericFutureListener<? extends Future<? super Void>> getGenericFutureListener() { return this.b; } // Paper - OBFHELPER // Akarin
public QueuedPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>>... agenericfuturelistener) {
public QueuedPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
this.a = packet;
this.b = agenericfuturelistener;
this.b = genericfuturelistener;
}
}

View File

@@ -1,314 +0,0 @@
package net.minecraft.server;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nullable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// CraftBukkit Start
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
// CraftBukkit end
/**
* Akarin Changes Note
* 1) Check whether players empty (performance, MC-120780)
*/
public class PlayerChunk {
private static final Logger a = LogManager.getLogger();
private final PlayerChunkMap playerChunkMap;
public final List<EntityPlayer> c = Lists.newArrayList(); // CraftBukkit - public
private final ChunkCoordIntPair location;
private final short[] dirtyBlocks = new short[64];
@Nullable
public Chunk chunk; // CraftBukkit - public
private int dirtyCount;
private int h;
private long i;
private boolean done;
// CraftBukkit start - add fields
boolean chunkExists; // Paper
private boolean loadInProgress = false;
private Runnable loadedRunnable = new Runnable() {
@Override
public void run() {
loadInProgress = false;
PlayerChunk.this.chunk = PlayerChunk.this.playerChunkMap.getWorld().getChunkProviderServer().getOrLoadChunkAt(location.x, location.z);
markChunkUsed(); // Paper - delay chunk unloads
}
};
// Paper start - delay chunk unloads
public final void markChunkUsed() {
if (chunk != null && chunk.scheduledForUnload != null) {
chunk.scheduledForUnload = null;
}
}
// Paper end
// CraftBukkit end
public PlayerChunk(PlayerChunkMap playerchunkmap, int i, int j) {
this.playerChunkMap = playerchunkmap;
this.location = new ChunkCoordIntPair(i, j);
// CraftBukkit start
loadInProgress = true;
this.chunk = playerchunkmap.getWorld().getChunkProviderServer().getChunkAt(i, j, loadedRunnable, false);
this.chunkExists = this.chunk != null || ChunkIOExecutor.hasQueuedChunkLoad(playerChunkMap.getWorld(), i, j); // Paper
markChunkUsed(); // Paper - delay chunk unloads
// CraftBukkit end
}
public ChunkCoordIntPair a() {
return this.location;
}
public void a(final EntityPlayer entityplayer) { // CraftBukkit - added final to argument
if (this.c.contains(entityplayer)) {
PlayerChunk.a.debug("Failed to add player. {} already is in chunk {}, {}", entityplayer, Integer.valueOf(this.location.x), Integer.valueOf(this.location.z));
} else {
if (this.c.isEmpty()) {
this.i = this.playerChunkMap.getWorld().getTime();
}
this.c.add(entityplayer);
// CraftBukkit start - use async chunk io
// if (this.done) {
// this.sendChunk(entityplayer);
// }
if (this.done) {
this.sendChunk(entityplayer);
}
// CraftBukkit end
}
}
public void b(EntityPlayer entityplayer) {
if (this.c.contains(entityplayer)) {
// CraftBukkit start - If we haven't loaded yet don't load the chunk just so we can clean it up
if (!this.done) {
this.c.remove(entityplayer);
if (this.c.isEmpty()) {
ChunkIOExecutor.dropQueuedChunkLoad(this.playerChunkMap.getWorld(), this.location.x, this.location.z, this.loadedRunnable);
this.playerChunkMap.b(this);
}
return;
}
// CraftBukkit end
if (this.done) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutUnloadChunk(this.location.x, this.location.z));
}
this.c.remove(entityplayer);
if (this.c.isEmpty()) {
this.playerChunkMap.b(this);
}
}
}
public boolean a(boolean flag) {
if (this.chunk != null) {
return true;
} else {
/* CraftBukkit start
if (flag) {
this.chunk = this.playerChunkMap.getWorld().getChunkProviderServer().getChunkAt(this.location.x, this.location.z);
} else {
this.chunk = this.playerChunkMap.getWorld().getChunkProviderServer().getOrLoadChunkAt(this.location.x, this.location.z);
}
*/
if (!loadInProgress) {
loadInProgress = true;
this.chunk = playerChunkMap.getWorld().getChunkProviderServer().getChunkAt(this.location.x, this.location.z, loadedRunnable, flag);
markChunkUsed(); // Paper - delay chunk unloads
}
// CraftBukkit end
return this.chunk != null;
}
}
public boolean b() {
if (this.done) {
return true;
} else if (this.chunk == null) {
return false;
} else if (!this.chunk.isReady()) {
return false;
} else if (!this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', false)) { // Paper - Anti-Xray - Load nearby chunks if necessary
return false; // Paper - Anti-Xray - Wait and try again later
} else {
this.dirtyCount = 0;
this.h = 0;
this.done = true;
if (c.isEmpty()) return true; // Akarin - Fixes MC-120780
PacketPlayOutMapChunk packetplayoutmapchunk = new PacketPlayOutMapChunk(this.chunk, '\uffff');
Iterator iterator = this.c.iterator();
while (iterator.hasNext()) {
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
entityplayer.playerConnection.sendPacket(packetplayoutmapchunk);
this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk);
}
return true;
}
}
public void sendChunk(EntityPlayer entityplayer) {
if (this.done) {
this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', true); // Paper - Anti-Xray - Load nearby chunks if necessary
entityplayer.playerConnection.sendPacket(new PacketPlayOutMapChunk(this.chunk, '\uffff'));
this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk);
}
}
public void c() {
long i = this.playerChunkMap.getWorld().getTime();
if (this.chunk != null) {
this.chunk.c(this.chunk.x() + i - this.i);
}
this.i = i;
}
public void a(int i, int j, int k) {
if (this.done) {
if (this.dirtyCount == 0) {
this.playerChunkMap.a(this);
}
this.h |= 1 << (j >> 4);
if (this.dirtyCount < 64) {
short short0 = (short) (i << 12 | k << 8 | j);
for (int l = 0; l < this.dirtyCount; ++l) {
if (this.dirtyBlocks[l] == short0) {
return;
}
}
this.dirtyBlocks[this.dirtyCount++] = short0;
}
}
}
public void a(Packet<?> packet) {
if (this.done) {
for (int i = 0; i < this.c.size(); ++i) {
this.c.get(i).playerConnection.sendPacket(packet);
}
}
}
public void d() {
if (this.done && this.chunk != null) {
if (this.dirtyCount != 0) {
int i;
int j;
int k;
if (this.dirtyCount == 1) {
i = (this.dirtyBlocks[0] >> 12 & 15) + this.location.x * 16;
j = this.dirtyBlocks[0] & 255;
k = (this.dirtyBlocks[0] >> 8 & 15) + this.location.z * 16;
BlockPosition blockposition = new BlockPosition(i, j, k);
this.a((new PacketPlayOutBlockChange(this.playerChunkMap.getWorld(), blockposition)));
if (this.playerChunkMap.getWorld().getType(blockposition).getBlock().isTileEntity()) {
this.a(this.playerChunkMap.getWorld().getTileEntity(blockposition));
}
} else if (this.dirtyCount == 64) {
// Paper - Anti-Xray - Loading chunks here could cause a ConcurrentModificationException #1104
//this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, this.h, true); // Paper - Anti-Xray - Load nearby chunks if necessary
this.a((new PacketPlayOutMapChunk(this.chunk, this.h)));
} else {
this.a((new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, this.chunk)));
for (i = 0; i < this.dirtyCount; ++i) {
j = (this.dirtyBlocks[i] >> 12 & 15) + this.location.x * 16;
k = this.dirtyBlocks[i] & 255;
int l = (this.dirtyBlocks[i] >> 8 & 15) + this.location.z * 16;
BlockPosition blockposition1 = new BlockPosition(j, k, l);
if (this.playerChunkMap.getWorld().getType(blockposition1).getBlock().isTileEntity()) {
this.a(this.playerChunkMap.getWorld().getTileEntity(blockposition1));
}
}
}
this.dirtyCount = 0;
this.h = 0;
}
}
}
private void a(@Nullable TileEntity tileentity) {
if (tileentity != null) {
PacketPlayOutTileEntityData packetplayouttileentitydata = tileentity.getUpdatePacket();
if (packetplayouttileentitydata != null) {
this.a(packetplayouttileentitydata);
}
}
}
public boolean d(EntityPlayer entityplayer) {
return this.c.contains(entityplayer);
}
public boolean a(Predicate<EntityPlayer> predicate) {
return Iterables.tryFind(this.c, predicate).isPresent();
}
public boolean a(double d0, Predicate<EntityPlayer> predicate) {
int i = 0;
for (int j = this.c.size(); i < j; ++i) {
EntityPlayer entityplayer = this.c.get(i);
if (predicate.apply(entityplayer) && this.location.a(entityplayer) < d0 * d0) {
return true;
}
}
return false;
}
public boolean e() {
return this.done;
}
@Nullable
public Chunk f() {
return this.chunk;
}
public double g() {
double d0 = Double.MAX_VALUE;
Iterator iterator = this.c.iterator();
while (iterator.hasNext()) {
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
double d1 = this.location.a(entityplayer);
if (d1 < d0) {
d0 = d1;
}
}
return d0;
}
}

View File

@@ -1,7 +1,8 @@
package net.minecraft.server;
import co.aikar.timings.Timing;
import com.google.common.base.Predicate;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Lists;
@@ -10,14 +11,11 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
@@ -27,32 +25,18 @@ import java.util.LinkedList;
/**
* Akarin Changes Note
* 1) Make whole class thread-safe (safety issue)
* 1) Add synchronizations (safety issue)
*/
@ThreadSafe // Akarin - idk why we need do so!!
public class PlayerChunkMap {
private static final Predicate<EntityPlayer> a = new Predicate() {
public boolean a(@Nullable EntityPlayer entityplayer) {
private static final Predicate<EntityPlayer> a = (entityplayer) -> {
return entityplayer != null && !entityplayer.isSpectator();
}
public boolean apply(@Nullable Object object) {
return this.a((EntityPlayer) object);
}
};
private static final Predicate<EntityPlayer> b = new Predicate() {
public boolean a(@Nullable EntityPlayer entityplayer) {
return entityplayer != null && (!entityplayer.isSpectator() || entityplayer.x().getGameRules().getBoolean("spectatorsGenerateChunks"));
}
public boolean apply(@Nullable Object object) {
return this.a((EntityPlayer) object);
}
private static final Predicate<EntityPlayer> b = (entityplayer) -> {
return entityplayer != null && (!entityplayer.isSpectator() || entityplayer.getWorldServer().getGameRules().getBoolean("spectatorsGenerateChunks"));
};
private final WorldServer world;
private final List<EntityPlayer> managedPlayers = Lists.newArrayList();
private final ReentrantReadWriteLock managedPlayersLock = new ReentrantReadWriteLock(); // Akarin - add lock
private final Long2ObjectMap<PlayerChunk> e = new Long2ObjectOpenHashMap(4096);
private final Set<PlayerChunk> f = Sets.newHashSet();
private final List<PlayerChunk> g = Lists.newLinkedList();
@@ -76,7 +60,7 @@ public class PlayerChunkMap {
public Iterator<Chunk> b() {
final Iterator iterator = this.i.iterator();
return new AbstractIterator<Chunk>() {
return new AbstractIterator() {
protected Chunk a() {
while (true) {
if (iterator.hasNext()) {
@@ -87,11 +71,7 @@ public class PlayerChunkMap {
continue;
}
if (!chunk.v() && chunk.isDone()) {
return chunk;
}
if (!chunk.j()) {
if (!chunk.v()) {
return chunk;
}
@@ -106,16 +86,17 @@ public class PlayerChunkMap {
}
}
protected Chunk computeNext() {
protected Object computeNext() {
return this.a();
}
};
}
public synchronized void flush() { // Akarin - synchronized
public void flush() {
long i = this.world.getTime();
int j;
PlayerChunk playerchunk;
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
if (i - this.k > 8000L) {
try (Timing ignored = world.timings.doChunkMapUpdate.startTiming()) { // Paper
@@ -145,14 +126,9 @@ public class PlayerChunkMap {
if (this.l && i % 4L == 0L) {
this.l = false;
try (Timing ignored = world.timings.doChunkMapSortMissing.startTiming()) { // Paper
Collections.sort(this.h, new Comparator() {
public int a(PlayerChunk playerchunk, PlayerChunk playerchunk1) {
return ComparisonChain.start().compare(playerchunk.g(), playerchunk1.g()).result();
}
public int compare(Object object, Object object1) {
return this.a((PlayerChunk) object, (PlayerChunk) object1);
}
// CraftBukkit start
Collections.sort(this.h, (playerchunkx, playerchunk1x) -> {
return ComparisonChain.start().compare(playerchunkx.g(), playerchunk1x.g()).result();
});
} // Paper timing
}
@@ -160,16 +136,11 @@ public class PlayerChunkMap {
if (this.m && i % 4L == 2L) {
this.m = false;
try (Timing ignored = world.timings.doChunkMapSortSendToPlayers.startTiming()) { // Paper
Collections.sort(this.g, new Comparator() {
public int a(PlayerChunk playerchunk, PlayerChunk playerchunk1) {
return ComparisonChain.start().compare(playerchunk.g(), playerchunk1.g()).result();
}
public int compare(Object object, Object object1) {
return this.a((PlayerChunk) object, (PlayerChunk) object1);
}
Collections.sort(this.g, (playerchunkx, playerchunk1x) -> {
return ComparisonChain.start().compare(playerchunkx.g(), playerchunk1x.g()).result();
});
} // Paper timing
// CraftBukkit end
}
if (!this.h.isEmpty()) {
@@ -233,29 +204,32 @@ public class PlayerChunkMap {
} // Paper timing
}
managedPlayersLock.readLock().lock(); // Akarin
if (this.managedPlayers.isEmpty()) {
try (Timing ignored = world.timings.doChunkMapUnloadChunks.startTiming()) { // Paper
WorldProvider worldprovider = this.world.worldProvider;
if (!worldprovider.e() && !this.world.savingDisabled) { // Paper - respect saving disabled setting
if (!worldprovider.p() && !this.world.savingDisabled) { // Paper - respect saving disabled setting
this.world.getChunkProviderServer().b();
}
} // Paper timing
}
managedPlayersLock.readLock().unlock(); // Akarin
} // Akarin
}
public synchronized boolean a(int i, int j) { // Akarin - synchronized
public boolean a(int i, int j) {
long k = d(i, j);
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
return this.e.get(k) != null;
} // Akarin
}
@Nullable
public synchronized PlayerChunk getChunk(int i, int j) { // Akarin - synchronized
public PlayerChunk getChunk(int i, int j) {
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
return (PlayerChunk) this.e.get(d(i, j));
} // Akarin
}
private PlayerChunk c(int i, int j) {
@@ -320,16 +294,14 @@ public class PlayerChunkMap {
}
Collections.sort(chunkList, new ChunkCoordComparator(entityplayer));
synchronized (this) { // Akarin - synchronized
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
for (ChunkCoordIntPair pair : chunkList) {
this.c(pair.x, pair.z).a(entityplayer);
}
} // Akarin
// CraftBukkit end
managedPlayersLock.writeLock().lock(); // Akarin
this.managedPlayers.add(entityplayer);
managedPlayersLock.writeLock().unlock(); // Akarin
} // Akarin
this.e();
}
@@ -350,9 +322,9 @@ public class PlayerChunkMap {
}
}
managedPlayersLock.writeLock().lock(); // Akarin
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
this.managedPlayers.remove(entityplayer);
managedPlayersLock.writeLock().unlock(); // Akarin
} // Akarin
this.e();
}
@@ -373,8 +345,7 @@ public class PlayerChunkMap {
if (d2 >= 64.0D) {
int k = (int) entityplayer.d >> 4;
int l = (int) entityplayer.e >> 4;
final int viewDistance = entityplayer.getViewDistance(); // Paper - Player view distance API
int i1 = Math.max(getViewDistance(), viewDistance); // Paper - Player view distance API
int i1 = entityplayer.getViewDistance(); // Paper - Player view distance API
int j1 = i - k;
int k1 = j - l;
@@ -384,7 +355,7 @@ public class PlayerChunkMap {
if (j1 != 0 || k1 != 0) {
for (int l1 = i - i1; l1 <= i + i1; ++l1) {
for (int i2 = j - i1; i2 <= j + i1; ++i2) {
if (!this.a(l1, i2, k, l, viewDistance)) { // Paper - Player view distance API
if (!this.a(l1, i2, k, l, i1)) {
// this.c(l1, i2).a(entityplayer);
chunksToLoad.add(new ChunkCoordIntPair(l1, i2)); // CraftBukkit
}
@@ -395,8 +366,14 @@ public class PlayerChunkMap {
if (playerchunk != null) {
playerchunk.b(entityplayer);
}
} else { // Paper start
PlayerChunk playerchunk = this.getChunk(l1 - j1, i2 - k1);
if (playerchunk != null) {
playerchunk.checkHighPriority(entityplayer); // Paper
}
}
// Paper end
}
}
entityplayer.d = entityplayer.locX;
@@ -405,9 +382,13 @@ public class PlayerChunkMap {
// CraftBukkit start - send nearest chunks first
Collections.sort(chunksToLoad, new ChunkCoordComparator(entityplayer));
synchronized (this) { // Akarin - synchronized
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
for (ChunkCoordIntPair pair : chunksToLoad) {
this.c(pair.x, pair.z).a(entityplayer);
// Paper start
PlayerChunk c = this.c(pair.x, pair.z);
c.checkHighPriority(entityplayer);
c.a(entityplayer);
// Paper end
}
} // Akarin
// CraftBukkit end
@@ -427,9 +408,10 @@ public class PlayerChunkMap {
i = MathHelper.clamp(i, 3, 32);
if (i != this.j) {
int j = i - this.j;
managedPlayersLock.readLock().lock(); // Akarin
ArrayList arraylist = Lists.newArrayList(this.managedPlayers);
managedPlayersLock.readLock().unlock(); // Akarin
ArrayList arraylist; // Akarin
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
arraylist = Lists.newArrayList(this.managedPlayers);
} // Akarin
Iterator iterator = arraylist.iterator();
while (iterator.hasNext()) {
@@ -458,8 +440,8 @@ public class PlayerChunkMap {
int i1;
int j1;
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
if (j > 0) {
synchronized (this) { // Akarin - synchronized
for (i1 = k - i; i1 <= k + i; ++i1) {
for (j1 = l - i; j1 <= l + i; ++j1) {
PlayerChunk playerchunk = this.c(i1, j1);
@@ -469,9 +451,7 @@ public class PlayerChunkMap {
}
}
}
} // Akarin
} else {
synchronized (this) { // Akarin - synchronized
for (i1 = k - oldViewDistance; i1 <= k + oldViewDistance; ++i1) {
for (j1 = l - oldViewDistance; j1 <= l + oldViewDistance; ++j1) {
if (!this.a(i1, j1, k, l, i)) {
@@ -479,11 +459,11 @@ public class PlayerChunkMap {
}
}
}
} // Akarin
if (markSort) {
this.e();
}
}
} // Akarin
}
}
// Paper end
@@ -501,12 +481,15 @@ public class PlayerChunkMap {
return (long) i + 2147483647L | (long) j + 2147483647L << 32;
}
public synchronized void a(PlayerChunk playerchunk) { // Akarin - synchronized
// org.spigotmc.AsyncCatcher.catchOp("Async Player Chunk Add"); // Paper // Akarin
public void a(PlayerChunk playerchunk) {
org.spigotmc.AsyncCatcher.catchOp("Async Player Chunk Add"); // Paper
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
this.f.add(playerchunk);
} // Akarin
}
public synchronized void b(PlayerChunk playerchunk) { // Akarin - synchronized
public void b(PlayerChunk playerchunk) {
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
org.spigotmc.AsyncCatcher.catchOp("Async Player Chunk Remove"); // Paper
ChunkCoordIntPair chunkcoordintpair = playerchunk.a();
long i = d(chunkcoordintpair.x, chunkcoordintpair.z);
@@ -528,6 +511,7 @@ public class PlayerChunkMap {
}
// Paper end
}
} // Akarin
}
@@ -595,6 +579,9 @@ public class PlayerChunkMap {
// Order matters
this.setViewDistance(player, toSet);
player.setViewDistance(playerViewDistance);
//Force update entity trackers
this.getWorld().getTracker().updatePlayer(player);
}
}
// Paper end

File diff suppressed because it is too large Load Diff

View File

@@ -1,127 +0,0 @@
package net.minecraft.server;
import com.destroystokyo.paper.exception.ServerInternalException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import com.destroystokyo.paper.PaperConfig; // Paper
import java.util.LinkedHashMap; // Paper
/**
* Akarin Changes Note
* 1) Removes unneed synchronization (performance)
*/
public class RegionFileCache {
public static final Map<File, RegionFile> a = new LinkedHashMap(PaperConfig.regionFileCacheSize, 0.75f, true); // Spigot - private -> public, Paper - HashMap -> LinkedHashMap
public static synchronized RegionFile a(File file, int i, int j) {
File file1 = new File(file, "region");
File file2 = new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca");
RegionFile regionfile = (RegionFile) RegionFileCache.a.get(file2);
if (regionfile != null) {
return regionfile;
} else {
if (!file1.exists()) {
file1.mkdirs();
}
if (RegionFileCache.a.size() >= PaperConfig.regionFileCacheSize) { // Paper
trimCache(); // Paper
}
RegionFile regionfile1 = new RegionFile(file2);
RegionFileCache.a.put(file2, regionfile1);
return regionfile1;
}
}
public static synchronized RegionFile b(File file, int i, int j) {
File file1 = new File(file, "region");
File file2 = new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca");
RegionFile regionfile = (RegionFile) RegionFileCache.a.get(file2);
if (regionfile != null) {
return regionfile;
} else if (file1.exists() && file2.exists()) {
if (RegionFileCache.a.size() >= 256) {
a();
}
RegionFile regionfile1 = new RegionFile(file2);
RegionFileCache.a.put(file2, regionfile1);
return regionfile1;
} else {
return null;
}
}
// Paper Start
private static synchronized void trimCache() {
Iterator<Map.Entry<File, RegionFile>> itr = RegionFileCache.a.entrySet().iterator();
int count = RegionFileCache.a.size() - PaperConfig.regionFileCacheSize;
while (count-- >= 0 && itr.hasNext()) {
try {
itr.next().getValue().c();
} catch (IOException ioexception) {
ioexception.printStackTrace();
ServerInternalException.reportInternalException(ioexception);
}
itr.remove();
}
}
// Paper End
public static synchronized void a() {
Iterator iterator = RegionFileCache.a.values().iterator();
while (iterator.hasNext()) {
RegionFile regionfile = (RegionFile) iterator.next();
try {
if (regionfile != null) {
regionfile.c();
}
} catch (IOException ioexception) {
ioexception.printStackTrace();
ServerInternalException.reportInternalException(ioexception); // Paper
}
}
RegionFileCache.a.clear();
}
// CraftBukkit start - call sites hoisted for synchronization
public static /*synchronized*/ NBTTagCompound d(File file, int i, int j) throws IOException { // Akarin - 1.13 backport - remove synchronization // OBFHELPER: read
RegionFile regionfile = a(file, i, j);
DataInputStream datainputstream = regionfile.a(i & 31, j & 31);
if (datainputstream == null) {
return null;
}
return NBTCompressedStreamTools.a(datainputstream);
}
public static /*synchronized*/ void e(File file, int i, int j, NBTTagCompound nbttagcompound) throws IOException { // Akarin - 1.13 backport - remove synchronization // OBFHELPER: write
RegionFile regionfile = a(file, i, j);
DataOutputStream dataoutputstream = regionfile.b(i & 31, j & 31);
NBTCompressedStreamTools.a(nbttagcompound, (java.io.DataOutput) dataoutputstream);
dataoutputstream.close();
}
// CraftBukkit end
public static /*synchronized*/ boolean chunkExists(File file, int i, int j) { // Akarin - 1.13 backport - remove synchronization
RegionFile regionfile = b(file, i, j);
return regionfile != null ? regionfile.c(i & 31, j & 31) : false;
}
}

View File

@@ -1,163 +0,0 @@
package net.minecraft.server;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterators;
import java.util.BitSet;
import java.util.Iterator;
import javax.annotation.Nullable;
/**
* Akarin Changes Note
* 1) BitSet for faster access (performance)
*/
public class RegistryID<K> implements Registry {
private static final Object a = null;
private K[] b;
private int[] c;
private K[] d;
private int e;
private int f;
private java.util.BitSet usedIds; // Akarin - 1.13 backport
public RegistryID(int i) {
i = (int) ((float) i / 0.8F);
this.b = (K[]) (new Object[i]);
this.c = new int[i];
this.d = (K[]) (new Object[i]);
this.usedIds = new BitSet(); // Akarin - 1.13 backport
}
public int getId(@Nullable K k0) {
return this.c(this.b(k0, this.d(k0)));
}
@Nullable
public K fromId(int i) {
return i >= 0 && i < this.d.length ? this.d[i] : null;
}
private int c(int i) {
return i == -1 ? -1 : this.c[i];
}
public int c(K k0) {
int i = this.c();
this.a(k0, i);
return i;
}
private int c() {
// Akarin start - 1.13 backport
/*
while (this.e < this.d.length && this.d[this.e] != null) {
++this.e;
}
*/
this.e = this.usedIds.nextClearBit(0);
// Akarin end - 1.13 backport
return this.e;
}
private void d(int i) {
K[] aobject = this.b;
int[] aint = this.c;
this.b = (K[]) (new Object[i]);
this.c = new int[i];
this.d = (K[]) (new Object[i]);
this.e = 0;
this.f = 0;
this.usedIds.clear(); // Akarin - 1.13 backport
for (int j = 0; j < aobject.length; ++j) {
if (aobject[j] != null) {
this.a(aobject[j], aint[j]);
}
}
}
public void a(K k0, int i) {
int j = Math.max(i, this.f + 1);
int k;
if ((float) j >= (float) this.b.length * 0.8F) {
for (k = this.b.length << 1; k < i; k <<= 1) {
;
}
this.d(k);
}
k = this.e(this.d(k0));
this.b[k] = k0;
this.c[k] = i;
this.d[i] = k0;
this.usedIds.set(i); // Akarin - 1.13 backport
++this.f;
if (i == this.e) {
++this.e;
}
}
private int d(@Nullable K k0) {
return (MathHelper.f(System.identityHashCode(k0)) & Integer.MAX_VALUE) % this.b.length;
}
private int b(@Nullable K k0, int i) {
int j;
for (j = i; j < this.b.length; ++j) {
if (this.b[j] == k0) {
return j;
}
if (this.b[j] == RegistryID.a) {
return -1;
}
}
for (j = 0; j < i; ++j) {
if (this.b[j] == k0) {
return j;
}
if (this.b[j] == RegistryID.a) {
return -1;
}
}
return -1;
}
private int e(int i) {
int j;
for (j = i; j < this.b.length; ++j) {
if (this.b[j] == RegistryID.a) {
return j;
}
}
for (j = 0; j < i; ++j) {
if (this.b[j] == RegistryID.a) {
return j;
}
}
throw new RuntimeException("Overflowed :(");
}
public Iterator<K> iterator() {
return Iterators.filter(Iterators.forArray(this.d), Predicates.notNull());
}
public int b() {
return this.f;
}
}

View File

@@ -9,9 +9,6 @@ import javax.annotation.Nullable;
// Spigot start
import com.google.common.base.Predicate;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.util.concurrent.Futures;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
@@ -30,13 +27,12 @@ import java.util.concurrent.Callable;
*/
public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Paper - remove tickable
private int a;
public int rotation;
private GameProfile g;
private int h;
private boolean i;
private static UserCache j;
private static MinecraftSessionService k;
private GameProfile a;
private int e;
private boolean f;
public boolean drop = true;
private static UserCache h;
private static MinecraftSessionService i;
// Spigot start
public static final ExecutorService executor = Executors.newFixedThreadPool(3,
new ThreadFactoryBuilder()
@@ -80,7 +76,7 @@ public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Pa
if ( property == null )
{
profile = MinecraftServer.getServer().az().fillProfileProperties( profile, true );
profile = TileEntitySkull.i.fillProfileProperties( profile, true );
}
}
@@ -90,24 +86,24 @@ public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Pa
} );
// Spigot end
public TileEntitySkull() {}
public TileEntitySkull() {
super(TileEntityTypes.SKULL);
}
public static void a(UserCache usercache) {
TileEntitySkull.j = usercache;
TileEntitySkull.h = usercache;
}
public static void a(MinecraftSessionService minecraftsessionservice) {
TileEntitySkull.k = minecraftsessionservice;
TileEntitySkull.i = minecraftsessionservice;
}
public NBTTagCompound save(NBTTagCompound nbttagcompound) {
super.save(nbttagcompound);
nbttagcompound.setByte("SkullType", (byte) (this.a & 255));
nbttagcompound.setByte("Rot", (byte) (this.rotation & 255));
if (this.g != null) {
if (this.a != null) {
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
GameProfileSerializer.serialize(nbttagcompound1, this.g);
GameProfileSerializer.serialize(nbttagcompound1, this.a);
nbttagcompound.set("Owner", nbttagcompound1);
}
@@ -116,30 +112,27 @@ public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Pa
public void load(NBTTagCompound nbttagcompound) {
super.load(nbttagcompound);
this.a = nbttagcompound.getByte("SkullType");
this.rotation = nbttagcompound.getByte("Rot");
if (this.a == 3) {
if (nbttagcompound.hasKeyOfType("Owner", 10)) {
this.g = GameProfileSerializer.deserialize(nbttagcompound.getCompound("Owner"));
this.setGameProfile(GameProfileSerializer.deserialize(nbttagcompound.getCompound("Owner")));
} else if (nbttagcompound.hasKeyOfType("ExtraType", 8)) {
String s = nbttagcompound.getString("ExtraType");
if (!UtilColor.b(s)) {
this.g = new GameProfile((UUID) null, s);
this.i();
}
this.setGameProfile(new GameProfile((UUID) null, s));
}
}
}
public void e() {
if (this.a == 5) {
public void Y_() {
Block block = this.getBlock().getBlock();
if (block == Blocks.DRAGON_HEAD || block == Blocks.DRAGON_WALL_HEAD) {
if (this.world.isBlockIndirectlyPowered(this.position)) {
this.i = true;
++this.h;
this.f = true;
++this.e;
} else {
this.i = false;
this.f = false;
}
}
@@ -147,43 +140,60 @@ public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Pa
@Nullable
public GameProfile getGameProfile() {
return this.g;
return this.a;
}
// Paper start
static NBTTagCompound sanitizeTileEntityUUID(NBTTagCompound cmp) {
NBTTagCompound owner = cmp.getCompound("Owner");
if (!owner.isEmpty()) {
sanitizeUUID(owner);
}
return cmp;
}
static void sanitizeUUID(NBTTagCompound owner) {
NBTTagCompound properties = owner.getCompound("Properties");
NBTTagList list = null;
if (!properties.isEmpty()) {
list = properties.getList("textures", 10);
}
if (list != null && !list.isEmpty()) {
String textures = ((NBTTagCompound)list.get(0)).getString("Value");
if (textures != null && textures.length() > 3) {
String uuid = UUID.nameUUIDFromBytes(textures.getBytes()).toString();
owner.setString("Id", uuid);
return;
}
}
owner.setString("Id", UUID.randomUUID().toString());
}
// Paper end
@Nullable
public PacketPlayOutTileEntityData getUpdatePacket() {
return new PacketPlayOutTileEntityData(this.position, 4, this.d());
return new PacketPlayOutTileEntityData(this.position, 4, sanitizeTileEntityUUID(this.aa_())); // Paper
}
public NBTTagCompound d() {
public NBTTagCompound aa_() {
return this.save(new NBTTagCompound());
}
public void setSkullType(int i) {
this.a = i;
this.g = null;
}
public void setGameProfile(@Nullable GameProfile gameprofile) {
this.a = 3;
this.g = gameprofile;
this.i();
this.a = gameprofile;
this.f();
}
private void i() {
private void f() {
// Spigot start
GameProfile profile = this.g;
setSkullType( 0 ); // Work around client bug
GameProfile profile = this.getGameProfile();
b(profile, new Predicate<GameProfile>() {
@Override
public boolean apply(GameProfile input) {
setSkullType(3); // Work around client bug
g = input;
a = input;
update();
if (world != null) {
world.m(position); // PAIL: notify
}
return false;
}
}, false);
@@ -241,25 +251,24 @@ public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Pa
}
// Spigot end
public int getSkullType() {
return this.a;
// CraftBukkit start
public static void a(IBlockAccess iblockaccess, BlockPosition blockposition) {
setShouldDrop(iblockaccess, blockposition, false);
}
public void setRotation(int i) {
this.rotation = i;
}
public static void setShouldDrop(IBlockAccess iblockaccess, BlockPosition blockposition, boolean flag) {
// CraftBukkit end
TileEntity tileentity = iblockaccess.getTileEntity(blockposition);
public void a(EnumBlockMirror enumblockmirror) {
if (this.world != null && this.world.getType(this.getPosition()).get(BlockSkull.FACING) == EnumDirection.UP) {
this.rotation = enumblockmirror.a(this.rotation, 16);
if (tileentity instanceof TileEntitySkull) {
TileEntitySkull tileentityskull = (TileEntitySkull) tileentity;
tileentityskull.drop = flag; // CraftBukkit
}
}
public void a(EnumBlockRotation enumblockrotation) {
if (this.world != null && this.world.getType(this.getPosition()).get(BlockSkull.FACING) == EnumDirection.UP) {
this.rotation = enumblockrotation.a(this.rotation, 16);
}
public boolean shouldDrop() {
return this.drop;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
@@ -17,6 +18,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
@@ -35,6 +37,7 @@ import org.bukkit.Server;
import org.bukkit.UnsafeValues;
import org.bukkit.Warning.WarningState;
import org.bukkit.World;
import org.bukkit.StructureType;
import org.bukkit.World.Environment;
import org.bukkit.WorldCreator;
import org.bukkit.boss.BarColor;
@@ -52,12 +55,12 @@ import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.conversations.Conversable;
import org.bukkit.craftbukkit.boss.CraftBossBar;
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.generator.CraftChunkData;
import org.bukkit.craftbukkit.help.SimpleHelpMap;
import org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe;
import org.bukkit.craftbukkit.inventory.CraftInventoryCustom;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.inventory.CraftMerchantCustom;
import org.bukkit.craftbukkit.inventory.CraftRecipe;
@@ -95,6 +98,8 @@ import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.Recipe;
import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.ShapelessRecipe;
import org.bukkit.loot.LootTable;
import org.bukkit.map.MapView;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.Plugin;
@@ -123,33 +128,53 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
import it.unimi.dsi.fastutil.longs.LongIterator;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import org.bukkit.Keyed;
import org.apache.commons.lang.StringUtils;
import org.bukkit.NamespacedKey;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.craftbukkit.command.BukkitCommandWrapper;
import org.bukkit.craftbukkit.command.CraftCommandMap;
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
import org.bukkit.craftbukkit.inventory.util.CraftInventoryCreator;
import org.bukkit.craftbukkit.tag.CraftBlockTag;
import org.bukkit.craftbukkit.tag.CraftItemTag;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.event.server.ServerLoadEvent;
import org.bukkit.event.server.TabCompleteEvent;
import net.md_5.bungee.api.chat.BaseComponent;
import javax.annotation.Nullable; // Paper
import javax.annotation.Nonnull; // Paper
/**
* Akarin Changes Note
* 1) Guava -> Caffeine (performance)
*/
public final class CraftServer implements Server {
private final String serverName = "Paper";
private final String serverName = "Paper"; // Paper
private final String serverVersion;
private final String bukkitVersion = Versioning.getBukkitVersion();
private final Logger logger = Logger.getLogger("Minecraft");
private final ServicesManager servicesManager = new SimpleServicesManager();
private final CraftScheduler scheduler = new CraftScheduler();
private final SimpleCommandMap commandMap = new SimpleCommandMap(this);
private final CraftCommandMap commandMap = new CraftCommandMap(this);
private final SimpleHelpMap helpMap = new SimpleHelpMap(this);
private final StandardMessenger messenger = new StandardMessenger();
private final SimplePluginManager pluginManager = new SimplePluginManager(this, commandMap);
@@ -177,8 +202,7 @@ public final class CraftServer implements Server {
private boolean printSaveWarning;
private CraftIconCache icon;
private boolean overrideAllCommandBlockCommands = false;
private boolean unrestrictedAdvancements;
private boolean unrestrictedSignCommands; // Paper
public boolean ignoreVanillaPermissions = false;
private final List<CraftPlayer> playerView;
public int reloadCount;
public static Exception excessiveVelEx; // Paper - Velocity warnings
@@ -256,13 +280,7 @@ public final class CraftServer implements Server {
saveCommandsConfig();
overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*");
unrestrictedAdvancements = commandsConfiguration.getBoolean("unrestricted-advancements");
// Paper start
unrestrictedSignCommands = commandsConfiguration.getBoolean("unrestricted-signs");
if (unrestrictedSignCommands) {
logger.warning("Warning: Commands are no longer restricted on signs. If you allow players to use Creative Mode, there may be risk of players bypassing permissions. Use this setting at your own risk!!!!");
}
// Paper end
ignoreVanillaPermissions = commandsConfiguration.getBoolean("ignore-vanilla-permissions");
pluginManager.useTimings(configuration.getBoolean("settings.plugin-profiling"));
monsterSpawn = configuration.getInt("spawn-limits.monsters");
animalSpawn = configuration.getInt("spawn-limits.animals");
@@ -275,15 +293,6 @@ public final class CraftServer implements Server {
loadIcon();
}
public boolean getPermissionOverride(ICommandListener listener) {
while (listener instanceof CommandListenerWrapper) {
listener = ((CommandListenerWrapper) listener).base;
}
if (unrestrictedSignCommands && listener instanceof TileEntitySign.ISignCommandListener) return true; // Paper
return unrestrictedAdvancements && listener instanceof AdvancementRewards.AdvancementCommandListener;
}
public boolean getCommandBlockOverride(String command) {
return overrideAllCommandBlockCommands || commandsConfiguration.getStringList("command-block-overrides").contains(command);
}
@@ -359,6 +368,7 @@ public final class CraftServer implements Server {
DefaultPermissions.registerCorePermissions();
CraftDefaultPermissions.registerCorePermissions();
helpMap.initializeCommands();
syncCommands();
}
}
@@ -367,10 +377,12 @@ public final class CraftServer implements Server {
}
private void setVanillaCommands(boolean first) { // Spigot
Map<String, ICommand> commands = new CommandDispatcher(console).getCommands();
for (ICommand cmd : commands.values()) {
CommandDispatcher dispatcher = console.vanillaCommandDispatcher;
// Build a list of all Vanilla commands and create wrappers
for (CommandNode<CommandListenerWrapper> cmd : dispatcher.a().getRoot().getChildren()) {
// Spigot start
VanillaCommandWrapper wrapper = new VanillaCommandWrapper((CommandAbstract) cmd, LocaleI18n.get(cmd.getUsage(null)));
VanillaCommandWrapper wrapper = new VanillaCommandWrapper(dispatcher, cmd);
if (org.spigotmc.SpigotConfig.replaceCommands.contains( wrapper.getName() ) ) {
if (first) {
commandMap.register("minecraft", wrapper);
@@ -382,6 +394,38 @@ public final class CraftServer implements Server {
}
}
private void syncCommands() {
// Clear existing commands
CommandDispatcher dispatcher = console.commandDispatcher = new CommandDispatcher();
// Register all commands, vanilla ones will be using the old dispatcher references
for (Map.Entry<String, Command> entry : commandMap.getKnownCommands().entrySet()) {
String label = entry.getKey();
Command command = entry.getValue();
if (command instanceof VanillaCommandWrapper) {
LiteralCommandNode<CommandListenerWrapper> node = (LiteralCommandNode<CommandListenerWrapper>) ((VanillaCommandWrapper) command).vanillaCommand;
if (!node.getLiteral().equals(label)) {
LiteralCommandNode<CommandListenerWrapper> clone = new LiteralCommandNode(label, node.getCommand(), node.getRequirement(), node.getRedirect(), node.getRedirectModifier(), node.isFork());
for (CommandNode<CommandListenerWrapper> child : node.getChildren()) {
clone.addChild(child);
}
node = clone;
}
dispatcher.a().getRoot().addChild(node);
} else {
new BukkitCommandWrapper(this, entry.getValue()).register(dispatcher.a(), label);
}
}
// Refresh commands
for (EntityPlayer player : getHandle().players) {
dispatcher.a(player);
}
}
private void enablePlugin(Plugin plugin) {
try {
List<Permission> perms = plugin.getDescription().getPermissions();
@@ -661,6 +705,7 @@ public final class CraftServer implements Server {
public boolean dispatchCommand(CommandSender sender, String commandLine) {
Validate.notNull(sender, "Sender cannot be null");
Validate.notNull(commandLine, "CommandLine cannot be null");
org.spigotmc.AsyncCatcher.catchOp( "command dispatch" ); // Spigot
// Paper Start
if (!org.spigotmc.AsyncCatcher.shuttingDown && !Bukkit.isPrimaryThread()) {
@@ -690,7 +735,7 @@ public final class CraftServer implements Server {
}
// Spigot start
if (StringUtils.isNotEmpty(org.spigotmc.SpigotConfig.unknownCommandMessage)) {
if (!org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty()) {
// Paper start
UnknownCommandEvent event = new UnknownCommandEvent(sender, commandLine, org.spigotmc.SpigotConfig.unknownCommandMessage);
Bukkit.getServer().getPluginManager().callEvent(event);
@@ -714,8 +759,8 @@ public final class CraftServer implements Server {
((DedicatedServer) console).propertyManager = config;
boolean animals = config.getBoolean("spawn-animals", console.getSpawnAnimals());
boolean monsters = config.getBoolean("spawn-monsters", console.worlds.get(0).getDifficulty() != EnumDifficulty.PEACEFUL);
EnumDifficulty difficulty = EnumDifficulty.getById(config.getInt("difficulty", console.worlds.get(0).getDifficulty().ordinal()));
boolean monsters = config.getBoolean("spawn-monsters", console.getWorldServer(DimensionManager.OVERWORLD).getDifficulty() != EnumDifficulty.PEACEFUL);
EnumDifficulty difficulty = EnumDifficulty.getById(config.getInt("difficulty", console.getWorldServer(DimensionManager.OVERWORLD).getDifficulty().ordinal()));
online.value = config.getBoolean("online-mode", console.getOnlineMode());
console.setSpawnAnimals(config.getBoolean("spawn-animals", console.getSpawnAnimals()));
@@ -746,7 +791,7 @@ public final class CraftServer implements Server {
org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot
com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings")); // Paper
for (WorldServer world : console.worlds) {
for (WorldServer world : console.getWorlds()) {
world.worldData.setDifficulty(difficulty);
world.setSpawnFlags(monsters, animals);
if (this.getTicksPerAnimalSpawns() < 0) {
@@ -781,6 +826,7 @@ public final class CraftServer implements Server {
org.spigotmc.SpigotConfig.registerCommands(); // Spigot
com.destroystokyo.paper.PaperConfig.registerCommands(); // Paper
overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*");
ignoreVanillaPermissions = commandsConfiguration.getBoolean("ignore-vanilla-permissions");
int pollCount = 0;
@@ -809,6 +855,7 @@ public final class CraftServer implements Server {
loadPlugins();
enablePlugins(PluginLoadOrder.STARTUP);
enablePlugins(PluginLoadOrder.POSTWORLD);
getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD));
}
@Override
@@ -919,31 +966,13 @@ public final class CraftServer implements Server {
generator = getGenerator(name);
}
Convertable converter = new WorldLoaderServer(getWorldContainer(), getHandle().getServer().dataConverterManager);
if (converter.isConvertable(name)) {
getLogger().info("Converting world '" + name + "'");
converter.convert(name, new IProgressUpdate() {
private long b = System.currentTimeMillis();
console.convertWorld(name);
public void a(String s) {}
public void a(int i) {
if (System.currentTimeMillis() - this.b >= 1000L) {
this.b = System.currentTimeMillis();
MinecraftServer.LOGGER.info("Converting... " + i + "%");
}
}
public void c(String s) {}
});
}
int dimension = CraftWorld.CUSTOM_DIMENSION_OFFSET + console.worlds.size();
int dimension = CraftWorld.CUSTOM_DIMENSION_OFFSET + console.worldServer.size();
boolean used = false;
do {
for (WorldServer server : console.worlds) {
used = server.dimension == dimension;
for (WorldServer server : console.getWorlds()) {
used = server.dimension.getDimensionID() + 1 == dimension; // Paper - getDimensionID returns the dimension - 1, so we have to add 1
if (used) {
dimension++;
break;
@@ -952,16 +981,22 @@ public final class CraftServer implements Server {
} while(used);
boolean hardcore = false;
IDataManager sdm = new ServerNBTManager(getWorldContainer(), name, true, getHandle().getServer().dataConverterManager);
IDataManager sdm = new ServerNBTManager(getWorldContainer(), name, getServer(), getHandle().getServer().dataConverterManager);
PersistentCollection persistentcollection = new PersistentCollection(sdm);
WorldData worlddata = sdm.getWorldData();
WorldSettings worldSettings = null;
if (worlddata == null) {
worldSettings = new WorldSettings(creator.seed(), EnumGamemode.getById(getDefaultGameMode().getValue()), generateStructures, hardcore, type);
worldSettings.setGeneratorSettings(creator.generatorSettings());
worldSettings = new WorldSettings(com.destroystokyo.paper.PaperConfig.seedOverride.getOrDefault(name, creator.seed()), EnumGamemode.getById(getDefaultGameMode().getValue()), generateStructures, hardcore, type); // Paper
JsonElement parsedSettings = new JsonParser().parse(creator.generatorSettings());
if (parsedSettings.isJsonObject()) {
worldSettings.setGeneratorSettings(parsedSettings.getAsJsonObject());
}
worlddata = new WorldData(worldSettings, name);
}
worlddata.checkName(name); // CraftBukkit - Migration did not rewrite the level.dat; This forces 1.8 to take the last loaded world as respawn (in this case the end)
WorldServer internal = (WorldServer) new WorldServer(console, sdm, worlddata, dimension, console.methodProfiler, creator.environment(), generator).b();
DimensionManager internalDimension = new DimensionManager(dimension, name, name, () -> DimensionManager.a(creator.environment().getId()).e());
WorldServer internal = (WorldServer) new WorldServer(console, sdm, persistentcollection, worlddata, internalDimension, console.methodProfiler, creator.environment(), generator).i_();
if (!(worlds.containsKey(name.toLowerCase(java.util.Locale.ENGLISH)))) {
return null;
@@ -970,22 +1005,25 @@ public final class CraftServer implements Server {
if (worldSettings != null) {
internal.a(worldSettings);
}
internal.scoreboard = getScoreboardManager().getMainScoreboard().getHandle();
internal.tracker = new EntityTracker(internal);
internal.addIWorldAccess(new WorldManager(console, internal));
internal.worldData.setDifficulty(EnumDifficulty.EASY);
internal.setSpawnFlags(true, true);
console.worlds.add(internal);
console.worldServer.put(internal.dimension, internal);
pluginManager.callEvent(new WorldInitEvent(internal.getWorld()));
System.out.println("Preparing start region for level " + (console.worlds.size() - 1) + " (Seed: " + internal.getSeed() + ")");
System.out.println("Preparing start region for level " + (console.worldServer.size() - 1) + " (Seed: " + internal.getSeed() + ")");
if (internal.getWorld().getKeepSpawnInMemory()) {
short short1 = internal.paperConfig.keepLoadedRange; // Paper
long i = System.currentTimeMillis();
for (int j = -short1; j <= short1; j += 16) {
for (int k = -short1; k <= short1; k += 16) {
// Paper start
for (ChunkCoordIntPair coords : internal.getChunkProviderServer().getSpiralOutChunks(internal.getSpawn(), short1 >> 4)) {{
int j = coords.x;
int k = coords.z;
// Paper end
long l = System.currentTimeMillis();
if (l < i) {
@@ -1001,10 +1039,26 @@ public final class CraftServer implements Server {
}
BlockPosition chunkcoordinates = internal.getSpawn();
internal.getChunkProviderServer().getChunkAt(chunkcoordinates.getX() + j >> 4, chunkcoordinates.getZ() + k >> 4);
internal.getChunkProviderServer().getChunkAt(chunkcoordinates.getX() + j >> 4, chunkcoordinates.getZ() + k >> 4, true, true, c -> {}); // Paper
}
}
}
DimensionManager dimensionmanager = internalDimension.e().getDimensionManager();
ForcedChunk forcedchunk = (ForcedChunk) persistentcollection.get(dimensionmanager, ForcedChunk::new, "chunks");
if (forcedchunk != null) {
LongIterator longiterator = forcedchunk.a().iterator();
while (longiterator.hasNext()) {
System.out.println("Loading forced chunks for dimension " + dimension + ", " + forcedchunk.a().size() * 100 / 625 + "%");
long k = longiterator.nextLong();
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(k);
internal.getChunkProviderServer().getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z, true, true);
}
}
pluginManager.callEvent(new WorldLoadEvent(internal.getWorld()));
return internal.getWorld();
}
@@ -1022,11 +1076,11 @@ public final class CraftServer implements Server {
WorldServer handle = ((CraftWorld) world).getHandle();
if (!(console.worlds.contains(handle))) {
if (!(console.worldServer.containsKey(handle.dimension))) {
return false;
}
if (handle.dimension == 0) {
if (handle.dimension == DimensionManager.OVERWORLD) {
return false;
}
@@ -1044,14 +1098,14 @@ public final class CraftServer implements Server {
if (save) {
try {
handle.save(true, null);
handle.saveLevel();
handle.close();
} catch (ExceptionWorldConflict ex) {
getLogger().log(Level.SEVERE, null, ex);
}
}
worlds.remove(world.getName().toLowerCase(java.util.Locale.ENGLISH));
console.worlds.remove(console.worlds.indexOf(handle));
console.worldServer.remove(handle.dimension);
File parentFolder = world.getWorldFolder().getAbsoluteFile();
@@ -1186,19 +1240,12 @@ public final class CraftServer implements Server {
@Override
public void clearRecipes() {
CraftingManager.recipes = new RegistryMaterials();
RecipesFurnace.getInstance().recipes.clear();
RecipesFurnace.getInstance().customRecipes.clear();
RecipesFurnace.getInstance().customExperience.clear();
console.getCraftingManager().recipes.clear();
}
@Override
public void resetRecipes() {
CraftingManager.recipes = new RegistryMaterials();
CraftingManager.init();
RecipesFurnace.getInstance().recipes = new RecipesFurnace().recipes;
RecipesFurnace.getInstance().customRecipes.clear();
RecipesFurnace.getInstance().customExperience.clear();
console.getCraftingManager().a(console.getResourceManager());
}
@Override
@@ -1302,8 +1349,8 @@ public final class CraftServer implements Server {
@Override
@Deprecated
public CraftMapView getMap(short id) {
PersistentCollection collection = console.worlds.get(0).worldMaps;
WorldMap worldmap = (WorldMap) collection.get(WorldMap.class, "map_" + id);
PersistentCollection collection = console.getWorldServer(DimensionManager.OVERWORLD).worldMaps;
WorldMap worldmap = (WorldMap) collection.get(DimensionManager.OVERWORLD, WorldMap::new, "map_" + id);
if (worldmap == null) {
return null;
}
@@ -1314,11 +1361,35 @@ public final class CraftServer implements Server {
public CraftMapView createMap(World world) {
Validate.notNull(world, "World cannot be null");
net.minecraft.server.ItemStack stack = new net.minecraft.server.ItemStack(Items.MAP, 1, -1);
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(stack, ((CraftWorld) world).getHandle());
net.minecraft.server.ItemStack stack = new net.minecraft.server.ItemStack(Items.MAP, 1);
WorldMap worldmap = ItemWorldMap.getSavedMap(stack, ((CraftWorld) world).getHandle());
return worldmap.mapView;
}
@Override
public ItemStack createExplorerMap(World world, Location location, StructureType structureType) {
return this.createExplorerMap(world, location, structureType, 100, true);
}
@Override
public ItemStack createExplorerMap(World world, Location location, StructureType structureType, int radius, boolean findUnexplored) {
Validate.notNull(world, "World cannot be null");
Validate.notNull(structureType, "StructureType cannot be null");
Validate.notNull(structureType.getMapIcon(), "Cannot create explorer maps for StructureType " + structureType.getName());
WorldServer worldServer = ((CraftWorld) world).getHandle();
Location structureLocation = world.locateNearestStructure(location, structureType, radius, findUnexplored);
BlockPosition structurePosition = new BlockPosition(structureLocation.getBlockX(), structureLocation.getBlockY(), structureLocation.getBlockZ());
// Create map with trackPlayer = true, unlimitedTracking = true
net.minecraft.server.ItemStack stack = ItemWorldMap.a(worldServer, structurePosition.getX(), structurePosition.getZ(), MapView.Scale.NORMAL.getValue(), true, true); //PAIL rename setFilledMapView
ItemWorldMap.a(worldServer, stack); // PAIL rename sepiaMapFilter
// "+" map ID taken from EntityVillager
ItemWorldMap.getSavedMap(stack, worldServer).a(stack, structurePosition, "+", MapIcon.Type.a(structureType.getMapIcon().getValue())); // PAIL rename decorateMap
return CraftItemStack.asBukkitCopy(stack);
}
@Override
public void shutdown() {
console.safeShutdown();
@@ -1503,7 +1574,7 @@ public final class CraftServer implements Server {
@Override
public GameMode getDefaultGameMode() {
return GameMode.getByValue(console.worlds.get(0).getWorldData().getGameType().getId());
return GameMode.getByValue(console.getWorldServer(DimensionManager.OVERWORLD).getWorldData().getGameType().getId());
}
@Override
@@ -1547,7 +1618,7 @@ public final class CraftServer implements Server {
@Override
public OfflinePlayer[] getOfflinePlayers() {
WorldNBTStorage storage = (WorldNBTStorage) console.worlds.get(0).getDataManager();
WorldNBTStorage storage = (WorldNBTStorage) console.getWorldServer(DimensionManager.OVERWORLD).getDataManager();
String[] files = storage.getPlayerDir().list(new DatFileFilter());
Set<OfflinePlayer> players = new HashSet<OfflinePlayer>();
@@ -1591,25 +1662,26 @@ public final class CraftServer implements Server {
@Override
public Inventory createInventory(InventoryHolder owner, InventoryType type) {
// TODO: Create the appropriate type, rather than Custom?
return new CraftInventoryCustom(owner, type);
Validate.isTrue(type.isCreatable(), "Cannot open an inventory of type ", type);
return CraftInventoryCreator.INSTANCE.createInventory(owner, type);
}
@Override
public Inventory createInventory(InventoryHolder owner, InventoryType type, String title) {
return new CraftInventoryCustom(owner, type, title);
Validate.isTrue(type.isCreatable(), "Cannot open an inventory of type ", type);
return CraftInventoryCreator.INSTANCE.createInventory(owner, type, title);
}
@Override
public Inventory createInventory(InventoryHolder owner, int size) throws IllegalArgumentException {
Validate.isTrue(size % 9 == 0, "Chests must have a size that is a multiple of 9!");
return new CraftInventoryCustom(owner, size);
return CraftInventoryCreator.INSTANCE.createInventory(owner, size);
}
@Override
public Inventory createInventory(InventoryHolder owner, int size, String title) throws IllegalArgumentException {
Validate.isTrue(size % 9 == 0, "Chests must have a size that is a multiple of 9!");
return new CraftInventoryCustom(owner, size, title);
return CraftInventoryCreator.INSTANCE.createInventory(owner, size, title);
}
@Override
@@ -1662,26 +1734,26 @@ public final class CraftServer implements Server {
return warningState;
}
public List<String> tabComplete(net.minecraft.server.ICommandListener sender, String message, BlockPosition pos, boolean forceCommand) {
if (!(sender instanceof EntityPlayer)) {
public List<String> tabComplete(CommandSender sender, String message, WorldServer world, Vec3D pos, boolean forceCommand) {
if (!(sender instanceof Player)) {
return ImmutableList.of();
}
List<String> offers;
Player player = ((EntityPlayer) sender).getBukkitEntity();
Player player = (Player) sender;
if (message.startsWith("/") || forceCommand) {
offers = tabCompleteCommand(player, message, pos);
offers = tabCompleteCommand(player, message, world, pos);
} else {
offers = tabCompleteChat(player, message);
}
TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers, message.startsWith("/") || forceCommand, pos != null ? MCUtil.toLocation(((CraftWorld) player.getWorld()).getHandle(), pos) : null); // Paper
TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers, message.startsWith("/") || forceCommand, pos != null ? MCUtil.toLocation(((CraftWorld) player.getWorld()).getHandle(), new BlockPosition(pos)) : null); // Paper
getPluginManager().callEvent(tabEvent);
return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions();
}
public List<String> tabCompleteCommand(Player player, String message, BlockPosition pos) {
public List<String> tabCompleteCommand(Player player, String message, WorldServer world, Vec3D pos) {
// Spigot Start
if ( (org.spigotmc.SpigotConfig.tabComplete < 0 || message.length() <= org.spigotmc.SpigotConfig.tabComplete) && !message.contains( " " ) )
{
@@ -1698,7 +1770,7 @@ public final class CraftServer implements Server {
if (pos == null) {
completions = getCommandMap().tabComplete(player, message);
} else {
completions = getCommandMap().tabComplete(player, message, new Location(player.getWorld(), pos.getX(), pos.getY(), pos.getZ()));
completions = getCommandMap().tabComplete(player, message, new Location(world.getWorld(), pos.x, pos.y, pos.z));
}
} catch (CommandException ex) {
player.sendMessage(ChatColor.RED + "An internal error occurred while attempting to tab-complete this command");
@@ -1779,9 +1851,9 @@ public final class CraftServer implements Server {
Validate.isTrue(image.getWidth() == 64, "Must be 64 pixels wide");
Validate.isTrue(image.getHeight() == 64, "Must be 64 pixels high");
ImageIO.write(image, "PNG", new ByteBufOutputStream(bytebuf));
ByteBuf bytebuf1 = Base64.encode(bytebuf);
ByteBuffer bytebuffer = Base64.getEncoder().encode(bytebuf.nioBuffer());
return new CraftIconCache("data:image/png;base64," + bytebuf1.toString(Charsets.UTF_8));
return new CraftIconCache("data:image/png;base64," + StandardCharsets.UTF_8.decode(bytebuffer));
}
@Override
@@ -1796,6 +1868,7 @@ public final class CraftServer implements Server {
@Override
public ChunkGenerator.ChunkData createChunkData(World world) {
Validate.notNull(world, "World cannot be null");
return new CraftChunkData(world);
}
@@ -1807,8 +1880,15 @@ public final class CraftServer implements Server {
@Override
public Entity getEntity(UUID uuid) {
Validate.notNull(uuid, "UUID cannot be null");
net.minecraft.server.Entity entity = console.a(uuid); // PAIL: getEntity
return entity == null ? null : entity.getBukkitEntity();
for (WorldServer world : getServer().getWorlds()) {
net.minecraft.server.Entity entity = world.getEntity(uuid);
if (entity != null) {
return entity.getBukkitEntity();
}
}
return null;
}
@Override
@@ -1821,7 +1901,7 @@ public final class CraftServer implements Server {
@Override
public Iterator<org.bukkit.advancement.Advancement> advancementIterator() {
return Iterators.unmodifiableIterator(Iterators.transform(console.getAdvancementData().c().iterator(), new Function<Advancement, org.bukkit.advancement.Advancement>() { // PAIL: rename
return Iterators.unmodifiableIterator(Iterators.transform(console.getAdvancementData().b().iterator(), new Function<Advancement, org.bukkit.advancement.Advancement>() { // PAIL: rename
@Override
public org.bukkit.advancement.Advancement apply(Advancement advancement) {
return advancement.bukkit;
@@ -1829,6 +1909,64 @@ public final class CraftServer implements Server {
}));
}
@Override
public BlockData createBlockData(org.bukkit.Material material) {
Validate.isTrue(material != null, "Must provide material");
return createBlockData(material, (String) null);
}
@Override
public BlockData createBlockData(org.bukkit.Material material, Consumer<BlockData> consumer) {
BlockData data = createBlockData(material);
if (consumer != null) {
consumer.accept(data);
}
return data;
}
@Override
public BlockData createBlockData(String data) throws IllegalArgumentException {
Validate.isTrue(data != null, "Must provide data");
return createBlockData(null, data);
}
@Override
public BlockData createBlockData(org.bukkit.Material material, String data) {
Validate.isTrue(material != null || data != null, "Must provide one of material or data");
return CraftBlockData.newData(material, data);
}
@Override
@SuppressWarnings("unchecked")
public <T extends Keyed> org.bukkit.Tag<T> getTag(String registry, NamespacedKey tag, Class<T> clazz) {
MinecraftKey key = CraftNamespacedKey.toMinecraft(tag);
switch (registry) {
case org.bukkit.Tag.REGISTRY_BLOCKS:
Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Block namespace must have material type");
return (org.bukkit.Tag<T>) new CraftBlockTag(console.getTagRegistry().a(), key);
case org.bukkit.Tag.REGISTRY_ITEMS:
Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Item namespace must have material type");
return (org.bukkit.Tag<T>) new CraftItemTag(console.getTagRegistry().b(), key);
default:
throw new IllegalArgumentException();
}
}
public LootTable getLootTable(NamespacedKey key) {
Validate.notNull(key, "NamespacedKey cannot be null");
LootTableRegistry registry = getServer().getLootTableRegistry();
return new CraftLootTable(key, registry.getLootTable(CraftNamespacedKey.toMinecraft(key)));
}
@Deprecated
@Override
public UnsafeValues getUnsafe() {

View File

@@ -5,9 +5,10 @@ import com.destroystokyo.paper.profile.CraftPlayerProfile;
import com.destroystokyo.paper.profile.PlayerProfile;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.io.BaseEncoding;
import com.mojang.authlib.GameProfile;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import io.netty.buffer.Unpooled;
import java.io.ByteArrayOutputStream;
@@ -17,6 +18,7 @@ import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@@ -28,8 +30,48 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import net.md_5.bungee.api.chat.BaseComponent;
import net.minecraft.server.*;
import net.minecraft.server.PacketPlayOutTitle.EnumTitleAction;
import net.minecraft.server.AdvancementDataPlayer;
import net.minecraft.server.AdvancementProgress;
import net.minecraft.server.AttributeInstance;
import net.minecraft.server.AttributeMapServer;
import net.minecraft.server.AttributeModifiable;
import net.minecraft.server.AttributeRanged;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.ChatComponentText;
import net.minecraft.server.Container;
import net.minecraft.server.Entity;
import net.minecraft.server.EntityHuman;
import net.minecraft.server.EntityLiving;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.EntityTracker;
import net.minecraft.server.EntityTrackerEntry;
import net.minecraft.server.EnumChatFormat;
import net.minecraft.server.EnumGamemode;
import net.minecraft.server.IChatBaseComponent;
import net.minecraft.server.MapIcon;
import net.minecraft.server.MinecraftKey;
import net.minecraft.server.NBTTagCompound;
import net.minecraft.server.PacketDataSerializer;
import net.minecraft.server.PacketPlayOutBlockChange;
import net.minecraft.server.PacketPlayOutChat;
import net.minecraft.server.PacketPlayOutCustomPayload;
import net.minecraft.server.PacketPlayOutCustomSoundEffect;
import net.minecraft.server.PacketPlayOutMap;
import net.minecraft.server.PacketPlayOutNamedSoundEffect;
import net.minecraft.server.PacketPlayOutPlayerInfo;
import net.minecraft.server.PacketPlayOutPlayerListHeaderFooter;
import net.minecraft.server.PacketPlayOutSpawnPosition;
import net.minecraft.server.PacketPlayOutStopSound;
import net.minecraft.server.PacketPlayOutTitle;
import net.minecraft.server.PacketPlayOutUpdateAttributes;
import net.minecraft.server.PacketPlayOutUpdateHealth;
import net.minecraft.server.PacketPlayOutWorldEvent;
import net.minecraft.server.PacketPlayOutWorldParticles;
import net.minecraft.server.PlayerConnection;
import net.minecraft.server.TileEntitySign;
import net.minecraft.server.Vec3D;
import net.minecraft.server.WhiteListEntry;
import net.minecraft.server.WorldServer;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.NotImplementedException;
@@ -40,12 +82,14 @@ import org.bukkit.Statistic;
import org.bukkit.Material;
import org.bukkit.Statistic.Type;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import org.bukkit.configuration.serialization.DelegateDeserialization;
import org.bukkit.conversations.Conversation;
import org.bukkit.conversations.ConversationAbandonedEvent;
import org.bukkit.conversations.ManuallyAbandonedConversationCanceller;
import org.bukkit.craftbukkit.*;
import org.bukkit.craftbukkit.CraftParticle;
import org.bukkit.craftbukkit.block.CraftSign;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.craftbukkit.conversations.ConversationTracker;
import org.bukkit.craftbukkit.CraftEffect;
import org.bukkit.craftbukkit.CraftOfflinePlayer;
@@ -78,6 +122,7 @@ import javax.annotation.Nullable;
/**
* Akarin Changes Note
* 1) Make hidden players thread-safe (safety issue)
* 2) Add locks for entry operation (safety issue)
*/
@DelegateDeserialization(CraftOfflinePlayer.class)
public class CraftPlayer extends CraftHumanEntity implements Player {
@@ -86,7 +131,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
private boolean hasPlayedBefore = false;
private final ConversationTracker conversationTracker = new ConversationTracker();
private final Set<String> channels = new HashSet<String>();
private final Map<UUID, Set<WeakReference<Plugin>>> hiddenPlayers = Maps.newConcurrentMap(); // new HashMap<>(); // Akarin
private final Map<UUID, Set<WeakReference<Plugin>>> hiddenPlayers = new java.util.concurrent.ConcurrentHashMap<>(); // Akarin
private static final WeakHashMap<Plugin, WeakReference<Plugin>> pluginWeakReferences = new WeakHashMap<>();
private int hash = 0;
private double health = 20;
@@ -126,12 +171,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
perm.recalculatePermissions();
}
@Override
public boolean isOnline() {
return server.getPlayer(getUniqueId()) != null;
}
@Override
public InetSocketAddress getAddress() {
if (getHandle().playerConnection == null) return null;
@@ -193,7 +236,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void sendActionBar(String message) {
if (getHandle().playerConnection == null || message == null || message.isEmpty()) return;
getHandle().playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(message), ChatMessageType.GAME_INFO));
getHandle().playerConnection.sendPacket(new PacketPlayOutChat(new net.minecraft.server.ChatComponentText(message), net.minecraft.server.ChatMessageType.GAME_INFO));
}
@Override
@@ -204,10 +247,21 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void setPlayerListHeaderFooter(BaseComponent[] header, BaseComponent[] footer) {
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
packet.header = header;
packet.footer = footer;
getHandle().playerConnection.sendPacket(packet);
if (header != null) {
String headerJson = net.md_5.bungee.chat.ComponentSerializer.toString(footer);
playerListHeader = net.minecraft.server.ChatBaseComponent.ChatSerializer.jsonToComponent(headerJson);
} else {
playerListHeader = null;
}
if (footer != null) {
String headerJson = net.md_5.bungee.chat.ComponentSerializer.toString(footer);
playerListFooter = net.minecraft.server.ChatBaseComponent.ChatSerializer.jsonToComponent(headerJson);
} else {
playerListFooter = null;
}
updatePlayerListHeaderFooter();
}
@Override
@@ -300,14 +354,55 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
if (name == null) {
name = getName();
}
getHandle().listName = name.equals(getName()) ? null : CraftChatMessage.fromString(name)[0];
for (EntityPlayer player : server.getHandle().players) {
getHandle().listName = name.equals(getName()) ? null : CraftChatMessage.fromStringOrNull(name);
for (EntityPlayer player : (List<EntityPlayer>)server.getHandle().players) {
if (player.getBukkitEntity().canSee(this)) {
player.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_DISPLAY_NAME, getHandle()));
}
}
}
private IChatBaseComponent playerListHeader;
private IChatBaseComponent playerListFooter;
@Override
public String getPlayerListHeader() {
return (playerListHeader == null) ? null : CraftChatMessage.fromComponent(playerListHeader);
}
@Override
public String getPlayerListFooter() {
return (playerListFooter == null) ? null : CraftChatMessage.fromComponent(playerListFooter);
}
@Override
public void setPlayerListHeader(String header) {
this.playerListHeader = CraftChatMessage.fromStringOrNull(header, true); // Paper - fix up spigot tab API
updatePlayerListHeaderFooter();
}
@Override
public void setPlayerListFooter(String footer) {
this.playerListFooter = CraftChatMessage.fromStringOrNull(footer, true); // Paper - fix up spigot tab API
updatePlayerListHeaderFooter();
}
@Override
public void setPlayerListHeaderFooter(String header, String footer) {
this.playerListHeader = CraftChatMessage.fromStringOrNull(header, true); // Paper - fix up spigot tab API
this.playerListFooter = CraftChatMessage.fromStringOrNull(footer, true); // Paper - fix up spigot tab API
updatePlayerListHeaderFooter();
}
private void updatePlayerListHeaderFooter() {
if (getHandle().playerConnection == null) return;
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
packet.header = (this.playerListHeader == null) ? new ChatComponentText("") : this.playerListHeader;
packet.footer = (this.playerListFooter == null) ? new ChatComponentText("") : this.playerListFooter;
getHandle().playerConnection.sendPacket(packet);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof OfflinePlayer)) {
@@ -400,7 +495,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
float f = (float) Math.pow(2.0D, (note - 12.0D) / 12.0D);
getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect("block.note." + instrumentName), net.minecraft.server.SoundCategory.RECORDS, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, f));
getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect("block.note_block." + instrumentName), net.minecraft.server.SoundCategory.RECORDS, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, f));
}
@Override
@@ -441,7 +536,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
break;
}
float f = (float) Math.pow(2.0D, (note.getId() - 12.0D) / 12.0D);
getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect("block.note." + instrumentName), net.minecraft.server.SoundCategory.RECORDS, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, f));
getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect("block.note_block." + instrumentName), net.minecraft.server.SoundCategory.RECORDS, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, f));
}
@Override
@@ -466,7 +561,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public void playSound(Location loc, String sound, org.bukkit.SoundCategory category, float volume, float pitch) {
if (loc == null || sound == null || category == null || getHandle().playerConnection == null) return;
PacketPlayOutCustomSoundEffect packet = new PacketPlayOutCustomSoundEffect(sound, net.minecraft.server.SoundCategory.valueOf(category.name()), loc.getX(), loc.getY(), loc.getZ(), volume, pitch);
PacketPlayOutCustomSoundEffect packet = new PacketPlayOutCustomSoundEffect(new MinecraftKey(sound), net.minecraft.server.SoundCategory.valueOf(category.name()), new Vec3D(loc.getX(), loc.getY(), loc.getZ()), volume, pitch);
getHandle().playerConnection.sendPacket(packet);
}
@@ -488,18 +583,17 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void stopSound(String sound, org.bukkit.SoundCategory category) {
if (getHandle().playerConnection == null) return;
PacketDataSerializer packetdataserializer = new PacketDataSerializer(Unpooled.buffer());
packetdataserializer.a(category == null ? "" : net.minecraft.server.SoundCategory.valueOf(category.name()).a());
packetdataserializer.a(sound);
getHandle().playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|StopSound", packetdataserializer));
getHandle().playerConnection.sendPacket(new PacketPlayOutStopSound(new MinecraftKey(sound), category == null ? net.minecraft.server.SoundCategory.MASTER : net.minecraft.server.SoundCategory.valueOf(category.name())));
}
@Override
public void playEffect(Location loc, Effect effect, int data) {
if (getHandle().playerConnection == null) return;
spigot().playEffect(loc, effect, data, 0, 0, 0, 0, 1, 1, 64); // Spigot
int packetData = effect.getId();
PacketPlayOutWorldEvent packet = new PacketPlayOutWorldEvent(packetData, new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()), data, false);
getHandle().playerConnection.sendPacket(packet);
}
@Override
@@ -516,16 +610,21 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void sendBlockChange(Location loc, Material material, byte data) {
sendBlockChange(loc, material.getId(), data);
}
@Override
public void sendBlockChange(Location loc, int material, byte data) {
if (getHandle().playerConnection == null) return;
PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(((CraftWorld) loc.getWorld()).getHandle(), new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
packet.block = CraftMagicNumbers.getBlock(material).fromLegacyData(data);
packet.block = CraftMagicNumbers.getBlock(material, data);
getHandle().playerConnection.sendPacket(packet);
}
@Override
public void sendBlockChange(Location loc, BlockData block) {
if (getHandle().playerConnection == null) return;
PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(((CraftWorld) loc.getWorld()).getHandle(), new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
packet.block = ((CraftBlockData) block).getState();
getHandle().playerConnection.sendPacket(packet);
}
@@ -594,7 +693,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
Collection<MapIcon> icons = new ArrayList<MapIcon>();
for (MapCursor cursor : data.cursors) {
if (cursor.isVisible()) {
icons.add(new MapIcon(MapIcon.Type.a(cursor.getRawType()), cursor.getX(), cursor.getY(), cursor.getDirection()));
icons.add(new MapIcon(MapIcon.Type.a(cursor.getRawType()), cursor.getX(), cursor.getY(), cursor.getDirection(), CraftChatMessage.fromStringOrNull(cursor.getCaption())));
}
}
@@ -604,6 +703,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
Preconditions.checkArgument(location != null, "location");
Preconditions.checkArgument(location.getWorld() != null, "location.world");
location.checkFinite();
EntityPlayer entity = getHandle();
if (getHealth() == 0 || entity.dead) {
@@ -908,9 +1011,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void setWhitelisted(boolean value) {
if (value) {
server.getHandle().addWhitelist(getProfile());
server.getHandle().getWhitelist().add(new WhiteListEntry(getProfile()));
} else {
server.getHandle().removeWhitelist(getProfile());
server.getHandle().getWhitelist().remove(getProfile());
}
}
@@ -935,10 +1038,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public int applyMending(int amount) {
EntityPlayer handle = getHandle();
// Logic copied from EntityExperienceOrb and remapped to unobfuscated methods/properties
ItemStack itemstack = EnchantmentManager.getRandomEquippedItemWithEnchant(Enchantments.MENDING, handle);
if (!itemstack.isEmpty() && itemstack.hasDamage()) {
net.minecraft.server.ItemStack itemstack = net.minecraft.server.EnchantmentManager.getRandomEquippedItemWithEnchant(net.minecraft.server.Enchantments.MENDING, handle);
if (!itemstack.isEmpty() && itemstack.getItem().usesDurability()) {
EntityExperienceOrb orb = new EntityExperienceOrb(handle.world);
net.minecraft.server.EntityExperienceOrb orb = new net.minecraft.server.EntityExperienceOrb(handle.world);
orb.value = amount;
orb.spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.CUSTOM;
orb.locX = handle.locX;
@@ -1107,14 +1210,12 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
EntityTracker tracker = ((WorldServer) entity.world).tracker;
// Paper end
tracker.entriesLock.updateLock().lock(); // Akarin
synchronized (((IMixinWorldServer) entity.world).trackLock()) { // Akarin
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
if (entry != null) {
tracker.entriesLock.writeLock().lock(); // Akarin
entry.clear(getHandle());
tracker.entriesLock.writeLock().unlock(); // Akarin
}
tracker.entriesLock.updateLock().unlock(); // Akarin
} // Akarin
// Remove the hidden player from this player user list, if they're on it
if (other.sentListPacket) {
@@ -1161,14 +1262,12 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
tracker.entriesLock.updateLock().lock(); // Akarin
synchronized (((IMixinWorldServer) entity.world).trackLock()) { // Akarin
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
tracker.entriesLock.writeLock().lock(); // Akarin
entry.updatePlayer(getHandle());
tracker.entriesLock.writeLock().unlock(); // Akarin
}
tracker.entriesLock.updateLock().unlock(); // Akarin
} // Akarin
}
// Paper start
private void reregisterPlayer(EntityPlayer player) {
@@ -1177,7 +1276,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
registerPlayer(player);
}
}
@Override
public void setPlayerProfile(PlayerProfile profile) {
EntityPlayer self = getHandle();
self.setProfile(CraftPlayerProfile.asAuthlibCopy(profile));
@@ -1187,7 +1285,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
refreshPlayer();
}
@Override
public PlayerProfile getPlayerProfile() {
return new CraftPlayerProfile(this).clone();
}
@@ -1201,10 +1298,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
reregisterPlayer(handle);
//Respawn the player then update their position and selected slot
connection.sendPacket(new PacketPlayOutRespawn(handle.dimension, handle.world.getDifficulty(), handle.world.getWorldData().getType(), handle.playerInteractManager.getGameMode()));
connection.sendPacket(new net.minecraft.server.PacketPlayOutRespawn(handle.dimension, handle.world.getDifficulty(), handle.world.getWorldData().getType(), handle.playerInteractManager.getGameMode()));
handle.updateAbilities();
connection.sendPacket(new PacketPlayOutPosition(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch(), new HashSet<>(), 0));
MinecraftServer.getServer().getPlayerList().updateClient(handle);
connection.sendPacket(new net.minecraft.server.PacketPlayOutPosition(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch(), new HashSet<>(), 0));
net.minecraft.server.MinecraftServer.getServer().getPlayerList().updateClient(handle);
if (this.isOp()) {
this.setOp(false);
@@ -1346,7 +1443,8 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
if (getHandle().playerConnection == null) return;
if (channels.contains(channel)) {
PacketPlayOutCustomPayload packet = new PacketPlayOutCustomPayload(channel, new PacketDataSerializer(Unpooled.wrappedBuffer(message)));
channel = StandardMessenger.validateAndCorrectChannel(channel);
PacketPlayOutCustomPayload packet = new PacketPlayOutCustomPayload(new MinecraftKey(channel), new PacketDataSerializer(Unpooled.wrappedBuffer(message)));
getHandle().playerConnection.sendPacket(packet);
}
}
@@ -1374,12 +1472,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public void addChannel(String channel) {
com.google.common.base.Preconditions.checkState( DISABLE_CHANNEL_LIMIT || channels.size() < 128, "Too many channels registered" ); // Spigot // Paper - flag to disable channel limit
channel = StandardMessenger.validateAndCorrectChannel(channel);
if (channels.add(channel)) {
server.getPluginManager().callEvent(new PlayerRegisterChannelEvent(this, channel));
}
}
public void removeChannel(String channel) {
channel = StandardMessenger.validateAndCorrectChannel(channel);
if (channels.remove(channel)) {
server.getPluginManager().callEvent(new PlayerUnregisterChannelEvent(this, channel));
}
@@ -1406,7 +1506,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
}
getHandle().playerConnection.sendPacket(new PacketPlayOutCustomPayload("REGISTER", new PacketDataSerializer(Unpooled.wrappedBuffer(stream.toByteArray()))));
getHandle().playerConnection.sendPacket(new PacketPlayOutCustomPayload(new MinecraftKey("register"), new PacketDataSerializer(Unpooled.wrappedBuffer(stream.toByteArray()))));
}
}
@@ -1443,7 +1543,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
// Paper start
if (prop == Property.REPAIR_COST && container instanceof net.minecraft.server.ContainerAnvil) {
((ContainerAnvil) container).levelCost = value;
((net.minecraft.server.ContainerAnvil) container).levelCost = value;
}
// Paper end
getHandle().setContainerData(container, prop.getId(), value);
@@ -1514,7 +1614,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public float getFlySpeed() {
return getHandle().abilities.flySpeed * 2f;
return (float) getHandle().abilities.flySpeed * 2f;
}
@Override
@@ -1616,13 +1716,21 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(getHandle().getId(), set));
sendHealthUpdate();
}
getHandle().getDataWatcher().set(EntityLiving.HEALTH, getScaledHealth());
getHandle().getDataWatcher().set(EntityLiving.HEALTH, (float) getScaledHealth());
getHandle().maxHealthCache = getMaxHealth();
}
public void sendHealthUpdate() {
getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()));
// Paper start - cancellable death event
//getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()));
PacketPlayOutUpdateHealth packet = new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel());
if (this.getHandle().queueHealthUpdatePacket) {
this.getHandle().queuedHealthUpdatePacket = packet;
} else {
this.getHandle().playerConnection.sendPacket(packet);
}
// Paper end
}
public void injectScaledMaxHealth(Collection<AttributeInstance> collection, boolean force) {
@@ -1669,19 +1777,19 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
getHandle().playerConnection.sendPacket(times);
if (title != null) {
PacketPlayOutTitle packetTitle = new PacketPlayOutTitle(EnumTitleAction.TITLE, CraftChatMessage.fromString(title)[0]);
PacketPlayOutTitle packetTitle = new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TITLE, CraftChatMessage.fromStringOrNull(title));
getHandle().playerConnection.sendPacket(packetTitle);
}
if (subtitle != null) {
PacketPlayOutTitle packetSubtitle = new PacketPlayOutTitle(EnumTitleAction.SUBTITLE, CraftChatMessage.fromString(subtitle)[0]);
PacketPlayOutTitle packetSubtitle = new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.SUBTITLE, CraftChatMessage.fromStringOrNull(subtitle));
getHandle().playerConnection.sendPacket(packetSubtitle);
}
}
@Override
public void resetTitle() {
PacketPlayOutTitle packetReset = new PacketPlayOutTitle(EnumTitleAction.RESET, null);
PacketPlayOutTitle packetReset = new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.RESET, null);
getHandle().playerConnection.sendPacket(packetReset);
}
@@ -1745,7 +1853,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
if (data != null && !particle.getDataType().isInstance(data)) {
throw new IllegalArgumentException("data should be " + particle.getDataType() + " got " + data.getClass());
}
PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(CraftParticle.toNMS(particle), true, (float) x, (float) y, (float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count, CraftParticle.toData(particle, data));
PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(CraftParticle.toNMS(particle, data), true, (float) x, (float) y, (float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count);
getHandle().playerConnection.sendPacket(packetplayoutworldparticles);
}
@@ -1769,7 +1877,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
// Paper end
}
@Override
// Paper start
public void setAffectsSpawning(boolean affects) {
this.getHandle().affectsSpawning = affects;
}
@@ -1788,6 +1896,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public void setViewDistance(int viewDistance) {
((WorldServer) getHandle().world).getPlayerChunkMap().updateViewDistance(getHandle(), viewDistance);
}
// Paper end
@Override
public void updateCommands() {
if (getHandle().playerConnection == null) return;
getHandle().server.getCommandDispatcher().a(getHandle());
}
@Override
public void setResourcePack(String url, String hash) {
@@ -1815,6 +1931,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
this.resourcePackStatus = status;
}
//Paper start
public float getCooldownPeriod() {
return getHandle().getCooldownPeriod();
}
public float getCooledAttackStrength(float adjustTicks) {
return getHandle().getCooledAttackStrength(adjustTicks);
}
public void resetCooldown() {
getHandle().resetCooldown();
}
//Paper end
// Spigot start
private final Player.Spigot spigot = new Player.Spigot()
{
@@ -1840,64 +1970,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
{
if ( getHealth() <= 0 && isOnline() )
{
server.getServer().getPlayerList().moveToWorld( getHandle(), 0, false );
}
}
@Override
public void playEffect( Location location, Effect effect, int id, int data, float offsetX, float offsetY, float offsetZ, float speed, int particleCount, int radius )
{
Validate.notNull( location, "Location cannot be null" );
Validate.notNull( effect, "Effect cannot be null" );
Validate.notNull( location.getWorld(), "World cannot be null" );
Packet packet;
if ( effect.getType() != Effect.Type.PARTICLE )
{
int packetData = effect.getId();
packet = new PacketPlayOutWorldEvent( packetData, new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ() ), id, false );
} else
{
net.minecraft.server.EnumParticle particle = null;
int[] extra = null;
for ( net.minecraft.server.EnumParticle p : net.minecraft.server.EnumParticle.values() )
{
if ( effect.getName().startsWith( p.b().replace("_", "") ) )
{
particle = p;
if ( effect.getData() != null )
{
if ( effect.getData().equals( org.bukkit.Material.class ) )
{
extra = new int[]{ id };
} else
{
extra = new int[]{ (data << 12) | (id & 0xFFF) };
}
}
break;
}
}
if ( extra == null )
{
extra = new int[0];
}
packet = new PacketPlayOutWorldParticles( particle, true, (float) location.getX(), (float) location.getY(), (float) location.getZ(), offsetX, offsetY, offsetZ, speed, particleCount, extra );
}
int distance;
radius *= radius;
if ( getHandle().playerConnection == null )
{
return;
}
if ( !location.getWorld().equals( getWorld() ) )
{
return;
}
distance = (int) getLocation().distanceSquared( location );
if ( distance <= radius )
{
getHandle().playerConnection.sendPacket( packet );
server.getServer().getPlayerList().moveToWorld( getHandle(), net.minecraft.server.DimensionManager.OVERWORLD, false );
}
}
@@ -1928,7 +2001,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public void sendMessage(BaseComponent... components) {
if ( getHandle().playerConnection == null ) return;
PacketPlayOutChat packet = new PacketPlayOutChat(null, ChatMessageType.CHAT);
PacketPlayOutChat packet = new PacketPlayOutChat(null, net.minecraft.server.ChatMessageType.CHAT);
packet.components = components;
getHandle().playerConnection.sendPacket(packet);
}
@@ -1942,7 +2015,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent... components) {
if ( getHandle().playerConnection == null ) return;
PacketPlayOutChat packet = new PacketPlayOutChat(null, ChatMessageType.a((byte) position.ordinal()));
PacketPlayOutChat packet = new PacketPlayOutChat(null, net.minecraft.server.ChatMessageType.a((byte) position.ordinal()));
// Action bar doesn't render colours, replace colours with legacy section symbols
if (position == net.md_5.bungee.api.ChatMessageType.ACTION_BAR) {
components = new BaseComponent[]{new net.md_5.bungee.api.chat.TextComponent(BaseComponent.toLegacyText(components))};
@@ -1951,14 +2024,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
getHandle().playerConnection.sendPacket(packet);
}
// Paper start
@Override
public int getPing()
{
return getHandle().ping;
}
// Paper end
};
@Override
public Player.Spigot spigot()
{
return spigot;

View File

@@ -0,0 +1,791 @@
package org.bukkit.plugin;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.destroystokyo.paper.event.server.ServerExceptionEvent;
import com.destroystokyo.paper.exception.ServerEventException;
import com.destroystokyo.paper.exception.ServerPluginEnableDisableException;
import org.apache.commons.lang.Validate;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.PluginCommandYamlParser;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.event.Event;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.util.FileUtil;
import com.google.common.collect.ImmutableSet;
import io.akarin.api.internal.Akari;
/**
* Handles all plugin management from the Server
*/
public final class SimplePluginManager implements PluginManager {
private final Server server;
private final Map<Pattern, PluginLoader> fileAssociations = new HashMap<Pattern, PluginLoader>();
private final List<Plugin> plugins = new ArrayList<Plugin>();
private final Map<String, Plugin> lookupNames = new HashMap<String, Plugin>();
private File updateDirectory;
private final SimpleCommandMap commandMap;
private final Map<String, Permission> permissions = new HashMap<String, Permission>();
private final Map<Boolean, Set<Permission>> defaultPerms = new LinkedHashMap<Boolean, Set<Permission>>();
private final Map<String, Map<Permissible, Boolean>> permSubs = new HashMap<String, Map<Permissible, Boolean>>();
private final Map<Boolean, Map<Permissible, Boolean>> defSubs = new HashMap<Boolean, Map<Permissible, Boolean>>();
private boolean useTimings = false;
public SimplePluginManager(Server instance, SimpleCommandMap commandMap) {
server = instance;
this.commandMap = commandMap;
defaultPerms.put(true, new HashSet<Permission>());
defaultPerms.put(false, new HashSet<Permission>());
}
/**
* Registers the specified plugin loader
*
* @param loader Class name of the PluginLoader to register
* @throws IllegalArgumentException Thrown when the given Class is not a
* valid PluginLoader
*/
public void registerInterface(Class<? extends PluginLoader> loader) throws IllegalArgumentException {
PluginLoader instance;
if (PluginLoader.class.isAssignableFrom(loader)) {
Constructor<? extends PluginLoader> constructor;
try {
constructor = loader.getConstructor(Server.class);
instance = constructor.newInstance(server);
} catch (NoSuchMethodException ex) {
String className = loader.getName();
throw new IllegalArgumentException(String.format("Class %s does not have a public %s(Server) constructor", className, className), ex);
} catch (Exception ex) {
throw new IllegalArgumentException(String.format("Unexpected exception %s while attempting to construct a new instance of %s", ex.getClass().getName(), loader.getName()), ex);
}
} else {
throw new IllegalArgumentException(String.format("Class %s does not implement interface PluginLoader", loader.getName()));
}
Pattern[] patterns = instance.getPluginFileFilters();
synchronized (this) {
for (Pattern pattern : patterns) {
fileAssociations.put(pattern, instance);
}
}
}
/**
* Loads the plugins contained within the specified directory
*
* @param directory Directory to check for plugins
* @return A list of all plugins loaded
*/
public Plugin[] loadPlugins(File directory) {
Validate.notNull(directory, "Directory cannot be null");
Validate.isTrue(directory.isDirectory(), "Directory must be a directory");
List<Plugin> result = new ArrayList<Plugin>();
Set<Pattern> filters = fileAssociations.keySet();
if (!(server.getUpdateFolder().equals(""))) {
updateDirectory = new File(directory, server.getUpdateFolder());
}
Map<String, File> plugins = new HashMap<String, File>();
Set<String> loadedPlugins = new HashSet<String>();
Map<String, Collection<String>> dependencies = new HashMap<String, Collection<String>>();
Map<String, Collection<String>> softDependencies = new HashMap<String, Collection<String>>();
// This is where it figures out all possible plugins
for (File file : directory.listFiles()) {
PluginLoader loader = null;
for (Pattern filter : filters) {
Matcher match = filter.matcher(file.getName());
if (match.find()) {
loader = fileAssociations.get(filter);
}
}
if (loader == null) continue;
PluginDescriptionFile description = null;
try {
description = loader.getPluginDescription(file);
String name = description.getName();
if (name.equalsIgnoreCase("bukkit") || name.equalsIgnoreCase("minecraft") || name.equalsIgnoreCase("mojang")) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': Restricted Name");
continue;
} else if (description.rawName.indexOf(' ') != -1) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': uses the space-character (0x20) in its name");
continue;
}
} catch (InvalidDescriptionException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
continue;
}
File replacedFile = plugins.put(description.getName(), file);
if (replacedFile != null) {
server.getLogger().severe(String.format(
"Ambiguous plugin name `%s' for files `%s' and `%s' in `%s'",
description.getName(),
file.getPath(),
replacedFile.getPath(),
directory.getPath()
));
}
Collection<String> softDependencySet = description.getSoftDepend();
if (softDependencySet != null && !softDependencySet.isEmpty()) {
if (softDependencies.containsKey(description.getName())) {
// Duplicates do not matter, they will be removed together if applicable
softDependencies.get(description.getName()).addAll(softDependencySet);
} else {
softDependencies.put(description.getName(), new LinkedList<String>(softDependencySet));
}
}
Collection<String> dependencySet = description.getDepend();
if (dependencySet != null && !dependencySet.isEmpty()) {
dependencies.put(description.getName(), new LinkedList<String>(dependencySet));
}
Collection<String> loadBeforeSet = description.getLoadBefore();
if (loadBeforeSet != null && !loadBeforeSet.isEmpty()) {
for (String loadBeforeTarget : loadBeforeSet) {
if (softDependencies.containsKey(loadBeforeTarget)) {
softDependencies.get(loadBeforeTarget).add(description.getName());
} else {
// softDependencies is never iterated, so 'ghost' plugins aren't an issue
Collection<String> shortSoftDependency = new LinkedList<String>();
shortSoftDependency.add(description.getName());
softDependencies.put(loadBeforeTarget, shortSoftDependency);
}
}
}
}
while (!plugins.isEmpty()) {
boolean missingDependency = true;
Iterator<Map.Entry<String, File>> pluginIterator = plugins.entrySet().iterator();
while (pluginIterator.hasNext()) {
Map.Entry<String, File> entry = pluginIterator.next();
String plugin = entry.getKey();
if (dependencies.containsKey(plugin)) {
Iterator<String> dependencyIterator = dependencies.get(plugin).iterator();
while (dependencyIterator.hasNext()) {
String dependency = dependencyIterator.next();
// Dependency loaded
if (loadedPlugins.contains(dependency)) {
dependencyIterator.remove();
// We have a dependency not found
} else if (!plugins.containsKey(dependency)) {
missingDependency = false;
pluginIterator.remove();
softDependencies.remove(plugin);
dependencies.remove(plugin);
server.getLogger().log(
Level.SEVERE,
"Could not load '" + entry.getValue().getPath() + "' in folder '" + directory.getPath() + "'",
new UnknownDependencyException(dependency));
break;
}
}
if (dependencies.containsKey(plugin) && dependencies.get(plugin).isEmpty()) {
dependencies.remove(plugin);
}
}
if (softDependencies.containsKey(plugin)) {
Iterator<String> softDependencyIterator = softDependencies.get(plugin).iterator();
while (softDependencyIterator.hasNext()) {
String softDependency = softDependencyIterator.next();
// Soft depend is no longer around
if (!plugins.containsKey(softDependency)) {
softDependencyIterator.remove();
}
}
if (softDependencies.get(plugin).isEmpty()) {
softDependencies.remove(plugin);
}
}
if (!(dependencies.containsKey(plugin) || softDependencies.containsKey(plugin)) && plugins.containsKey(plugin)) {
// We're clear to load, no more soft or hard dependencies left
File file = plugins.get(plugin);
pluginIterator.remove();
missingDependency = false;
try {
result.add(loadPlugin(file));
loadedPlugins.add(plugin);
continue;
} catch (InvalidPluginException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
}
}
}
if (missingDependency) {
// We now iterate over plugins until something loads
// This loop will ignore soft dependencies
pluginIterator = plugins.entrySet().iterator();
while (pluginIterator.hasNext()) {
Map.Entry<String, File> entry = pluginIterator.next();
String plugin = entry.getKey();
if (!dependencies.containsKey(plugin)) {
softDependencies.remove(plugin);
missingDependency = false;
File file = entry.getValue();
pluginIterator.remove();
try {
result.add(loadPlugin(file));
loadedPlugins.add(plugin);
break;
} catch (InvalidPluginException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
}
}
}
// We have no plugins left without a depend
if (missingDependency) {
softDependencies.clear();
dependencies.clear();
Iterator<File> failedPluginIterator = plugins.values().iterator();
while (failedPluginIterator.hasNext()) {
File file = failedPluginIterator.next();
failedPluginIterator.remove();
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': circular dependency detected");
}
}
}
}
return result.toArray(new Plugin[result.size()]);
}
/**
* Loads the plugin in the specified file
* <p>
* File must be valid according to the current enabled Plugin interfaces
*
* @param file File containing the plugin to load
* @return The Plugin loaded, or null if it was invalid
* @throws InvalidPluginException Thrown when the specified file is not a
* valid plugin
* @throws UnknownDependencyException If a required dependency could not
* be found
*/
public synchronized Plugin loadPlugin(File file) throws InvalidPluginException, UnknownDependencyException {
Validate.notNull(file, "File cannot be null");
checkUpdate(file);
Set<Pattern> filters = fileAssociations.keySet();
Plugin result = null;
for (Pattern filter : filters) {
String name = file.getName();
Matcher match = filter.matcher(name);
if (match.find()) {
PluginLoader loader = fileAssociations.get(filter);
result = loader.loadPlugin(file);
}
}
if (result != null) {
plugins.add(result);
lookupNames.put(result.getDescription().getName().toLowerCase(java.util.Locale.ENGLISH), result); // Spigot
}
return result;
}
private void checkUpdate(File file) {
if (updateDirectory == null || !updateDirectory.isDirectory()) {
return;
}
File updateFile = new File(updateDirectory, file.getName());
if (updateFile.isFile() && FileUtil.copy(updateFile, file)) {
updateFile.delete();
}
}
/**
* Checks if the given plugin is loaded and returns it when applicable
* <p>
* Please note that the name of the plugin is case-sensitive
*
* @param name Name of the plugin to check
* @return Plugin if it exists, otherwise null
*/
public synchronized Plugin getPlugin(String name) {
return lookupNames.get(name.replace(' ', '_').toLowerCase(java.util.Locale.ENGLISH)); // Spigot
}
public synchronized Plugin[] getPlugins() {
return plugins.toArray(new Plugin[plugins.size()]);
}
/**
* Checks if the given plugin is enabled or not
* <p>
* Please note that the name of the plugin is case-sensitive.
*
* @param name Name of the plugin to check
* @return true if the plugin is enabled, otherwise false
*/
public boolean isPluginEnabled(String name) {
Plugin plugin = getPlugin(name);
return isPluginEnabled(plugin);
}
/**
* Checks if the given plugin is enabled or not
*
* @param plugin Plugin to check
* @return true if the plugin is enabled, otherwise false
*/
public synchronized boolean isPluginEnabled(Plugin plugin) { // Paper - synchronize
if ((plugin != null) && (plugins.contains(plugin))) {
return plugin.isEnabled();
} else {
return false;
}
}
public synchronized void enablePlugin(final Plugin plugin) { // Paper - synchronize
if (!plugin.isEnabled()) {
List<Command> pluginCommands = PluginCommandYamlParser.parse(plugin);
if (!pluginCommands.isEmpty()) {
commandMap.registerAll(plugin.getDescription().getName(), pluginCommands);
}
try {
plugin.getPluginLoader().enablePlugin(plugin);
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while enabling "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin);
}
HandlerList.bakeAll();
}
}
// Paper start - close Classloader on disable
public void disablePlugins() {
disablePlugins(false);
}
public void disablePlugins(boolean closeClassloaders) {
// Paper end - close Classloader on disable
Plugin[] plugins = getPlugins();
for (int i = plugins.length - 1; i >= 0; i--) {
disablePlugin(plugins[i], closeClassloaders); // Paper - close Classloader on disable
}
}
// Paper start - close Classloader on disable
public void disablePlugin(Plugin plugin) {
disablePlugin(plugin, false);
}
public synchronized void disablePlugin(final Plugin plugin, boolean closeClassloader) { // Paper - synchronize
// Paper end - close Classloader on disable
if (plugin.isEnabled()) {
try {
plugin.getPluginLoader().disablePlugin(plugin, closeClassloader); // Paper - close Classloader on disable
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while disabling "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
server.getScheduler().cancelTasks(plugin);
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while cancelling tasks for "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
server.getServicesManager().unregisterAll(plugin);
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while unregistering services for "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
HandlerList.unregisterAll(plugin);
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while unregistering events for "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
server.getMessenger().unregisterIncomingPluginChannel(plugin);
server.getMessenger().unregisterOutgoingPluginChannel(plugin);
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while unregistering plugin channels for "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
}
}
// Paper start
private void handlePluginException(String msg, Throwable ex, Plugin plugin) {
server.getLogger().log(Level.SEVERE, msg, ex);
callEvent(new ServerExceptionEvent(new ServerPluginEnableDisableException(msg, ex, plugin)));
}
// Paper end
public void clearPlugins() {
synchronized (this) {
disablePlugins(true); // Paper - close Classloader on disable
plugins.clear();
lookupNames.clear();
HandlerList.unregisterAll();
fileAssociations.clear();
permissions.clear();
defaultPerms.get(true).clear();
defaultPerms.get(false).clear();
}
}
/**
* Calls an event with the given details.
* <p>
* This method only synchronizes when the event is not asynchronous.
*
* @param event Event details
*/
public void callEvent(Event event) {
if (event.isAsynchronous()) {
if (Thread.holdsLock(this)) {
throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from inside synchronized code.");
}
if (server.isPrimaryThread()) {
throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from primary server thread.");
}
fireEvent(event);
} else {
Akari.eventLock.lock(); // Akarin
synchronized (this) {
fireEvent(event);
}
Akari.eventLock.unlock(); // Akarin
}
}
private void fireEvent(Event event) {
HandlerList handlers = event.getHandlers();
RegisteredListener[] listeners = handlers.getRegisteredListeners();
for (RegisteredListener registration : listeners) {
if (!registration.getPlugin().isEnabled()) {
continue;
}
try {
registration.callEvent(event);
} catch (AuthorNagException ex) {
Plugin plugin = registration.getPlugin();
if (plugin.isNaggable()) {
plugin.setNaggable(false);
server.getLogger().log(Level.SEVERE, String.format(
"Nag author(s): '%s' of '%s' about the following: %s",
plugin.getDescription().getAuthors(),
plugin.getDescription().getFullName(),
ex.getMessage()
));
}
} catch (Throwable ex) {
// Paper start - error reporting
String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName();
server.getLogger().log(Level.SEVERE, msg, ex);
if (!(event instanceof ServerExceptionEvent)) { // We don't want to cause an endless event loop
callEvent(new ServerExceptionEvent(new ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event)));
}
// Paper end
}
}
}
public void registerEvents(Listener listener, Plugin plugin) {
if (!plugin.isEnabled()) {
throw new IllegalPluginAccessException("Plugin attempted to register " + listener + " while not enabled");
}
for (Map.Entry<Class<? extends Event>, Set<RegisteredListener>> entry : plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet()) {
getEventListeners(getRegistrationClass(entry.getKey())).registerAll(entry.getValue());
}
}
public void registerEvent(Class<? extends Event> event, Listener listener, EventPriority priority, EventExecutor executor, Plugin plugin) {
registerEvent(event, listener, priority, executor, plugin, false);
}
/**
* Registers the given event to the specified listener using a directly
* passed EventExecutor
*
* @param event Event class to register
* @param listener PlayerListener to register
* @param priority Priority of this event
* @param executor EventExecutor to register
* @param plugin Plugin to register
* @param ignoreCancelled Do not call executor if event was already
* cancelled
*/
public void registerEvent(Class<? extends Event> event, Listener listener, EventPriority priority, EventExecutor executor, Plugin plugin, boolean ignoreCancelled) {
Validate.notNull(listener, "Listener cannot be null");
Validate.notNull(priority, "Priority cannot be null");
Validate.notNull(executor, "Executor cannot be null");
Validate.notNull(plugin, "Plugin cannot be null");
if (!plugin.isEnabled()) {
throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled");
}
executor = new co.aikar.timings.TimedEventExecutor(executor, plugin, null, event); // Spigot
if (false) { // Spigot - RL handles useTimings check now
getEventListeners(event).register(new TimedRegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
} else {
getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
}
}
private HandlerList getEventListeners(Class<? extends Event> type) {
try {
Method method = getRegistrationClass(type).getDeclaredMethod("getHandlerList");
method.setAccessible(true);
return (HandlerList) method.invoke(null);
} catch (Exception e) {
throw new IllegalPluginAccessException(e.toString());
}
}
private Class<? extends Event> getRegistrationClass(Class<? extends Event> clazz) {
try {
clazz.getDeclaredMethod("getHandlerList");
return clazz;
} catch (NoSuchMethodException e) {
if (clazz.getSuperclass() != null
&& !clazz.getSuperclass().equals(Event.class)
&& Event.class.isAssignableFrom(clazz.getSuperclass())) {
return getRegistrationClass(clazz.getSuperclass().asSubclass(Event.class));
} else {
throw new IllegalPluginAccessException("Unable to find handler list for event " + clazz.getName() + ". Static getHandlerList method required!");
}
}
}
public Permission getPermission(String name) {
return permissions.get(name.toLowerCase(java.util.Locale.ENGLISH));
}
public void addPermission(Permission perm) {
addPermission(perm, true);
}
@Deprecated
public void addPermission(Permission perm, boolean dirty) {
String name = perm.getName().toLowerCase(java.util.Locale.ENGLISH);
if (permissions.containsKey(name)) {
throw new IllegalArgumentException("The permission " + name + " is already defined!");
}
permissions.put(name, perm);
calculatePermissionDefault(perm, dirty);
}
public Set<Permission> getDefaultPermissions(boolean op) {
return ImmutableSet.copyOf(defaultPerms.get(op));
}
public void removePermission(Permission perm) {
removePermission(perm.getName());
}
public void removePermission(String name) {
permissions.remove(name.toLowerCase(java.util.Locale.ENGLISH));
}
public void recalculatePermissionDefaults(Permission perm) {
if (perm != null && permissions.containsKey(perm.getName().toLowerCase(java.util.Locale.ENGLISH))) {
defaultPerms.get(true).remove(perm);
defaultPerms.get(false).remove(perm);
calculatePermissionDefault(perm, true);
}
}
private void calculatePermissionDefault(Permission perm, boolean dirty) {
if ((perm.getDefault() == PermissionDefault.OP) || (perm.getDefault() == PermissionDefault.TRUE)) {
defaultPerms.get(true).add(perm);
if (dirty) {
dirtyPermissibles(true);
}
}
if ((perm.getDefault() == PermissionDefault.NOT_OP) || (perm.getDefault() == PermissionDefault.TRUE)) {
defaultPerms.get(false).add(perm);
if (dirty) {
dirtyPermissibles(false);
}
}
}
@Deprecated
public void dirtyPermissibles() {
dirtyPermissibles(true);
dirtyPermissibles(false);
}
private void dirtyPermissibles(boolean op) {
Set<Permissible> permissibles = getDefaultPermSubscriptions(op);
for (Permissible p : permissibles) {
p.recalculatePermissions();
}
}
public void subscribeToPermission(String permission, Permissible permissible) {
String name = permission.toLowerCase(java.util.Locale.ENGLISH);
Map<Permissible, Boolean> map = permSubs.get(name);
if (map == null) {
map = new WeakHashMap<Permissible, Boolean>();
permSubs.put(name, map);
}
map.put(permissible, true);
}
public void unsubscribeFromPermission(String permission, Permissible permissible) {
String name = permission.toLowerCase(java.util.Locale.ENGLISH);
Map<Permissible, Boolean> map = permSubs.get(name);
if (map != null) {
map.remove(permissible);
if (map.isEmpty()) {
permSubs.remove(name);
}
}
}
public Set<Permissible> getPermissionSubscriptions(String permission) {
String name = permission.toLowerCase(java.util.Locale.ENGLISH);
Map<Permissible, Boolean> map = permSubs.get(name);
if (map == null) {
return ImmutableSet.of();
} else {
return ImmutableSet.copyOf(map.keySet());
}
}
public void subscribeToDefaultPerms(boolean op, Permissible permissible) {
Map<Permissible, Boolean> map = defSubs.get(op);
if (map == null) {
map = new WeakHashMap<Permissible, Boolean>();
defSubs.put(op, map);
}
map.put(permissible, true);
}
public void unsubscribeFromDefaultPerms(boolean op, Permissible permissible) {
Map<Permissible, Boolean> map = defSubs.get(op);
if (map != null) {
map.remove(permissible);
if (map.isEmpty()) {
defSubs.remove(op);
}
}
}
public Set<Permissible> getDefaultPermSubscriptions(boolean op) {
Map<Permissible, Boolean> map = defSubs.get(op);
if (map == null) {
return ImmutableSet.of();
} else {
return ImmutableSet.copyOf(map.keySet());
}
}
public Set<Permission> getPermissions() {
return new HashSet<Permission>(permissions.values());
}
public boolean useTimings() {
return co.aikar.timings.Timings.isTimingsEnabled(); // Spigot
}
/**
* Sets whether or not per event timing code should be used
*
* @param use True if per event timing code should be used
*/
public void useTimings(boolean use) {
co.aikar.timings.Timings.setTimingsEnabled(use); // Spigot
}
// Paper start
public void clearPermissions() {
permissions.clear();
defaultPerms.get(true).clear();
defaultPerms.get(false).clear();
}
// Paper end
}

View File

@@ -19,7 +19,7 @@ settings:
update-folder: update
plugin-profiling: false
connection-throttle: 4000
query-plugins: false
query-plugins: true
deprecated-verbose: default
shutdown-message: "Server closed"
spawn-limits:

View File

@@ -9,19 +9,14 @@
"bootstrap.Bootstrap",
"bootstrap.DummyEula",
"bootstrap.MixinMetrics",
"bootstrap.ParallelRegistry",
"bootstrap.MetricsBootstrap",
"bootstrap.MixinRestartCommand",
"core.MixinWorld",
"core.MixinMCUtil",
"core.MixinCommandBan",
"core.MixinCommandKick",
"core.MixinCraftServer",
"core.MixinWorldServer",
"core.MixinFileIOThread",
"core.MixinWorldManager",
"core.MixinCommandBanIp",
"core.MixinChunkSection",
"core.MixinAsyncCatcher",
"core.MixinTimingHandler",
@@ -34,11 +29,8 @@
"nsc.NonblockingServerConnection",
"optimization.MixinEntity",
"optimization.WeakBigTree",
"optimization.WeakEnchantmentManager",
"optimization.MixinEntityHorseAbstract",
"optimization.MixinEntityTameableAnimal",
"optimization.MixinPersistentCollection",
"optimization.MixinTileEntityEnchantTable"
]
}

View File

@@ -1,11 +0,0 @@
{
"required": true,
"minVersion": "0.7.10",
"package": "io.akarin.server.mixin",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8",
"server": [
"cps.MixinCraftWorld",
"cps.MixinChunkProviderServer",
]
}

View File

@@ -1,10 +0,0 @@
{
"required": true,
"minVersion": "0.7.10",
"package": "io.akarin.server.mixin",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8",
"server": [
"optimization.PandaRedstoneWire",
]
}

View File

@@ -1,24 +0,0 @@
{
"required": true,
"minVersion": "0.7.10",
"package": "io.akarin.server.mixin",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8",
"server": [
"realtime.MixinWorld",
"realtime.MixinEntity",
"realtime.MixinEntityItem",
"realtime.MixinWorldServer",
"realtime.MixinEntityHuman",
"realtime.MixinEntityPlayer",
"realtime.MixinEntityAgeable",
"realtime.MixinMinecraftServer",
"realtime.MixinEntityInsentient",
"realtime.MixinPlayerConnection",
"realtime.MixinTileEntityFurnace",
"realtime.MixinEntityExperienceOrb",
"realtime.MixinEntityZombieVillager",
"realtime.MixinPlayerInteractManager",
"realtime.MixinTileEntityBrewingStand",
]
}