Compare commits
54 Commits
Build-scri
...
ver/1.13
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b5de991da | ||
|
|
2ce7ad3504 | ||
|
|
458d25a6ff | ||
|
|
7c02d15374 | ||
|
|
5b84e0b4ae | ||
|
|
521961be73 | ||
|
|
6f58c7158d | ||
|
|
1356bf841f | ||
|
|
48a23b9e63 | ||
|
|
c063f92ff1 | ||
|
|
2c18d26438 | ||
|
|
3b949e82ba | ||
|
|
49c3060432 | ||
|
|
88415e09b5 | ||
|
|
0a7f790e78 | ||
|
|
ef0ce05cae | ||
|
|
2593ab8eba | ||
|
|
732cc57ee5 | ||
|
|
2f867dd364 | ||
|
|
75ab6a414c | ||
|
|
4eac44c8df | ||
|
|
0c3fec2c10 | ||
|
|
46e8595949 | ||
|
|
59b1567405 | ||
|
|
f8e01fa0f9 | ||
|
|
1c5da01dec | ||
|
|
916c298fbf | ||
|
|
72f3d31652 | ||
|
|
64f2e7dcd1 | ||
|
|
2695e3551f | ||
|
|
c16c1ef11c | ||
|
|
687b8369a6 | ||
|
|
779148ddc1 | ||
|
|
d4f5420183 | ||
|
|
b4fa26e0c1 | ||
|
|
90adbd71c7 | ||
|
|
9f7d36ff50 | ||
|
|
6dc0a094df | ||
|
|
5005a19195 | ||
|
|
a64ee6fa0d | ||
|
|
67817f6ba2 | ||
|
|
7eea320298 | ||
|
|
0edce075eb | ||
|
|
042c7b3301 | ||
|
|
a7b15975df | ||
|
|
5d8221f7aa | ||
|
|
598d8ed104 | ||
|
|
56066ac93e | ||
|
|
eac7e704f1 | ||
|
|
e45247fab4 | ||
|
|
996c3e4949 | ||
|
|
48b9fea5af | ||
|
|
7e6de94cec | ||
|
|
563d3973c1 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +1,4 @@
|
||||
[submodule "work/Paper"]
|
||||
path = work/Paper
|
||||
url = https://github.com/LegacyGamerHD/Paper.git
|
||||
url = https://github.com/Akarin-project/Paper.git
|
||||
branch = pre/1.13
|
||||
25
Jenkinsfile
vendored
25
Jenkinsfile
vendored
@@ -1,25 +0,0 @@
|
||||
pipeline {
|
||||
agent any
|
||||
stages {
|
||||
stage('Init Submodules') {
|
||||
steps {
|
||||
sh 'git submodule update --init --recursive'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'update-alternatives --set java /usr/lib/jvm/zulu8/jre/bin/java'
|
||||
sh 'chmod +x scripts/inst.sh'
|
||||
sh './scripts/inst.sh --setup'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Archive') {
|
||||
steps {
|
||||
archiveArtifacts(artifacts: '*.jar', fingerprint: true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
[](https://akarin.io)
|
||||
[](https://discord.gg/fw2pJAj)
|
||||
[](https://bstats.org/plugin/bukkit/Torch)
|
||||
[](https://circleci.com/gh/Akarin-project/Akarin/tree/ver/1.13)
|
||||
|
||||
Akarin is currently **under heavy development** and contributions are welcome!
|
||||
|
||||
@@ -23,7 +24,8 @@ Get Akarin
|
||||
---
|
||||
### Download
|
||||
#### Recommended
|
||||
+ [**Jenkins**](http://ci.josephworks.net/job/Akarin/job/ver%252F1.12.2/) - Kudos to [JosephWorks](https://github.com/josephworks)
|
||||
+ [**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*
|
||||
|
||||
|
||||
@@ -3,25 +3,20 @@ 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 {
|
||||
public class ChunkPacketInfo<T> {
|
||||
|
||||
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
|
||||
private final Chunk chunk;
|
||||
private final int chunkSectionSelector;
|
||||
private ByteBuf data; // Akarin
|
||||
private final int[] bitsPerValue = new int[16];
|
||||
private final DataPalette[] dataPalettes = new DataPalette[16];
|
||||
private final int[] bitsPerObject = new int[16];
|
||||
private final Object[] dataPalettes = new Object[16];
|
||||
private final int[] dataBitsIndexes = new int[16];
|
||||
private final IBlockData[][] predefinedBlockData = new IBlockData[16][];
|
||||
private final Object[][] predefinedObjects = new Object[16][];
|
||||
|
||||
public PacketPlayOutMapChunkInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
|
||||
public ChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
|
||||
this.packetPlayOutMapChunk = packetPlayOutMapChunk;
|
||||
this.chunk = chunk;
|
||||
this.chunkSectionSelector = chunkSectionSelector;
|
||||
@@ -47,19 +42,20 @@ public class PacketPlayOutMapChunkInfo {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getBitsPerValue(int chunkSectionIndex) {
|
||||
return bitsPerValue[chunkSectionIndex];
|
||||
public int getBitsPerObject(int chunkSectionIndex) {
|
||||
return bitsPerObject[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setBitsPerValue(int chunkSectionIndex, int bitsPerValue) {
|
||||
this.bitsPerValue[chunkSectionIndex] = bitsPerValue;
|
||||
public void setBitsPerObject(int chunkSectionIndex, int bitsPerObject) {
|
||||
this.bitsPerObject[chunkSectionIndex] = bitsPerObject;
|
||||
}
|
||||
|
||||
public DataPalette getDataPalette(int chunkSectionIndex) {
|
||||
return dataPalettes[chunkSectionIndex];
|
||||
@SuppressWarnings("unchecked")
|
||||
public DataPalette<T> getDataPalette(int chunkSectionIndex) {
|
||||
return (DataPalette<T>) dataPalettes[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setDataPalette(int chunkSectionIndex, DataPalette dataPalette) {
|
||||
public void setDataPalette(int chunkSectionIndex, DataPalette<T> dataPalette) {
|
||||
dataPalettes[chunkSectionIndex] = dataPalette;
|
||||
}
|
||||
|
||||
@@ -71,15 +67,16 @@ public class PacketPlayOutMapChunkInfo {
|
||||
dataBitsIndexes[chunkSectionIndex] = dataBitsIndex;
|
||||
}
|
||||
|
||||
public IBlockData[] getPredefinedBlockData(int chunkSectionIndex) {
|
||||
return predefinedBlockData[chunkSectionIndex];
|
||||
@SuppressWarnings("unchecked")
|
||||
public T[] getPredefinedObjects(int chunkSectionIndex) {
|
||||
return (T[]) predefinedObjects[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setPredefinedBlockData(int chunkSectionIndex, IBlockData[] predefinedBlockData) {
|
||||
this.predefinedBlockData[chunkSectionIndex] = predefinedBlockData;
|
||||
public void setPredefinedObjects(int chunkSectionIndex, T[] predefinedObjects) {
|
||||
this.predefinedObjects[chunkSectionIndex] = predefinedObjects;
|
||||
}
|
||||
|
||||
public boolean isWritten(int chunkSectionIndex) {
|
||||
return bitsPerValue[chunkSectionIndex] != 0;
|
||||
return bitsPerObject[chunkSectionIndex] != 0;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import io.netty.buffer.ByteBuf;
|
||||
public class DataBitsReader {
|
||||
|
||||
private ByteBuf dataBits; // Akarin
|
||||
private int bitsPerValue;
|
||||
private int bitsPerObject;
|
||||
private int mask;
|
||||
private int longInDataBitsIndex;
|
||||
private int bitInLongIndex;
|
||||
@@ -15,9 +15,9 @@ public class DataBitsReader {
|
||||
this.dataBits = dataBits;
|
||||
}
|
||||
|
||||
public void setBitsPerValue(int bitsPerValue) {
|
||||
this.bitsPerValue = bitsPerValue;
|
||||
mask = (1 << bitsPerValue) - 1;
|
||||
public void setBitsPerObject(int bitsPerObject) {
|
||||
this.bitsPerObject = bitsPerObject;
|
||||
mask = (1 << bitsPerObject) - 1;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
@@ -45,7 +45,7 @@ public class DataBitsReader {
|
||||
|
||||
public int read() {
|
||||
int value = (int) (current >>> bitInLongIndex) & mask;
|
||||
bitInLongIndex += bitsPerValue;
|
||||
bitInLongIndex += bitsPerObject;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
bitInLongIndex -= 64;
|
||||
@@ -53,7 +53,7 @@ public class DataBitsReader {
|
||||
init();
|
||||
|
||||
if (bitInLongIndex > 0) {
|
||||
value |= current << bitsPerValue - bitInLongIndex & mask;
|
||||
value |= current << bitsPerObject - bitInLongIndex & mask;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import io.netty.buffer.ByteBuf;
|
||||
public class DataBitsWriter {
|
||||
|
||||
private ByteBuf dataBits; // Akarin
|
||||
private int bitsPerValue;
|
||||
private int bitsPerObject;
|
||||
private long mask;
|
||||
private int longInDataBitsIndex;
|
||||
private int bitInLongIndex;
|
||||
@@ -16,9 +16,9 @@ public class DataBitsWriter {
|
||||
this.dataBits = dataBits;
|
||||
}
|
||||
|
||||
public void setBitsPerValue(int bitsPerValue) {
|
||||
this.bitsPerValue = bitsPerValue;
|
||||
mask = (1 << bitsPerValue) - 1;
|
||||
public void setBitsPerObject(int bitsPerObject) {
|
||||
this.bitsPerObject = bitsPerObject;
|
||||
mask = (1 << bitsPerObject) - 1;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
@@ -66,7 +66,7 @@ public class DataBitsWriter {
|
||||
public void write(int value) {
|
||||
current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex;
|
||||
dirty = true;
|
||||
bitInLongIndex += bitsPerValue;
|
||||
bitInLongIndex += bitsPerObject;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
finish();
|
||||
@@ -75,14 +75,14 @@ public class DataBitsWriter {
|
||||
init();
|
||||
|
||||
if (bitInLongIndex > 0) {
|
||||
current = current & ~(mask >>> bitsPerValue - bitInLongIndex) | (value & mask) >>> bitsPerValue - bitInLongIndex;
|
||||
current = current & ~(mask >>> bitsPerObject - bitInLongIndex) | (value & mask) >>> bitsPerObject - bitInLongIndex;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void skip() {
|
||||
bitInLongIndex += bitsPerValue;
|
||||
bitInLongIndex += bitsPerObject;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
finish();
|
||||
|
||||
@@ -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.capacity()); // 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,7 +121,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
||||
packetlistenerplayout.a(this);
|
||||
}
|
||||
|
||||
private ByteBuf g() { return allocateBuffer(-1); } // 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
|
||||
|
||||
@@ -130,25 +129,27 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -15,7 +16,8 @@ if [ "$2" == "--setup" ] || [ "$3" == "--setup" ] || [ "$4" == "--setup" ]; then
|
||||
if [ -d "Minecraft" ]; then
|
||||
rm Minecraft/ -r
|
||||
fi
|
||||
git clone https://github.com/LegacyGamerHD/Minecraft.git
|
||||
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"
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
(
|
||||
set -e
|
||||
basedir="$pwd"
|
||||
basedir="$(pwd -P)"
|
||||
version="pre/1.13"
|
||||
|
||||
(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
|
||||
|
||||
221
sources/pom.xml
221
sources/pom.xml
@@ -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.78.Final</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.destroystokyo.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
@@ -51,30 +44,6 @@
|
||||
<version>${minecraft.version}-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.jopt-simple</groupId>
|
||||
<artifactId>jopt-simple</artifactId>
|
||||
<version>5.0.4</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>
|
||||
@@ -86,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
|
||||
@@ -96,36 +64,56 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.17.2</version>
|
||||
<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>
|
||||
<version>2.17.2</version>
|
||||
<version>2.8.1</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-iostreams</artifactId>
|
||||
<version>2.17.2</version>
|
||||
<version>2.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Paper - Async loggers -->
|
||||
<dependency>
|
||||
<groupId>com.lmax</groupId>
|
||||
<artifactId>disruptor</artifactId>
|
||||
<version>3.4.4</version>
|
||||
<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>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.1</version>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -134,42 +122,16 @@
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Akarin -->
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>9.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-tree</artifactId>
|
||||
<version>9.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-analysis</artifactId>
|
||||
<version>9.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-util</artifactId>
|
||||
<version>9.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-commons</artifactId>
|
||||
<version>9.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.akarin</groupId>
|
||||
<artifactId>mixin</artifactId>
|
||||
<version>0.8.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.akarin</groupId>
|
||||
<artifactId>legacylauncher</artifactId>
|
||||
<version>1.26</version>
|
||||
<version>2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>mixin</artifactId>
|
||||
<version>0.7.11-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.concurrent-locks</groupId>
|
||||
@@ -179,14 +141,18 @@
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>2.9.3</version>
|
||||
<version>2.6.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>elmakers-repo</id>
|
||||
<url>http://maven.elmakers.com/repository/</url>
|
||||
<id>akarin-repo</id>
|
||||
<url>https://raw.githubusercontent.com/Akarin-project/akarin-repo/master/repository</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spigotmc-public</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spongepowered-repo</id>
|
||||
@@ -196,36 +162,12 @@
|
||||
<id>nallar-repo</id>
|
||||
<url>http://repo.nallar.me/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>josephworks</id>
|
||||
<url>https://repo.josephworks.net/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype-nexusg</id>
|
||||
<url>https://oss.sonatype.org/content/repositories</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>papermc</id>
|
||||
<url>https://papermc.io/repo/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>paper</id>
|
||||
<url>https://papermc.io/repo/repository/maven-public/</url>
|
||||
<id>spigotmc-public</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
@@ -240,7 +182,7 @@
|
||||
<version>1.3</version>
|
||||
<configuration>
|
||||
<outputPrefix>git-Akarin-</outputPrefix>
|
||||
<scmDirectory>../..</scmDirectory> <!-- Akarin -->
|
||||
<scmDirectory>../..</scmDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
@@ -261,12 +203,12 @@
|
||||
<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>
|
||||
<Specification-Version>${api.version}</Specification-Version>
|
||||
<Specification-Vendor>Bukkit Team</Specification-Vendor>
|
||||
<Multi-Release>true</Multi-Release> <!-- Paper start - update log4j -->
|
||||
</manifestEntries>
|
||||
<manifestSections>
|
||||
<manifestSection>
|
||||
@@ -294,7 +236,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<version>3.1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
@@ -303,37 +245,6 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation> <!-- Paper -->
|
||||
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>org.spigotmc:minecraft-server:**</artifact>
|
||||
<excludes>
|
||||
<exclude>io/netty/**</exclude>
|
||||
<exclude>org/apache/logging/log4j/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>io.akarin:legacylauncher:**</artifact>
|
||||
<excludes>
|
||||
<exclude>org/objectweb/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>com.destroystokyo.paper:paper-api:**</artifact>
|
||||
<excludes>
|
||||
<exclude>org/objectweb/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
|
||||
<relocations>
|
||||
<!-- Paper - Workaround for hardcoded path lookup in dependency, easier than forking it - GH-189 -->
|
||||
<!--<relocation>-->
|
||||
@@ -365,16 +276,27 @@
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/services/java.sql.Driver</resource>
|
||||
</transformer>
|
||||
<transformer implementation="io.github.edwgiz.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" />
|
||||
<transformer implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer" />
|
||||
</transformers>
|
||||
<!-- Akarin - Avoid signature failure -->
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.github.edwgiz</groupId>
|
||||
<artifactId>log4j-maven-shade-plugin-extensions</artifactId>
|
||||
<version>2.17.2</version>
|
||||
<groupId>com.github.edwgiz</groupId>
|
||||
<artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>
|
||||
<version>2.8.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
@@ -382,18 +304,11 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>org/spigotmc/CaseInsensitiveHashingStrategy.java</exclude>
|
||||
<exclude>org/spigotmc/CaseInsensitiveMap.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<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>
|
||||
@@ -417,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>
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
package co.aikar.timings;
|
||||
|
||||
import com.google.common.collect.MapMaker;
|
||||
import net.minecraft.server.*;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import org.bukkit.craftbukkit.scheduler.CraftTask;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class MinecraftTimings {
|
||||
|
||||
public static final Timing playerListTimer = Timings.ofSafe("Player List");
|
||||
public static final Timing commandFunctionsTimer = Timings.ofSafe("Command Functions");
|
||||
public static final Timing connectionTimer = Timings.ofSafe("Connection Handler");
|
||||
public static final Timing tickablesTimer = Timings.ofSafe("Tickables");
|
||||
public static final Timing minecraftSchedulerTimer = Timings.ofSafe("Minecraft Scheduler");
|
||||
public static final Timing bukkitSchedulerTimer = Timings.ofSafe("Bukkit Scheduler");
|
||||
public static final Timing bukkitSchedulerPendingTimer = Timings.ofSafe("Bukkit Scheduler - Pending");
|
||||
public static final Timing bukkitSchedulerFinishTimer = Timings.ofSafe("Bukkit Scheduler - Finishing");
|
||||
public static final Timing chunkIOTickTimer = Timings.ofSafe("ChunkIOTick");
|
||||
public static final Timing timeUpdateTimer = Timings.ofSafe("Time Update");
|
||||
public static final Timing serverCommandTimer = Timings.ofSafe("Server Command");
|
||||
public static final Timing savePlayers = Timings.ofSafe("Save Players");
|
||||
|
||||
public static final Timing tickEntityTimer = Timings.ofSafe("## tickEntity");
|
||||
public static final Timing tickTileEntityTimer = Timings.ofSafe("## tickTileEntity");
|
||||
public static final Timing packetProcessTimer = Timings.ofSafe("## Packet Processing");
|
||||
public static final Timing scheduledBlocksTimer = Timings.ofSafe("## Scheduled Blocks");
|
||||
public static final Timing structureGenerationTimer = Timings.ofSafe("Structure Generation");
|
||||
|
||||
public static final Timing processQueueTimer = Timings.ofSafe("processQueue");
|
||||
|
||||
public static final Timing playerCommandTimer = Timings.ofSafe("playerCommand");
|
||||
|
||||
public static final Timing entityActivationCheckTimer = Timings.ofSafe("entityActivationCheck");
|
||||
|
||||
public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update");
|
||||
public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate");
|
||||
|
||||
private static final Map<Class<? extends Runnable>, String> taskNameCache = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
private MinecraftTimings() {}
|
||||
|
||||
/**
|
||||
* Gets a timer associated with a plugins tasks.
|
||||
* @param bukkitTask
|
||||
* @param period
|
||||
* @return
|
||||
*/
|
||||
public static Timing getPluginTaskTimings(BukkitTask bukkitTask, long period) {
|
||||
if (!bukkitTask.isSync()) {
|
||||
return NullTimingHandler.NULL;
|
||||
}
|
||||
Plugin plugin;
|
||||
|
||||
Runnable task = ((CraftTask) bukkitTask).task;
|
||||
|
||||
final Class<? extends Runnable> taskClass = task.getClass();
|
||||
if (bukkitTask.getOwner() != null) {
|
||||
plugin = bukkitTask.getOwner();
|
||||
} else {
|
||||
plugin = TimingsManager.getPluginByClassloader(taskClass);
|
||||
}
|
||||
|
||||
final String taskname = taskNameCache.computeIfAbsent(taskClass, clazz ->
|
||||
clazz.isAnonymousClass() || clazz.isLocalClass()
|
||||
? clazz.getName()
|
||||
: clazz.getCanonicalName());
|
||||
|
||||
StringBuilder name = new StringBuilder(64);
|
||||
name.append("Task: ").append(taskname);
|
||||
if (period > 0) {
|
||||
name.append(" (interval:").append(period).append(")");
|
||||
} else {
|
||||
name.append(" (Single)");
|
||||
}
|
||||
|
||||
if (plugin == null) {
|
||||
return Timings.ofSafe(null, name.toString());
|
||||
}
|
||||
|
||||
return Timings.ofSafe(plugin, name.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a named timer for the specified entity type to track type specific timings.
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
public static Timing getEntityTimings(Entity entity) {
|
||||
String entityType = entity.getClass().getName();
|
||||
return Timings.ofSafe("Minecraft", "## tickEntity - " + entityType, tickEntityTimer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a named timer for the specified tile entity type to track type specific timings.
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
public static Timing getTileEntityTimings(TileEntity entity) {
|
||||
String entityType = entity.getClass().getName();
|
||||
return Timings.ofSafe("Minecraft", "## tickTileEntity - " + entityType, tickTileEntityTimer);
|
||||
}
|
||||
public static Timing getCancelTasksTimer() {
|
||||
return Timings.ofSafe("Cancel Tasks");
|
||||
}
|
||||
public static Timing getCancelTasksTimer(Plugin plugin) {
|
||||
return Timings.ofSafe(plugin, "Cancel Tasks");
|
||||
}
|
||||
|
||||
public static void stopServer() {
|
||||
TimingsManager.stopServer();
|
||||
}
|
||||
|
||||
public static Timing getBlockTiming(Block block) {
|
||||
return Timings.ofSafe("## Scheduled Block: " + block.getName(), scheduledBlocksTimer);
|
||||
}
|
||||
|
||||
public static Timing getStructureTiming(StructureGenerator structureGenerator) {
|
||||
return Timings.ofSafe("Structure Generator - " + structureGenerator.getName(), structureGenerationTimer);
|
||||
}
|
||||
|
||||
public static Timing getPacketTiming(Packet packet) {
|
||||
return Timings.ofSafe("## Packet - " + packet.getClass().getSimpleName(), packetProcessTimer);
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2014-2016 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 net.minecraft.server.BiomeBase.BiomeMeta;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumCreatureType;
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.WorldServer;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.generator.InternalChunkGenerator;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class TimedChunkGenerator extends InternalChunkGenerator {
|
||||
private final WorldServer world;
|
||||
private final InternalChunkGenerator timedGenerator;
|
||||
|
||||
public TimedChunkGenerator(WorldServer worldServer, InternalChunkGenerator gen) {
|
||||
world = worldServer;
|
||||
timedGenerator = gen;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public byte[] generate(org.bukkit.World world, Random random, int x, int z) {
|
||||
return timedGenerator.generate(world, random, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public short[][] generateExtBlockSections(org.bukkit.World world, Random random, int x, int z,
|
||||
BiomeGrid biomes) {
|
||||
return timedGenerator.generateExtBlockSections(world, random, x, z, biomes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public byte[][] generateBlockSections(org.bukkit.World world, Random random, int x, int z,
|
||||
BiomeGrid biomes) {
|
||||
return timedGenerator.generateBlockSections(world, random, x, z, biomes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkData generateChunkData(org.bukkit.World world, Random random, int x, int z, BiomeGrid biome) {
|
||||
return timedGenerator.generateChunkData(world, random, x, z, biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSpawn(org.bukkit.World world, int x, int z) {
|
||||
return timedGenerator.canSpawn(world, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BlockPopulator> getDefaultPopulators(org.bukkit.World world) {
|
||||
return timedGenerator.getDefaultPopulators(world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getFixedSpawnLocation(org.bukkit.World world, Random random) {
|
||||
return timedGenerator.getFixedSpawnLocation(world, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk getOrCreateChunk(int i, int j) {
|
||||
try (Timing ignored = world.timings.chunkGeneration.startTiming()) {
|
||||
return timedGenerator.getOrCreateChunk(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recreateStructures(int i, int j) {
|
||||
try (Timing ignored = world.timings.syncChunkLoadStructuresTimer.startTiming()) {
|
||||
timedGenerator.recreateStructures(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean a(Chunk chunk, int i, int j) {
|
||||
return timedGenerator.a(chunk, i, j);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BiomeMeta> getMobsFor(EnumCreatureType enumcreaturetype, BlockPosition blockposition) {
|
||||
return timedGenerator.getMobsFor(enumcreaturetype, blockposition);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BlockPosition findNearestMapFeature(World world, String s, BlockPosition blockposition, boolean flag) {
|
||||
return timedGenerator.findNearestMapFeature(world, s, blockposition, flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recreateStructures(Chunk chunk, int i, int j) {
|
||||
try (Timing ignored = world.timings.syncChunkLoadStructuresTimer.startTiming()) {
|
||||
timedGenerator.recreateStructures(chunk, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean a(World world, String s, BlockPosition blockPosition) {
|
||||
return timedGenerator.a(world, s, blockPosition);
|
||||
}
|
||||
}
|
||||
127
sources/src/main/java/co/aikar/timings/TimingData.java
Normal file
127
sources/src/main/java/co/aikar/timings/TimingData.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -25,25 +25,23 @@ package co.aikar.timings;
|
||||
|
||||
import co.aikar.util.LoadingIntMap;
|
||||
import io.akarin.api.internal.Akari;
|
||||
import io.akarin.api.internal.Akari.AssignableThread;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
|
||||
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 {
|
||||
String name;
|
||||
|
||||
private static AtomicInteger idPool = new AtomicInteger(1);
|
||||
static Deque<TimingHandler> TIMING_STACK = new ArrayDeque<>();
|
||||
final int id = idPool.getAndIncrement();
|
||||
|
||||
final TimingIdentifier identifier;
|
||||
final String name;
|
||||
private final boolean verbose;
|
||||
|
||||
private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
|
||||
@@ -51,15 +49,22 @@ class TimingHandler implements Timing {
|
||||
final TimingData record;
|
||||
private final TimingHandler groupHandler;
|
||||
|
||||
private long start = 0;
|
||||
private int timingDepth = 0;
|
||||
private boolean added;
|
||||
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) {
|
||||
this.identifier = id;
|
||||
this.verbose = id.name.startsWith("##");
|
||||
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;
|
||||
|
||||
@@ -72,113 +77,95 @@ class TimingHandler implements Timing {
|
||||
}
|
||||
|
||||
void processTick(boolean violated) {
|
||||
if (timingDepth != 0 || record.getCurTickCount() == 0) {
|
||||
timingDepth = 0;
|
||||
start = 0;
|
||||
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 (Timing) this;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopTimingIfSync() {
|
||||
if (Akari.isPrimaryThread(false)) {
|
||||
stopTiming(true); // Avoid twice thread check
|
||||
}
|
||||
stopTiming();
|
||||
}
|
||||
|
||||
public Timing startTiming() {
|
||||
if (enabled && Bukkit.isPrimaryThread() && ++timingDepth == 1) {
|
||||
start = System.nanoTime();
|
||||
TIMING_STACK.addLast(this);
|
||||
if (enabled && /*Bukkit.isPrimaryThread() &&*/ timingDepth.incrementAndGet() == 1) { // Akarin
|
||||
start.getAndSet(System.nanoTime());
|
||||
parent = TimingsManager.CURRENT;
|
||||
TimingsManager.CURRENT = this;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void stopTiming() {
|
||||
stopTiming(false);
|
||||
}
|
||||
|
||||
public void stopTiming(long start) {
|
||||
if (enabled) addDiff(System.nanoTime() - start);
|
||||
}
|
||||
|
||||
public void stopTiming(boolean alreadySync) {
|
||||
if (!enabled) return;
|
||||
if (!alreadySync) {
|
||||
Thread curThread = Thread.currentThread();
|
||||
if (curThread.getClass() == AssignableThread.class) return;
|
||||
if (curThread != MinecraftServer.getServer().primaryThread) {
|
||||
if (AkarinGlobalConfig.silentAsyncTimings) return;
|
||||
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name);
|
||||
Thread.dumpStack();
|
||||
}
|
||||
}
|
||||
|
||||
// Main thread ensured
|
||||
if (--timingDepth == 0 && start != 0) {
|
||||
addDiff(System.nanoTime() - start);
|
||||
start = 0;
|
||||
if (enabled && start.get() != 0 && /*Bukkit.isPrimaryThread() &&*/ timingDepth.decrementAndGet() == 0) { // Akarin
|
||||
long prev = start.getAndSet(0); // Akarin
|
||||
addDiff(System.nanoTime() - prev); // Akarin
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void abort() {
|
||||
|
||||
public void abort() {
|
||||
if (enabled && timingDepth.get() > 0) {
|
||||
start.getAndSet(0);
|
||||
}
|
||||
}
|
||||
|
||||
void addDiff(long diff) {
|
||||
if (this != null) {
|
||||
this.children.get(id).add(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, this);
|
||||
groupHandler.children.get(id).add(diff);
|
||||
}
|
||||
}
|
||||
void addDiff(long diff, TimingHandler parent) {
|
||||
if (parent != null) {
|
||||
parent.children.get(id).add(diff);
|
||||
}
|
||||
|
||||
record.add(diff);
|
||||
if (!added) {
|
||||
added = true;
|
||||
timed = true;
|
||||
TimingsManager.HANDLERS.add(this);
|
||||
}
|
||||
if (groupHandler != null) {
|
||||
groupHandler.addDiff(diff, parent);
|
||||
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 = 0;
|
||||
timingDepth = 0;
|
||||
start.set(0);
|
||||
timingDepth.set(0);
|
||||
added = false;
|
||||
Akari.timingsLock.lock(); // Akarin
|
||||
children.clear();
|
||||
Akari.timingsLock.unlock(); // Akarin
|
||||
checkEnabled();
|
||||
}
|
||||
|
||||
@@ -198,7 +185,8 @@ class TimingHandler implements Timing {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is simply for the Closeable interface so it can be used with try-with-resources ()
|
||||
* This is simply for the Closeable interface so it can be used with
|
||||
* try-with-resources ()
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
@@ -218,11 +206,13 @@ class TimingHandler implements Timing {
|
||||
}
|
||||
|
||||
TimingData[] cloneChildren() {
|
||||
final TimingData[] clonedChildren = new TimingData[children.size()];
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
355
sources/src/main/java/co/aikar/timings/TimingHistory.java
Normal file
355
sources/src/main/java/co/aikar/timings/TimingHistory.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
198
sources/src/main/java/co/aikar/timings/TimingsManager.java
Normal file
198
sources/src/main/java/co/aikar/timings/TimingsManager.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package co.aikar.timings;
|
||||
|
||||
import net.minecraft.server.World;
|
||||
|
||||
/**
|
||||
* Set of timers per world, to track world specific timings.
|
||||
*/
|
||||
public class WorldTimingsHandler {
|
||||
public final Timing mobSpawn;
|
||||
public final Timing doChunkUnload;
|
||||
public final Timing doPortalForcer;
|
||||
public final Timing scheduledBlocks;
|
||||
public final Timing scheduledBlocksCleanup;
|
||||
public final Timing scheduledBlocksTicking;
|
||||
public final Timing chunkTicks;
|
||||
public final Timing lightChunk;
|
||||
public final Timing chunkTicksBlocks;
|
||||
public final Timing doVillages;
|
||||
public final Timing doChunkMap;
|
||||
public final Timing doChunkMapUpdate;
|
||||
public final Timing doChunkMapToUpdate;
|
||||
public final Timing doChunkMapSortMissing;
|
||||
public final Timing doChunkMapSortSendToPlayers;
|
||||
public final Timing doChunkMapPlayersNeedingChunks;
|
||||
public final Timing doChunkMapPendingSendToPlayers;
|
||||
public final Timing doChunkMapUnloadChunks;
|
||||
public final Timing doChunkGC;
|
||||
public final Timing doSounds;
|
||||
public final Timing entityRemoval;
|
||||
public final Timing entityTick;
|
||||
public final Timing tileEntityTick;
|
||||
public final Timing tileEntityPending;
|
||||
public final Timing tracker1;
|
||||
public final Timing tracker2;
|
||||
public final Timing doTick;
|
||||
public final Timing tickEntities;
|
||||
|
||||
public final Timing syncChunkLoadTimer;
|
||||
public final Timing syncChunkLoadDataTimer;
|
||||
public final Timing syncChunkLoadStructuresTimer;
|
||||
public final Timing syncChunkLoadPostTimer;
|
||||
public final Timing syncChunkLoadNBTTimer;
|
||||
public final Timing syncChunkLoadPopulateNeighbors;
|
||||
public final Timing chunkGeneration;
|
||||
public final Timing chunkIOStage1;
|
||||
public final Timing chunkIOStage2;
|
||||
public final Timing worldSave;
|
||||
public final Timing worldSaveChunks;
|
||||
public final Timing worldSaveLevel;
|
||||
public final Timing chunkSaveData;
|
||||
|
||||
public final Timing lightingQueueTimer;
|
||||
|
||||
public WorldTimingsHandler(World server) {
|
||||
String name = server.worldData.getName() +" - ";
|
||||
|
||||
mobSpawn = Timings.ofSafe(name + "mobSpawn");
|
||||
doChunkUnload = Timings.ofSafe(name + "doChunkUnload");
|
||||
scheduledBlocks = Timings.ofSafe(name + "Scheduled Blocks");
|
||||
scheduledBlocksCleanup = Timings.ofSafe(name + "Scheduled Blocks - Cleanup");
|
||||
scheduledBlocksTicking = Timings.ofSafe(name + "Scheduled Blocks - Ticking");
|
||||
chunkTicks = Timings.ofSafe(name + "Chunk Ticks");
|
||||
lightChunk = Timings.ofSafe(name + "Light Chunk");
|
||||
chunkTicksBlocks = Timings.ofSafe(name + "Chunk Ticks - Blocks");
|
||||
doVillages = Timings.ofSafe(name + "doVillages");
|
||||
doChunkMap = Timings.ofSafe(name + "doChunkMap");
|
||||
doChunkMapUpdate = Timings.ofSafe(name + "doChunkMap - Update");
|
||||
doChunkMapToUpdate = Timings.ofSafe(name + "doChunkMap - To Update");
|
||||
doChunkMapSortMissing = Timings.ofSafe(name + "doChunkMap - Sort Missing");
|
||||
doChunkMapSortSendToPlayers = Timings.ofSafe(name + "doChunkMap - Sort Send To Players");
|
||||
doChunkMapPlayersNeedingChunks = Timings.ofSafe(name + "doChunkMap - Players Needing Chunks");
|
||||
doChunkMapPendingSendToPlayers = Timings.ofSafe(name + "doChunkMap - Pending Send To Players");
|
||||
doChunkMapUnloadChunks = Timings.ofSafe(name + "doChunkMap - Unload Chunks");
|
||||
doSounds = Timings.ofSafe(name + "doSounds");
|
||||
doChunkGC = Timings.ofSafe(name + "doChunkGC");
|
||||
doPortalForcer = Timings.ofSafe(name + "doPortalForcer");
|
||||
entityTick = Timings.ofSafe(name + "entityTick");
|
||||
entityRemoval = Timings.ofSafe(name + "entityRemoval");
|
||||
tileEntityTick = Timings.ofSafe(name + "tileEntityTick");
|
||||
tileEntityPending = Timings.ofSafe(name + "tileEntityPending");
|
||||
|
||||
syncChunkLoadTimer = Timings.ofSafe(name + "syncChunkLoad");
|
||||
syncChunkLoadDataTimer = Timings.ofSafe(name + "syncChunkLoad - Data");
|
||||
syncChunkLoadStructuresTimer = Timings.ofSafe(name + "chunkLoad - recreateStructures");
|
||||
syncChunkLoadPostTimer = Timings.ofSafe(name + "chunkLoad - Post");
|
||||
syncChunkLoadNBTTimer = Timings.ofSafe(name + "chunkLoad - NBT");
|
||||
syncChunkLoadPopulateNeighbors = Timings.ofSafe(name + "chunkLoad - Populate Neighbors");
|
||||
chunkGeneration = Timings.ofSafe(name + "chunkGeneration");
|
||||
chunkIOStage1 = Timings.ofSafe(name + "ChunkIO Stage 1 - DiskIO");
|
||||
chunkIOStage2 = Timings.ofSafe(name + "ChunkIO Stage 2 - Post Load");
|
||||
worldSave = Timings.ofSafe(name + "World Save");
|
||||
worldSaveLevel = Timings.ofSafe(name + "World Save - Level");
|
||||
worldSaveChunks = Timings.ofSafe(name + "World Save - Chunks");
|
||||
chunkSaveData = Timings.ofSafe(name + "Chunk Save - Data");
|
||||
|
||||
tracker1 = Timings.ofSafe(name + "tracker stage 1");
|
||||
tracker2 = Timings.ofSafe(name + "tracker stage 2");
|
||||
doTick = Timings.ofSafe(name + "doTick");
|
||||
tickEntities = Timings.ofSafe(name + "tickEntities");
|
||||
|
||||
lightingQueueTimer = Timings.ofSafe(name + "Lighting Queue");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -160,11 +160,6 @@ public class AkarinGlobalConfig {
|
||||
playersPerIOThread = getInt("core.players-per-chunk-io-thread", 50);
|
||||
}
|
||||
|
||||
public static boolean silentAsyncTimings;
|
||||
private static void silentAsyncTimings() {
|
||||
silentAsyncTimings = getBoolean("core.always-silent-async-timing", false);
|
||||
}
|
||||
|
||||
public static long timeUpdateInterval;
|
||||
private static void timeUpdateInterval() {
|
||||
timeUpdateInterval = getSeconds(getString("core.tick-rate.world-time-update-interval", "1s")) * 10;
|
||||
@@ -195,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", "");
|
||||
|
||||
@@ -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,10 +97,10 @@ public class AkarinSlackScheduler extends Thread {
|
||||
}
|
||||
|
||||
try {
|
||||
long sleepFixed = 100 - (System.currentTimeMillis() - startProcessTiming);
|
||||
if (sleepFixed > 0) Thread.sleep(sleepFixed);
|
||||
} catch (InterruptedException interrupted) {
|
||||
continue;
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
Akari.logger.warn("Slack scheduler thread was interrupted unexpectly!");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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..");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 +40,7 @@ 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;
|
||||
private int cachedWorldSize;
|
||||
|
||||
@Overwrite
|
||||
public String getServerModName() {
|
||||
@@ -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);
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
synchronized (((IMixinWorldServer) entityWorld).lock()) {
|
||||
tickEntities(entityWorld);
|
||||
}
|
||||
}, null/*new TimingSignal(entityWorld, true)*/);
|
||||
|
||||
if (AkarinGlobalConfig.parallelMode != 1) {
|
||||
int fi = i;
|
||||
WorldServer interlacedWorld = null;
|
||||
for (WorldServer world : worldServer.values()) {
|
||||
if (interlacedWorld == null) {
|
||||
interlacedWorld = world;
|
||||
} else {
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
WorldServer world = worlds.get(fi);
|
||||
synchronized (((IMixinWorldServer) world).lock()) {
|
||||
tickWorld(world);
|
||||
synchronized (((IMixinWorldServer) world).tickLock()) {
|
||||
tickEntities(world);
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
if (AkarinGlobalConfig.parallelMode != 1 /* >= 2 */) {
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.akarin.server.mixin.core;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
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;
|
||||
|
||||
@Mixin(targets = "co.aikar.timings.TimingHandler", remap = false)
|
||||
public abstract class MixinTimingHandler {
|
||||
@Shadow @Final String name;
|
||||
@Shadow private boolean enabled;
|
||||
@Shadow private AtomicLong start;
|
||||
@Shadow private AtomicInteger timingDepth;
|
||||
|
||||
@Shadow abstract void addDiff(long diff);
|
||||
@Shadow public abstract Timing startTiming();
|
||||
|
||||
@Overwrite
|
||||
public void stopTiming() {
|
||||
stopTiming(false);
|
||||
}
|
||||
|
||||
public void stopTiming(boolean alreadySync) {
|
||||
if (!enabled || start.get() == 0 || timingDepth.decrementAndGet() != 0) return;
|
||||
long prev = start.getAndSet(0); // Akarin
|
||||
addDiff(System.nanoTime() - prev); // Akarin
|
||||
}
|
||||
}
|
||||
@@ -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(() -> {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
this.world.getTracker().track(entity);
|
||||
this.world.getTracker().entriesLock.writeLock().unlock(); // Akarin
|
||||
synchronized (((IMixinWorldServer) this.world).trackLock()) { // Akarin
|
||||
this.world.getTracker().track(entity);
|
||||
}
|
||||
|
||||
if (entity instanceof EntityPlayer) {
|
||||
this.world.worldProvider.a((EntityPlayer) entity);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
package io.akarin.server.mixin.optimization;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import net.minecraft.server.Block;
|
||||
import net.minecraft.server.BlockChest;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.EnumDirection;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.Material;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
@Mixin(value = BlockChest.class, remap = false)
|
||||
public abstract class MixinBlockChest extends Block {
|
||||
protected MixinBlockChest(Material material) {
|
||||
super(material);
|
||||
}
|
||||
@Shadow public abstract IBlockData e(World world, BlockPosition blockposition, IBlockData iblockdata);
|
||||
@Overwrite
|
||||
public void onPlace(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
//((BlockChest)(Object)this).e(world, blockposition, iblockdata);
|
||||
e(world, blockposition, iblockdata);
|
||||
Iterator<EnumDirection> iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EnumDirection enumdirection = iterator.next();
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection);
|
||||
// NeonPaper start - Dont load chunks for chests
|
||||
final IBlockData iblockdata1 = world.isLoaded(blockposition1) ? world.getType(blockposition1) : null;
|
||||
if (iblockdata1 == null) {
|
||||
continue;
|
||||
}
|
||||
// NeonPaper end
|
||||
|
||||
if (iblockdata1.getBlock() == this) {
|
||||
//((BlockChest)(Object)this).e(world, blockposition1, iblockdata1);
|
||||
e(world, blockposition1, iblockdata1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package io.akarin.server.mixin.optimization;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
||||
import net.minecraft.server.BlockFluids;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.BlockStationary;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.Material;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
@Mixin(value = BlockStationary.class, remap = false)
|
||||
public abstract class MixinBlockStationary extends BlockFluids {
|
||||
protected MixinBlockStationary(Material material) {
|
||||
super(material);
|
||||
}
|
||||
@Overwrite
|
||||
private boolean d(World world, BlockPosition blockposition) {
|
||||
if (blockposition.getY() >= 0 && blockposition.getY() < 256) {
|
||||
IBlockData blockData = world.getTypeIfLoaded(blockposition);
|
||||
|
||||
if (blockData != null) {
|
||||
return blockData.getMaterial().isBurnable();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package io.akarin.server.mixin.optimization;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import net.minecraft.server.Container;
|
||||
import net.minecraft.server.ContainerHorse;
|
||||
import net.minecraft.server.Entity;
|
||||
import net.minecraft.server.EntityHorseAbstract;
|
||||
import net.minecraft.server.EntityHuman;
|
||||
import net.minecraft.server.IInventory;
|
||||
|
||||
@Mixin(value = ContainerHorse.class, remap = false)
|
||||
public abstract class MixinContainerHorse extends Container {
|
||||
@Shadow private IInventory a;
|
||||
@Shadow private EntityHorseAbstract f;
|
||||
|
||||
@Overwrite
|
||||
public boolean canUse(EntityHuman entityhuman) {
|
||||
return this.a.a(entityhuman) && this.f.isAlive() && this.f.valid && this.f.g((Entity) entityhuman) < 8.0F; // NeonPaper! - Fix MC-161754
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import org.spongepowered.asm.mixin.Shadow;
|
||||
import net.minecraft.server.AxisAlignedBB;
|
||||
import net.minecraft.server.Entity;
|
||||
import net.minecraft.server.Material;
|
||||
import net.minecraft.server.MathHelper;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
@@ -16,19 +15,11 @@ public abstract class MixinEntity {
|
||||
@Shadow public World world;
|
||||
@Shadow public abstract AxisAlignedBB getBoundingBox();
|
||||
|
||||
@Shadow public boolean noclip;
|
||||
@Shadow public float R;
|
||||
@Shadow public double locX;
|
||||
@Shadow public double locZ;
|
||||
@Shadow public abstract boolean x(Entity entity);
|
||||
@Shadow public abstract boolean isVehicle();
|
||||
@Shadow public abstract void f(double d0, double d1, double d2);
|
||||
|
||||
private boolean isInLava;
|
||||
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
|
||||
@@ -36,40 +27,8 @@ 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;
|
||||
}
|
||||
@Overwrite
|
||||
public void collide(Entity entity) {
|
||||
if (entity.noclip || this.noclip || this.x(entity)) return; // NeonPaper - Test this earlier
|
||||
double d0 = entity.locX - this.locX;
|
||||
double d1 = entity.locZ - this.locZ;
|
||||
double d2 = MathHelper.a(d0, d1);
|
||||
|
||||
if (d2 >= 0.009999999776482582D) {
|
||||
d2 = (double) MathHelper.sqrt(d2);
|
||||
d0 /= d2;
|
||||
d1 /= d2;
|
||||
double d3 = 1.0D / d2;
|
||||
|
||||
if (d3 > 1.0D) {
|
||||
d3 = 1.0D;
|
||||
}
|
||||
|
||||
d0 *= d3;
|
||||
d1 *= d3;
|
||||
d0 *= 0.05000000074505806D;
|
||||
d1 *= 0.05000000074505806D;
|
||||
d0 *= (double) (1.0F - this.R);
|
||||
d1 *= (double) (1.0F - this.R);
|
||||
if (!this.isVehicle()) {
|
||||
this.f(-d0, 0.0D, -d1);
|
||||
}
|
||||
|
||||
if (!entity.isVehicle()) {
|
||||
entity.f(d0, 0.0D, d1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
package io.akarin.server.mixin.optimization;
|
||||
|
||||
import org.bukkit.event.player.PlayerShearEntityEvent;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
||||
import net.minecraft.server.Blocks;
|
||||
import net.minecraft.server.EntityCow;
|
||||
import net.minecraft.server.EntityHuman;
|
||||
import net.minecraft.server.EntityItem;
|
||||
import net.minecraft.server.EntityMushroomCow;
|
||||
import net.minecraft.server.EnumHand;
|
||||
import net.minecraft.server.EnumParticle;
|
||||
import net.minecraft.server.ItemStack;
|
||||
import net.minecraft.server.Items;
|
||||
import net.minecraft.server.SoundEffects;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
@Mixin(value = EntityMushroomCow.class, remap = false)
|
||||
public abstract class MixinEntityMushroomCow extends EntityCow {
|
||||
|
||||
public MixinEntityMushroomCow(World world) {
|
||||
super(world);
|
||||
}
|
||||
|
||||
@Overwrite
|
||||
public boolean a(EntityHuman entityhuman, EnumHand enumhand) {
|
||||
ItemStack itemstack = entityhuman.b(enumhand);
|
||||
|
||||
if (itemstack.getItem() == Items.BOWL && this.getAge() >= 0 && !entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack.subtract(1);
|
||||
if (itemstack.isEmpty()) {
|
||||
entityhuman.a(enumhand, new ItemStack(Items.MUSHROOM_STEW));
|
||||
} else if (!entityhuman.inventory.pickup(new ItemStack(Items.MUSHROOM_STEW))) {
|
||||
entityhuman.drop(new ItemStack(Items.MUSHROOM_STEW), false);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (itemstack.getItem() == Items.SHEARS && this.getAge() >= 0) {
|
||||
if (this.dead) return false; // Reaper - Fix cow dupe
|
||||
// CraftBukkit start
|
||||
PlayerShearEntityEvent event = new PlayerShearEntityEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), this.getBukkitEntity());
|
||||
this.world.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.die();
|
||||
this.world.addParticle(EnumParticle.EXPLOSION_LARGE, this.locX, this.locY + (double) (this.length / 2.0F), this.locZ, 0.0D, 0.0D, 0.0D, new int[0]);
|
||||
if (!this.world.isClientSide) {
|
||||
EntityCow entitycow = new EntityCow(this.world);
|
||||
|
||||
entitycow.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch);
|
||||
entitycow.setHealth(this.getHealth());
|
||||
entitycow.aN = this.aN;
|
||||
if (this.hasCustomName()) {
|
||||
entitycow.setCustomName(this.getCustomName());
|
||||
}
|
||||
|
||||
this.world.addEntity(entitycow);
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
this.world.addEntity(new EntityItem(this.world, this.locX, this.locY + (double) this.length, this.locZ, new ItemStack(Blocks.RED_MUSHROOM)));
|
||||
}
|
||||
|
||||
itemstack.damage(1, entityhuman);
|
||||
this.a(SoundEffects.ei, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return super.a(entityhuman, enumhand);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,426 +0,0 @@
|
||||
package io.akarin.server.mixin.optimization;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory;
|
||||
import org.bukkit.event.block.BlockExplodeEvent;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import net.minecraft.server.AxisAlignedBB;
|
||||
import net.minecraft.server.Block;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Blocks;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.ChunkSection;
|
||||
import net.minecraft.server.DamageSource;
|
||||
import net.minecraft.server.EnchantmentProtection;
|
||||
import net.minecraft.server.Entity;
|
||||
import net.minecraft.server.EntityFallingBlock;
|
||||
import net.minecraft.server.EntityHuman;
|
||||
import net.minecraft.server.EntityLiving;
|
||||
import net.minecraft.server.EntityTNTPrimed;
|
||||
import net.minecraft.server.EnumParticle;
|
||||
import net.minecraft.server.Explosion;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.Material;
|
||||
import net.minecraft.server.MathHelper;
|
||||
import net.minecraft.server.SoundCategory;
|
||||
import net.minecraft.server.SoundEffects;
|
||||
import net.minecraft.server.Vec3D;
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.IEntitySelector;
|
||||
|
||||
@Mixin(value = Explosion.class, remap = false)
|
||||
public abstract class MixinExplosion {
|
||||
@Shadow private boolean a;
|
||||
@Shadow private boolean b;
|
||||
@Shadow private Random c = new Random();
|
||||
@Shadow private World world;
|
||||
@Shadow private double posX;
|
||||
@Shadow private double posY;
|
||||
@Shadow private double posZ;
|
||||
@Shadow public Entity source;
|
||||
@Shadow private float size;
|
||||
@Shadow private Map<EntityHuman, Vec3D> k;
|
||||
@Shadow public boolean wasCanceled = false; // CraftBukkit - add field
|
||||
@Shadow private List<BlockPosition> blocks;
|
||||
private final BlockPosition.MutableBlockPosition cachedPos = new BlockPosition.MutableBlockPosition();
|
||||
private int prevChunkX = Integer.MIN_VALUE;
|
||||
private int prevChunkZ = Integer.MIN_VALUE;
|
||||
private Chunk prevChunk;
|
||||
private static final com.google.common.base.Predicate<Entity> hitPredicate = entity -> IEntitySelector.d.apply(entity) && !entity.dead; // Dionysus - Paper - don't hit dead entities
|
||||
|
||||
@Overwrite
|
||||
public void a() {
|
||||
// CraftBukkit start
|
||||
if (this.size < 0.1F) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
// Dionysus start - optimize memory usage from explosions
|
||||
// CaffeineMC optimized raytracing loop
|
||||
// @author JellySquid
|
||||
// @author nopjmp
|
||||
// https://github.com/CaffeineMC/lithium-fabric
|
||||
// Using integer encoding for the block positions provides a massive speedup and prevents us from needing to
|
||||
// allocate a block position for every step we make along each ray, eliminating essentially all the memory
|
||||
// allocations of this function. The overhead of packing block positions into integer format is negligible
|
||||
// compared to a memory allocation and associated overhead of hashing real objects in a set.
|
||||
final LongOpenHashSet touched = new LongOpenHashSet(0);
|
||||
final Random random = this.world.random;
|
||||
for (int rayX = 0; rayX < 16; ++rayX) {
|
||||
boolean xPlane = rayX == 0 || rayX == 15;
|
||||
double vecX = (((float) rayX / 15.0F) * 2.0F) - 1.0F;
|
||||
|
||||
for (int rayY = 0; rayY < 16; ++rayY) {
|
||||
boolean yPlane = rayY == 0 || rayY == 15;
|
||||
double vecY = (((float) rayY / 15.0F) * 2.0F) - 1.0F;
|
||||
|
||||
for (int rayZ = 0; rayZ < 16; ++rayZ) {
|
||||
boolean zPlane = rayZ == 0 || rayZ == 15;
|
||||
|
||||
// We only fire rays from the surface of our origin volume
|
||||
if (xPlane || yPlane || zPlane) {
|
||||
double vecZ = (((float) rayZ / 15.0F) * 2.0F) - 1.0F;
|
||||
|
||||
this.performRayCast(random, vecX, vecY, vecZ, touched);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can now iterate back over the set of positions we modified and re-build BlockPos objects from them
|
||||
// This will only allocate as many objects as there are in the set, where otherwise we would allocate them
|
||||
// each step of a every ray.
|
||||
((ArrayList<BlockPosition>)this.blocks).ensureCapacity(touched.size());
|
||||
for (Long longPos : touched) {
|
||||
this.blocks.add(BlockPosition.fromLong(longPos));
|
||||
}
|
||||
float f3 = this.size * 2.0F;
|
||||
|
||||
int i = MathHelper.floor(this.posX - (double) f3 - 1.0D);
|
||||
int j = MathHelper.floor(this.posX + (double) f3 + 1.0D);
|
||||
int l = MathHelper.floor(this.posY - (double) f3 - 1.0D);
|
||||
int i1 = MathHelper.floor(this.posY + (double) f3 + 1.0D);
|
||||
int j1 = MathHelper.floor(this.posZ - (double) f3 - 1.0D);
|
||||
int k1 = MathHelper.floor(this.posZ + (double) f3 + 1.0D);
|
||||
// Paper start - Fix lag from explosions processing dead entities
|
||||
List<Entity> list = this.world.getEntities(this.source, new AxisAlignedBB(i, l, j1, j, i1, k1), hitPredicate);
|
||||
// Paper end
|
||||
Vec3D vec3d = new Vec3D(this.posX, this.posY, this.posZ);
|
||||
|
||||
for (Entity entity : list) {
|
||||
|
||||
if (!entity.bB()) {
|
||||
double d7 = entity.e(this.posX, this.posY, this.posZ) / (double) f3;
|
||||
|
||||
if (d7 <= 1.0D) {
|
||||
double d8 = entity.locX - this.posX;
|
||||
double d9 = entity.locY + (double) entity.getHeadHeight() - this.posY;
|
||||
double d10 = entity.locZ - this.posZ;
|
||||
double d11 = MathHelper.sqrt(d8 * d8 + d9 * d9 + d10 * d10);
|
||||
|
||||
if (d11 != 0.0D) {
|
||||
d8 /= d11;
|
||||
d9 /= d11;
|
||||
d10 /= d11;
|
||||
double d12 = this.getBlockDensity(vec3d, entity.getBoundingBox()); // Paper - Optimize explosions
|
||||
double d13 = (1.0D - d7) * d12;
|
||||
|
||||
// CraftBukkit start
|
||||
// entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 7.0D * (double) f3 + 1.0D)));
|
||||
CraftEventFactory.entityDamage = source;
|
||||
entity.forceExplosionKnockback = false;
|
||||
boolean wasDamaged = entity.damageEntity(DamageSource.explosion((Explosion)(Object)this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 7.0D * (double) f3 + 1.0D)));
|
||||
CraftEventFactory.entityDamage = null;
|
||||
if (!wasDamaged && !(entity instanceof EntityTNTPrimed || entity instanceof EntityFallingBlock) && !entity.forceExplosionKnockback) {
|
||||
continue;
|
||||
}
|
||||
// CraftBukkit end
|
||||
double d14 = d13;
|
||||
|
||||
if (entity instanceof EntityLiving) {
|
||||
d14 = entity instanceof EntityHuman && world.paperConfig.disableExplosionKnockback ? 0 : EnchantmentProtection.a((EntityLiving) entity, d13); // Paper - Disable explosion knockback
|
||||
}
|
||||
|
||||
entity.motX += d8 * d14;
|
||||
entity.motY += d9 * d14;
|
||||
entity.motZ += d10 * d14;
|
||||
if (entity instanceof EntityHuman) {
|
||||
EntityHuman entityhuman = (EntityHuman) entity;
|
||||
|
||||
if (!entityhuman.isSpectator() && (!entityhuman.z() && !world.paperConfig.disableExplosionKnockback || !entityhuman.abilities.isFlying)) { // Paper - Disable explosion knockback
|
||||
this.k.put(entityhuman, new Vec3D(d8 * d13, d9 * d13, d10 * d13));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private void performRayCast(Random random, double vecX, double vecY, double vecZ, LongOpenHashSet touched) {
|
||||
double dist = Math.sqrt((vecX * vecX) + (vecY * vecY) + (vecZ * vecZ));
|
||||
|
||||
double normX = (vecX / dist) * 0.3D;
|
||||
double normY = (vecY / dist) * 0.3D;
|
||||
double normZ = (vecZ / dist) * 0.3D;
|
||||
|
||||
float strength = this.size * (0.7F + (random.nextFloat() * 0.6F));
|
||||
|
||||
double stepX = this.posX;
|
||||
double stepY = this.posY;
|
||||
double stepZ = this.posZ;
|
||||
|
||||
int prevX = Integer.MIN_VALUE;
|
||||
int prevY = Integer.MIN_VALUE;
|
||||
int prevZ = Integer.MIN_VALUE;
|
||||
|
||||
float prevResistance = 0.0F;
|
||||
|
||||
// Step through the ray until it is finally stopped
|
||||
while (strength > 0.0F) {
|
||||
int blockX = MathHelper.floor(stepX);
|
||||
int blockY = MathHelper.floor(stepY);
|
||||
int blockZ = MathHelper.floor(stepZ);
|
||||
|
||||
float resistance;
|
||||
|
||||
// Check whether or not we have actually moved into a new block this step. Due to how rays are stepped through,
|
||||
// over-sampling of the same block positions will occur. Changing this behaviour would introduce differences in
|
||||
// aliasing and sampling, which is unacceptable for our purposes. As a band-aid, we can simply re-use the
|
||||
// previous result and get a decent boost.
|
||||
if (prevX != blockX || prevY != blockY || prevZ != blockZ) {
|
||||
resistance = this.traverseBlock(strength, blockX, blockY, blockZ, touched);
|
||||
|
||||
prevX = blockX;
|
||||
prevY = blockY;
|
||||
prevZ = blockZ;
|
||||
|
||||
prevResistance = resistance;
|
||||
} else {
|
||||
resistance = prevResistance;
|
||||
}
|
||||
|
||||
strength -= resistance;
|
||||
// Apply a constant fall-off
|
||||
strength -= 0.22500001F;
|
||||
|
||||
stepX += normX;
|
||||
stepY += normY;
|
||||
stepZ += normZ;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Called for every step made by a ray being cast by an explosion.
|
||||
*
|
||||
* @param strength The strength of the ray during this step
|
||||
* @param blockX The x-coordinate of the block the ray is inside of
|
||||
* @param blockY The y-coordinate of the block the ray is inside of
|
||||
* @param blockZ The z-coordinate of the block the ray is inside of
|
||||
* @return The resistance of the current block space to the ray
|
||||
*/
|
||||
private float traverseBlock(float strength, int blockX, int blockY, int blockZ, LongOpenHashSet touched) {
|
||||
cachedPos.c(blockX, blockY, blockZ);
|
||||
IBlockData iblockdata = this.world.getType(cachedPos);
|
||||
|
||||
// Early-exit if the y-coordinate is out of bounds.
|
||||
if (cachedPos.isInvalidYLocation()) {
|
||||
if (iblockdata.getMaterial() != Material.AIR) {
|
||||
float blastResistance = this.source != null ? this.source.a((Explosion)(Object)this, this.world, cachedPos, iblockdata) : iblockdata.getBlock().a((Entity) null);
|
||||
return (blastResistance + 0.3F) * 0.3F;
|
||||
}
|
||||
return 0.0F;
|
||||
}
|
||||
|
||||
|
||||
int chunkX = blockX >> 4;
|
||||
int chunkZ = blockZ >> 4;
|
||||
|
||||
// Avoid calling into the chunk manager as much as possible through managing chunks locally
|
||||
if (this.prevChunkX != chunkX || this.prevChunkZ != chunkZ) {
|
||||
this.prevChunk = this.world.getChunkAt(chunkX, chunkZ);
|
||||
|
||||
this.prevChunkX = chunkX;
|
||||
this.prevChunkZ = chunkZ;
|
||||
}
|
||||
|
||||
final Chunk chunk = this.prevChunk;
|
||||
|
||||
float totalResistance = 0.0F;
|
||||
Optional<Float> blastResistance = Optional.empty();
|
||||
|
||||
// If the chunk is missing or out of bounds, assume that it is air
|
||||
if (chunk != null) {
|
||||
// We operate directly on chunk sections to avoid interacting with BlockPos and to squeeze out as much
|
||||
// performance as possible here
|
||||
ChunkSection section = chunk.getSections()[blockY >> 4];
|
||||
|
||||
// If the section doesn't exist or it's empty, assume that the block is air
|
||||
if (section != null && !section.a()) {
|
||||
// Retrieve the block state from the chunk section directly to avoid associated overhead
|
||||
IBlockData blockState = section.getType(blockX & 15, blockY & 15, blockZ & 15);
|
||||
|
||||
// If the block state is air, it cannot have fluid or any kind of resistance, so just leave
|
||||
if (blockState.getBlock() != Blocks.AIR) {
|
||||
// Get the explosion resistance like vanilla
|
||||
blastResistance = Optional.of(this.source != null ? this.source.a((Explosion)(Object)this, this.world, cachedPos, iblockdata) : iblockdata.getBlock().a((Entity) null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate how much this block will resist an explosion's ray
|
||||
if (blastResistance.isPresent()) {
|
||||
totalResistance = (blastResistance.get() + 0.3F) * 0.3F;
|
||||
}
|
||||
|
||||
// Check if this ray is still strong enough to break blocks, and if so, add this position to the set
|
||||
// of positions to destroy
|
||||
float reducedStrength = strength - totalResistance;
|
||||
if (reducedStrength > 0.0F && (this.a || iblockdata.getMaterial() != Material.AIR)) {
|
||||
if ((this.source == null || this.source.a((Explosion)(Object)this, this.world, cachedPos, iblockdata, reducedStrength)) && cachedPos.getY() < 256 && cachedPos.getY() >= 0) {
|
||||
touched.add(cachedPos.asLong());
|
||||
}
|
||||
}
|
||||
|
||||
return totalResistance;
|
||||
}
|
||||
@Overwrite
|
||||
public void a(boolean flag) {
|
||||
this.world.a((EntityHuman) null, this.posX, this.posY, this.posZ, SoundEffects.bV, SoundCategory.BLOCKS, 4.0F, (1.0F + (this.world.random.nextFloat() - this.world.random.nextFloat()) * 0.2F) * 0.7F);
|
||||
if (this.size >= 2.0F && this.b) {
|
||||
this.world.addParticle(EnumParticle.EXPLOSION_HUGE, this.posX, this.posY, this.posZ, 1.0D, 0.0D, 0.0D);
|
||||
} else {
|
||||
this.world.addParticle(EnumParticle.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 1.0D, 0.0D, 0.0D);
|
||||
}
|
||||
|
||||
Iterator<BlockPosition> iterator;
|
||||
BlockPosition blockposition;
|
||||
|
||||
if (this.b) {
|
||||
// CraftBukkit start
|
||||
org.bukkit.World bworld = this.world.getWorld();
|
||||
org.bukkit.entity.Entity explode = this.source == null ? null : this.source.getBukkitEntity();
|
||||
Location location = new Location(bworld, this.posX, this.posY, this.posZ);
|
||||
|
||||
List<org.bukkit.block.Block> blockList = Lists.newArrayList();
|
||||
for (int i1 = this.blocks.size() - 1; i1 >= 0; i1--) {
|
||||
BlockPosition cpos = (BlockPosition) this.blocks.get(i1);
|
||||
org.bukkit.block.Block bblock = bworld.getBlockAt(cpos.getX(), cpos.getY(), cpos.getZ());
|
||||
if (bblock.getType() != org.bukkit.Material.AIR) {
|
||||
blockList.add(bblock);
|
||||
}
|
||||
}
|
||||
|
||||
boolean cancelled;
|
||||
List<org.bukkit.block.Block> bukkitBlocks;
|
||||
float yield;
|
||||
|
||||
if (explode != null) {
|
||||
EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, 1.0F / this.size);
|
||||
this.world.getServer().getPluginManager().callEvent(event);
|
||||
cancelled = event.isCancelled();
|
||||
bukkitBlocks = event.blockList();
|
||||
yield = event.getYield();
|
||||
} else {
|
||||
BlockExplodeEvent event = new BlockExplodeEvent(location.getBlock(), blockList, 1.0F / this.size);
|
||||
this.world.getServer().getPluginManager().callEvent(event);
|
||||
cancelled = event.isCancelled();
|
||||
bukkitBlocks = event.blockList();
|
||||
yield = event.getYield();
|
||||
}
|
||||
|
||||
this.blocks.clear();
|
||||
|
||||
((ArrayList<BlockPosition>)this.blocks).ensureCapacity(bukkitBlocks.size());
|
||||
for (org.bukkit.block.Block bblock : bukkitBlocks) {
|
||||
BlockPosition coords = new BlockPosition(bblock.getX(), bblock.getY(), bblock.getZ());
|
||||
blocks.add(coords);
|
||||
}
|
||||
|
||||
if (cancelled) {
|
||||
this.wasCanceled = true;
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
iterator = this.blocks.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
blockposition = (BlockPosition) iterator.next();
|
||||
IBlockData iblockdata = this.world.getType(blockposition);
|
||||
Block block = iblockdata.getBlock();
|
||||
this.world.chunkPacketBlockController.updateNearbyBlocks(this.world, blockposition); // Paper - Anti-Xray
|
||||
|
||||
if (flag) {
|
||||
double d0 = (double) ((float) blockposition.getX() + this.world.random.nextFloat());
|
||||
double d1 = (double) ((float) blockposition.getY() + this.world.random.nextFloat());
|
||||
double d2 = (double) ((float) blockposition.getZ() + this.world.random.nextFloat());
|
||||
double d3 = d0 - this.posX;
|
||||
double d4 = d1 - this.posY;
|
||||
double d5 = d2 - this.posZ;
|
||||
double d6 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5);
|
||||
|
||||
d3 /= d6;
|
||||
d4 /= d6;
|
||||
d5 /= d6;
|
||||
double d7 = 0.5D / (d6 / (double) this.size + 0.1D);
|
||||
|
||||
d7 *= (double) (this.world.random.nextFloat() * this.world.random.nextFloat() + 0.3F);
|
||||
d3 *= d7;
|
||||
d4 *= d7;
|
||||
d5 *= d7;
|
||||
this.world.addParticle(EnumParticle.EXPLOSION_NORMAL, (d0 + this.posX) / 2.0D, (d1 + this.posY) / 2.0D, (d2 + this.posZ) / 2.0D, d3, d4, d5);
|
||||
this.world.addParticle(EnumParticle.SMOKE_NORMAL, d0, d1, d2, d3, d4, d5);
|
||||
}
|
||||
|
||||
if (iblockdata.getMaterial() != Material.AIR) {
|
||||
if (block.a((Explosion)(Object)this)) {
|
||||
// CraftBukkit - add yield
|
||||
block.dropNaturally(this.world, blockposition, this.world.getType(blockposition), yield, 0);
|
||||
}
|
||||
|
||||
this.world.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 3);
|
||||
block.wasExploded(this.world, blockposition, (Explosion)(Object)this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.a) {
|
||||
iterator = this.blocks.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
blockposition = (BlockPosition) iterator.next();
|
||||
if (this.world.getType(blockposition).getMaterial() == Material.AIR && this.world.getType(blockposition.down()).b() && this.c.nextInt(3) == 0) {
|
||||
// CraftBukkit start - Ignition by explosion
|
||||
if (!CraftEventFactory.callBlockIgniteEvent(this.world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), (Explosion)(Object)this).isCancelled()) {
|
||||
this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData());
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@Shadow protected abstract float getBlockDensity(Vec3D vec3d, AxisAlignedBB aabb);
|
||||
//private float getBlockDensity(Vec3D vec3d, AxisAlignedBB aabb) {
|
||||
//if (!this.world.paperConfig.optimizeExplosions) {
|
||||
// return this.world.a(vec3d, aabb);
|
||||
//}
|
||||
//CacheKey key = new CacheKey((Explosion)(Object)this, aabb);
|
||||
//return this.world.explosionDensityCache.computeIfAbsent(key, k1 -> this.world.a(vec3d, aabb));
|
||||
//}
|
||||
// Paper end
|
||||
// Dionysus end
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package io.akarin.server.mixin.optimization;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
||||
import net.minecraft.server.MathHelper;
|
||||
|
||||
@Mixin(value = MathHelper.class, remap = false)
|
||||
public abstract class MixinMathHelper {
|
||||
private static final int[] SINE_TABLE_INT = new int[16384 + 1];
|
||||
private static final float SINE_TABLE_MIDPOINT;
|
||||
@Overwrite
|
||||
public static float sin(float f) {
|
||||
return lookup((int) (f * 10430.38) & 0xFFFF);
|
||||
}
|
||||
@Overwrite
|
||||
public static float cos(float f) {
|
||||
return lookup((int) (f * 10430.38 + 16384.0) & 0xFFFF);
|
||||
}
|
||||
private static float lookup(int index) {
|
||||
if (index == 32768) {
|
||||
return SINE_TABLE_MIDPOINT;
|
||||
}
|
||||
int neg = (index & 0x8000) << 16;
|
||||
int mask = (index << 17) >> 31;
|
||||
int pos = (0x8001 & mask) + (index ^ mask);
|
||||
pos &= 0x7fff;
|
||||
return Float.intBitsToFloat(SINE_TABLE_INT[pos] ^ neg);
|
||||
}
|
||||
static {
|
||||
int i;
|
||||
|
||||
final float[] SINE_TABLE = new float[65536];
|
||||
for (i = 0; i < 65536; ++i) {
|
||||
SINE_TABLE[i] = (float) Math.sin((double) i * 3.141592653589793D * 2.0D / 65536.0D);
|
||||
}
|
||||
for (i = 0; i < SINE_TABLE_INT.length; i++) {
|
||||
SINE_TABLE_INT[i] = Float.floatToRawIntBits(SINE_TABLE[i]);
|
||||
}
|
||||
|
||||
SINE_TABLE_MIDPOINT = SINE_TABLE[SINE_TABLE.length / 2];
|
||||
for (i = 0; i < SINE_TABLE.length; i++) {
|
||||
float expected = SINE_TABLE[i];
|
||||
float value = lookup(i);
|
||||
|
||||
if (expected != value) {
|
||||
throw new IllegalArgumentException(String.format("LUT error at index %d (expected: %s, found: %s)", i, expected, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,24 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.destroystokyo.paper.PaperConfig;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongArraySet;
|
||||
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.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
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;
|
||||
|
||||
@@ -24,44 +27,35 @@ 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 LongArraySet unloadQueue = new LongArraySet(512); // Dionysus
|
||||
public final ChunkGenerator chunkGenerator;
|
||||
private final IChunkLoader chunkLoader;
|
||||
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
|
||||
// Paper start
|
||||
protected Chunk lastChunkByPos = null;
|
||||
public Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<Chunk>(8192) {
|
||||
|
||||
@Override
|
||||
public Chunk get(long key) {
|
||||
if (lastChunkByPos != null && key == lastChunkByPos.chunkKey) {
|
||||
return lastChunkByPos;
|
||||
}
|
||||
return lastChunkByPos = super.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk remove(long key) {
|
||||
if (lastChunkByPos != null && key == lastChunkByPos.chunkKey) {
|
||||
lastChunkByPos = null;
|
||||
}
|
||||
return super.remove(key);
|
||||
}
|
||||
}; // CraftBukkit
|
||||
// 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) {
|
||||
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() {
|
||||
@@ -69,9 +63,8 @@ public class ChunkProviderServer implements IChunkProvider {
|
||||
}
|
||||
|
||||
public void unload(Chunk chunk) {
|
||||
if (this.world.worldProvider.c(chunk.locX, chunk.locZ)) {
|
||||
this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)));
|
||||
chunk.d = true;
|
||||
if (this.world.worldProvider.a(chunk.locX, chunk.locZ)) {
|
||||
this.unloadQueue.add(ChunkCoordIntPair.a(chunk.locX, chunk.locZ));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -87,176 +80,206 @@ public class ChunkProviderServer implements IChunkProvider {
|
||||
|
||||
}
|
||||
|
||||
// Paper start
|
||||
public boolean isChunkGenerated(int x, int z) {
|
||||
return this.chunks.containsKey(ChunkCoordIntPair.asLong(x, z)) || this.chunkLoader.chunkExists(x, z);
|
||||
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 getLoadedChunkAt(int i, int j) {
|
||||
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 = (Chunk) this.chunks.get(k);
|
||||
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) {
|
||||
chunk.d = false;
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Chunk getOrLoadChunkAt(int i, int j) {
|
||||
Chunk chunk = this.getLoadedChunkAt(i, j);
|
||||
|
||||
if (chunk == null) {
|
||||
// CraftBukkit start
|
||||
ChunkRegionLoader loader = null;
|
||||
|
||||
if (this.chunkLoader instanceof ChunkRegionLoader) {
|
||||
loader = (ChunkRegionLoader) this.chunkLoader;
|
||||
}
|
||||
if (loader != null && loader.chunkExists(i, j)) {
|
||||
chunk = ChunkIOExecutor.syncChunkLoad(world, loader, this, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Chunk originalGetOrLoadChunkAt(int i, int j) {
|
||||
// CraftBukkit end
|
||||
Chunk chunk = this.getLoadedChunkAt(i, j);
|
||||
|
||||
if (chunk == null) {
|
||||
chunk = this.loadChunk(i, j);
|
||||
if (chunk != null) {
|
||||
this.chunks.put(ChunkCoordIntPair.a(i, j), chunk);
|
||||
chunk.addEntities();
|
||||
chunk.loadNearby(this, this.chunkGenerator, false); // CraftBukkit
|
||||
}
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
public Chunk getChunkIfLoaded(int x, int z) {
|
||||
return chunks.get(ChunkCoordIntPair.a(x, z));
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
public Chunk getChunkAt(int i, int j) {
|
||||
return getChunkAt(i, j, null);
|
||||
}
|
||||
|
||||
public Chunk getChunkAt(int i, int j, Runnable runnable) {
|
||||
return getChunkAt(i, j, runnable, true);
|
||||
}
|
||||
|
||||
public Chunk getChunkAt(int i, int j, Runnable runnable, boolean generate) {
|
||||
Chunk chunk = world.paperConfig.allowPermaChunkLoaders ? getLoadedChunkAt(i, j) : getChunkIfLoaded(i, j); // Paper - Configurable perma chunk loaders
|
||||
ChunkRegionLoader loader = null;
|
||||
|
||||
if (this.chunkLoader instanceof ChunkRegionLoader) {
|
||||
loader = (ChunkRegionLoader) this.chunkLoader;
|
||||
|
||||
}
|
||||
// We can only use the queue for already generated chunks
|
||||
if (chunk == null && loader != null && loader.chunkExists(i, j)) {
|
||||
if (runnable != null) {
|
||||
ChunkIOExecutor.queueChunkLoad(world, loader, this, i, j, runnable);
|
||||
return null;
|
||||
} else {
|
||||
chunk = ChunkIOExecutor.syncChunkLoad(world, loader, this, i, j);
|
||||
|
||||
// Paper start - If there was an issue loading the chunk from region, stage1 will fail and stage2 will load it sync
|
||||
// all we need to do is fetch an instance
|
||||
if (chunk == null) {
|
||||
chunk = getChunkIfLoaded(i, j);
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
} else if (chunk == null && generate) {
|
||||
chunk = originalGetChunkAt(i, j);
|
||||
}
|
||||
|
||||
// If we didn't load the chunk async and have a callback run it now
|
||||
if (runnable != null) {
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public Chunk originalGetChunkAt(int i, int j) {
|
||||
Chunk chunk = this.originalGetOrLoadChunkAt(i, j);
|
||||
// CraftBukkit end
|
||||
|
||||
if (chunk == null) {
|
||||
world.timings.syncChunkLoadTimer.startTiming(); // Spigot
|
||||
long k = ChunkCoordIntPair.a(i, j);
|
||||
|
||||
try {
|
||||
chunk = this.chunkGenerator.getOrCreateChunk(i, j);
|
||||
} catch (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(k));
|
||||
crashreportsystemdetails.a("Generator", (Object) this.chunkGenerator);
|
||||
throw new ReportedException(crashreport);
|
||||
}
|
||||
|
||||
this.chunks.put(k, chunk);
|
||||
chunk.addEntities();
|
||||
chunk.loadNearby(this, this.chunkGenerator, true); // CraftBukkit
|
||||
world.timings.syncChunkLoadTimer.stopTiming(); // Spigot
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Chunk loadChunk(int i, int j) {
|
||||
try {
|
||||
Chunk chunk = this.chunkLoader.a(this.world, i, j);
|
||||
|
||||
if (chunk != null) {
|
||||
chunk.setLastSaved(this.world.getTime());
|
||||
this.chunkGenerator.recreateStructures(chunk, i, j);
|
||||
}
|
||||
|
||||
this.asyncTaskHandler.postToMainThread(chunk::addEntities);
|
||||
return chunk;
|
||||
} catch (Exception exception) {
|
||||
// Paper start
|
||||
String msg = "Couldn\'t load chunk";
|
||||
ChunkProviderServer.a.error(msg, exception);
|
||||
ServerInternalException.reportInternalException(exception);
|
||||
// Paper end
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
public void saveChunkNOP(Chunk chunk) {
|
||||
// CraftBukkit start
|
||||
public Chunk generateChunk(int x, int z) {
|
||||
try {
|
||||
// this.chunkLoader.b(this.world, chunk); // Spigot
|
||||
} catch (Exception exception) {
|
||||
// Paper start
|
||||
String msg = "Couldn\'t save entities";
|
||||
ChunkProviderServer.a.error(msg, exception);
|
||||
ServerInternalException.reportInternalException(exception);
|
||||
// Paper end
|
||||
}
|
||||
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 void saveChunk(Chunk chunk, boolean unloaded) { // Spigot
|
||||
try (co.aikar.timings.Timing timed = world.timings.chunkSaveData.startTiming()) {
|
||||
chunk.setLastSaved(this.world.getTime());
|
||||
this.chunkLoader.saveChunk(this.world, chunk, unloaded); // Spigot
|
||||
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";
|
||||
@@ -273,68 +296,83 @@ public class ChunkProviderServer implements IChunkProvider {
|
||||
public boolean a(boolean flag) {
|
||||
int i = 0;
|
||||
|
||||
// CraftBukkit start
|
||||
// Paper start
|
||||
final ChunkRegionLoader chunkLoader = (ChunkRegionLoader) world.getChunkProviderServer().chunkLoader;
|
||||
final int queueSize = chunkLoader.getQueueSize();
|
||||
this.chunkScheduler.a(() -> {
|
||||
return true;
|
||||
});
|
||||
IChunkLoader ichunkloader = this.chunkLoader;
|
||||
|
||||
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;
|
||||
synchronized (this.chunkLoader) {
|
||||
ObjectIterator objectiterator = this.chunks.values().iterator();
|
||||
|
||||
final long processedSaves = chunkLoader.getProcessedSaves();
|
||||
long processedDiff = processedSaves - lastProcessedSaves;
|
||||
lastProcessedSaves = processedSaves;
|
||||
// Paper start
|
||||
final ChunkRegionLoader chunkLoader = (ChunkRegionLoader) world.getChunkProviderServer().chunkLoader;
|
||||
final int queueSize = chunkLoader.getQueueSize();
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
if (queueSize > world.paperConfig.queueSizeAutoSaveThreshold){
|
||||
return false;
|
||||
}
|
||||
final int autoSaveLimit = world.paperConfig.maxAutoSaveChunksPerTick;
|
||||
// Paper end
|
||||
Iterator iterator = this.chunks.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Chunk chunk = (Chunk) iterator.next();
|
||||
// CraftBukkit end
|
||||
final long processedSaves = chunkLoader.getProcessedSaves();
|
||||
long processedDiff = processedSaves - lastProcessedSaves;
|
||||
lastProcessedSaves = processedSaves;
|
||||
|
||||
if (flag) {
|
||||
this.saveChunkNOP(chunk);
|
||||
}
|
||||
|
||||
if (chunk.a(flag)) {
|
||||
this.saveChunk(chunk, false); // Spigot
|
||||
chunk.f(false);
|
||||
++i;
|
||||
if (!flag && i >= autoSaveLimit) { // Spigot - // Paper - Incremental Auto Save - cap max per tick
|
||||
return false;
|
||||
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
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void c() {
|
||||
this.chunkLoader.c();
|
||||
IChunkLoader ichunkloader = this.chunkLoader;
|
||||
|
||||
synchronized (this.chunkLoader) {
|
||||
this.chunkLoader.b();
|
||||
}
|
||||
}
|
||||
|
||||
private static final double UNLOAD_QUEUE_RESIZE_FACTOR = 0.96;
|
||||
private static final double UNLOAD_QUEUE_RESIZE_FACTOR = 0.96; // Spigot
|
||||
|
||||
public boolean unloadChunks() {
|
||||
public boolean unloadChunks(BooleanSupplier booleansupplier) {
|
||||
if (!this.world.savingDisabled) {
|
||||
if (!this.unloadQueue.isEmpty()) {
|
||||
// Spigot start
|
||||
@@ -343,29 +381,31 @@ public class ChunkProviderServer implements IChunkProvider {
|
||||
int targetSize = Math.min(this.unloadQueue.size() - 100, (int) (this.unloadQueue.size() * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
|
||||
// Spigot end
|
||||
|
||||
LongIterator iterator = this.unloadQueue.iterator();
|
||||
LongIterator longiterator = this.unloadQueue.iterator();
|
||||
|
||||
while (iterator.hasNext()) { // Spigot
|
||||
Long chunkKey = iterator.nextLong();
|
||||
iterator.remove(); // Spigot
|
||||
Chunk chunk = (Chunk) this.chunks.get(chunkKey);
|
||||
while (longiterator.hasNext()) { // Spigot
|
||||
Long olong = (Long) longiterator.next();
|
||||
longiterator.remove(); // Spigot
|
||||
IChunkLoader ichunkloader = this.chunkLoader;
|
||||
|
||||
if (chunk != null && chunk.d) {
|
||||
// CraftBukkit start - move unload logic to own method
|
||||
chunk.setShouldUnload(false); // Paper
|
||||
if (!unloadChunk(chunk, true)) {
|
||||
continue;
|
||||
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
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
// Spigot start
|
||||
if (this.unloadQueue.size() <= targetSize && activityAccountant.activityTimeIsExhausted()) {
|
||||
break;
|
||||
}
|
||||
// Spigot end
|
||||
}
|
||||
}
|
||||
|
||||
activityAccountant.endActivity(); // Spigot
|
||||
}
|
||||
// Paper start - delayed chunk unloads
|
||||
@@ -382,7 +422,7 @@ public class ChunkProviderServer implements IChunkProvider {
|
||||
}
|
||||
// Paper end
|
||||
|
||||
this.chunkLoader.b();
|
||||
this.chunkScheduler.a(booleansupplier);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -405,7 +445,7 @@ public class ChunkProviderServer implements IChunkProvider {
|
||||
continue;
|
||||
}
|
||||
|
||||
Chunk neighbor = this.getChunkIfLoaded(chunk.locX + x, chunk.locZ + z);
|
||||
Chunk neighbor = this.chunks.get(chunk.chunkKey); // Paper
|
||||
if (neighbor != null) {
|
||||
neighbor.setNeighborUnloaded(-x, -z);
|
||||
chunk.setNeighborUnloaded(x, z);
|
||||
@@ -413,17 +453,19 @@ public class ChunkProviderServer implements IChunkProvider {
|
||||
}
|
||||
}
|
||||
// Moved from unloadChunks above
|
||||
chunk.removeEntities();
|
||||
if (save) {
|
||||
this.saveChunk(chunk, true); // Spigot
|
||||
this.saveChunkNOP(chunk);
|
||||
synchronized (this.chunkLoader) {
|
||||
chunk.removeEntities();
|
||||
if (save) {
|
||||
this.saveChunk(chunk, true); // Spigot
|
||||
}
|
||||
this.chunks.remove(chunk.chunkKey);
|
||||
// this.lastChunk = null; // Paper
|
||||
}
|
||||
this.chunks.remove(chunk.chunkKey);
|
||||
return true;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
public boolean e() {
|
||||
public boolean d() {
|
||||
return !this.world.savingDisabled;
|
||||
}
|
||||
|
||||
@@ -435,13 +477,17 @@ public class ChunkProviderServer implements IChunkProvider {
|
||||
return this.chunkGenerator.getMobsFor(enumcreaturetype, blockposition);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BlockPosition a(World world, String s, BlockPosition blockposition, boolean flag) {
|
||||
return this.chunkGenerator.findNearestMapFeature(world, s, blockposition, flag);
|
||||
public int a(World world, boolean flag, boolean flag1) {
|
||||
return this.chunkGenerator.a(world, flag, flag1);
|
||||
}
|
||||
|
||||
public boolean a(World world, String s, BlockPosition blockposition) {
|
||||
return this.chunkGenerator.a(world, s, blockposition);
|
||||
@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() {
|
||||
@@ -451,8 +497,4 @@ public class ChunkProviderServer implements IChunkProvider {
|
||||
public boolean isLoaded(int i, int j) {
|
||||
return this.chunks.containsKey(ChunkCoordIntPair.a(i, j));
|
||||
}
|
||||
|
||||
public boolean e(int i, int j) {
|
||||
return this.chunks.containsKey(ChunkCoordIntPair.a(i, j)) || this.chunkLoader.chunkExists(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,642 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
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);
|
||||
}
|
||||
|
||||
// Paper start
|
||||
private static final int CURRENT_DATA_VERSION = 1343; // Paper
|
||||
private static final boolean JUST_CORRUPT_IT = Boolean.valueOf("Paper.ignoreWorldDataVersion");
|
||||
// Paper end
|
||||
|
||||
@Nullable
|
||||
protected Object[] a(World world, int i, int j, NBTTagCompound nbttagcompound) { // CraftBukkit - return Chunk -> Object[]
|
||||
// Paper start - Do NOT attempt to load chunks saved with newer versions
|
||||
if (nbttagcompound.hasKeyOfType("DataVersion", 3)) {
|
||||
int dataVersion = nbttagcompound.getInt("DataVersion");
|
||||
if (!JUST_CORRUPT_IT && dataVersion > CURRENT_DATA_VERSION) {
|
||||
new RuntimeException("Server attempted to load chunk saved with newer version of minecraft! " + dataVersion + " > " + CURRENT_DATA_VERSION).printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
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
|
||||
}
|
||||
@@ -1,868 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
// CraftBukkit start
|
||||
import java.util.List;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.TreeType;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.event.block.BlockDispenseEvent;
|
||||
import org.bukkit.event.world.StructureGrowEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class DispenserRegistry {
|
||||
|
||||
public static final PrintStream a = System.out;
|
||||
private static boolean c;
|
||||
public static boolean b;
|
||||
private static final Logger d = LogManager.getLogger();
|
||||
|
||||
public static boolean a() {
|
||||
return DispenserRegistry.c;
|
||||
}
|
||||
|
||||
static void b() {
|
||||
BlockDispenser.REGISTRY.a(Items.ARROW, new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
|
||||
EntityTippedArrow entitytippedarrow = new EntityTippedArrow(world, iposition.getX(), iposition.getY(), iposition.getZ());
|
||||
|
||||
entitytippedarrow.fromPlayer = EntityArrow.PickupStatus.ALLOWED;
|
||||
return entitytippedarrow;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.TIPPED_ARROW, new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
|
||||
EntityTippedArrow entitytippedarrow = new EntityTippedArrow(world, iposition.getX(), iposition.getY(), iposition.getZ());
|
||||
|
||||
entitytippedarrow.a(itemstack);
|
||||
entitytippedarrow.fromPlayer = EntityArrow.PickupStatus.ALLOWED;
|
||||
return entitytippedarrow;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.SPECTRAL_ARROW, new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
|
||||
EntitySpectralArrow entityspectralarrow = new EntitySpectralArrow(world, iposition.getX(), iposition.getY(), iposition.getZ());
|
||||
|
||||
entityspectralarrow.fromPlayer = EntityArrow.PickupStatus.ALLOWED;
|
||||
return entityspectralarrow;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.EGG, new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
|
||||
return new EntityEgg(world, iposition.getX(), iposition.getY(), iposition.getZ());
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.SNOWBALL, new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
|
||||
return new EntitySnowball(world, iposition.getX(), iposition.getY(), iposition.getZ());
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.EXPERIENCE_BOTTLE, new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
|
||||
return new EntityThrownExpBottle(world, iposition.getX(), iposition.getY(), iposition.getZ());
|
||||
}
|
||||
|
||||
protected float a() {
|
||||
return super.a() * 0.5F;
|
||||
}
|
||||
|
||||
protected float getPower() {
|
||||
return super.getPower() * 1.25F;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.SPLASH_POTION, new IDispenseBehavior() {
|
||||
public ItemStack a(ISourceBlock isourceblock, final ItemStack itemstack) {
|
||||
return (new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack1) { // CraftBukkit - decompile issue
|
||||
return new EntityPotion(world, iposition.getX(), iposition.getY(), iposition.getZ(), itemstack1.cloneItemStack());
|
||||
}
|
||||
|
||||
protected float a() {
|
||||
return super.a() * 0.5F;
|
||||
}
|
||||
|
||||
protected float getPower() {
|
||||
return super.getPower() * 1.25F;
|
||||
}
|
||||
}).a(isourceblock, itemstack);
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.LINGERING_POTION, new IDispenseBehavior() {
|
||||
public ItemStack a(ISourceBlock isourceblock, final ItemStack itemstack) {
|
||||
return (new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack1) { // CraftBukkit - decompile issue
|
||||
return new EntityPotion(world, iposition.getX(), iposition.getY(), iposition.getZ(), itemstack1.cloneItemStack());
|
||||
}
|
||||
|
||||
protected float a() {
|
||||
return super.a() * 0.5F;
|
||||
}
|
||||
|
||||
protected float getPower() {
|
||||
return super.getPower() * 1.25F;
|
||||
}
|
||||
}).a(isourceblock, itemstack);
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.SPAWN_EGG, new DispenseBehaviorItem() {
|
||||
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
double d0 = isourceblock.getX() + (double) enumdirection.getAdjacentX();
|
||||
double d1 = (double) ((float) (isourceblock.getBlockPosition().getY() + enumdirection.getAdjacentY()) + 0.2F);
|
||||
double d2 = isourceblock.getZ() + (double) enumdirection.getAdjacentZ();
|
||||
// Entity entity = ItemMonsterEgg.a(isourceblock.getWorld(), ItemMonsterEgg.h(itemstack), d0, d1, d2);
|
||||
|
||||
// CraftBukkit start
|
||||
World world = isourceblock.getWorld();
|
||||
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1, d2));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
itemstack.add(1);
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
itemstack.add(1);
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
itemstack1 = CraftItemStack.asNMSCopy(event.getItem());
|
||||
|
||||
Entity entity = ItemMonsterEgg.spawnCreature(isourceblock.getWorld(), ItemMonsterEgg.h(itemstack), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DISPENSE_EGG);
|
||||
|
||||
if (entity instanceof EntityLiving && itemstack.hasName()) {
|
||||
entity.setCustomName(itemstack.getName());
|
||||
}
|
||||
|
||||
ItemMonsterEgg.a(isourceblock.getWorld(), (EntityHuman) null, itemstack, entity);
|
||||
// itemstack.subtract(1);// Handled during event processing
|
||||
// CraftBukkit end
|
||||
return itemstack;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.FIREWORKS, new DispenseBehaviorItem() {
|
||||
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
double d0 = isourceblock.getX() + (double) enumdirection.getAdjacentX();
|
||||
double d1 = (double) ((float) isourceblock.getBlockPosition().getY() + 0.2F);
|
||||
double d2 = isourceblock.getZ() + (double) enumdirection.getAdjacentZ();
|
||||
// CraftBukkit start
|
||||
World world = isourceblock.getWorld();
|
||||
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1, d2));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
itemstack.add(1);
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
itemstack.add(1);
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
itemstack1 = CraftItemStack.asNMSCopy(event.getItem());
|
||||
EntityFireworks entityfireworks = new EntityFireworks(isourceblock.getWorld(), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), itemstack1);
|
||||
|
||||
isourceblock.getWorld().addEntity(entityfireworks);
|
||||
// itemstack.subtract(1); // Handled during event processing
|
||||
// CraftBukkit end
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
protected void a(ISourceBlock isourceblock) {
|
||||
isourceblock.getWorld().triggerEffect(1004, isourceblock.getBlockPosition(), 0);
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.FIRE_CHARGE, new DispenseBehaviorItem() {
|
||||
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
IPosition iposition = BlockDispenser.a(isourceblock);
|
||||
double d0 = iposition.getX() + (double) ((float) enumdirection.getAdjacentX() * 0.3F);
|
||||
double d1 = iposition.getY() + (double) ((float) enumdirection.getAdjacentY() * 0.3F);
|
||||
double d2 = iposition.getZ() + (double) ((float) enumdirection.getAdjacentZ() * 0.3F);
|
||||
World world = isourceblock.getWorld();
|
||||
Random random = world.random;
|
||||
double d3 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentX();
|
||||
double d4 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentY();
|
||||
double d5 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentZ();
|
||||
|
||||
// CraftBukkit start
|
||||
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d3, d4, d5));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
itemstack.add(1);
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
itemstack.add(1);
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
EntitySmallFireball fireball = new EntitySmallFireball(world, d0, d1, d2, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ());
|
||||
fireball.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource((TileEntityDispenser) isourceblock.getTileEntity());
|
||||
|
||||
world.addEntity(fireball);
|
||||
// itemstack.subtract(1); // Handled during event processing
|
||||
// CraftBukkit end
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
protected void a(ISourceBlock isourceblock) {
|
||||
isourceblock.getWorld().triggerEffect(1018, isourceblock.getBlockPosition(), 0);
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.aH, new DispenserRegistry.a(EntityBoat.EnumBoatType.OAK));
|
||||
BlockDispenser.REGISTRY.a(Items.aI, new DispenserRegistry.a(EntityBoat.EnumBoatType.SPRUCE));
|
||||
BlockDispenser.REGISTRY.a(Items.aJ, new DispenserRegistry.a(EntityBoat.EnumBoatType.BIRCH));
|
||||
BlockDispenser.REGISTRY.a(Items.aK, new DispenserRegistry.a(EntityBoat.EnumBoatType.JUNGLE));
|
||||
BlockDispenser.REGISTRY.a(Items.aM, new DispenserRegistry.a(EntityBoat.EnumBoatType.DARK_OAK));
|
||||
BlockDispenser.REGISTRY.a(Items.aL, new DispenserRegistry.a(EntityBoat.EnumBoatType.ACACIA));
|
||||
DispenseBehaviorItem dispensebehavioritem = new DispenseBehaviorItem() {
|
||||
private final DispenseBehaviorItem b = new DispenseBehaviorItem();
|
||||
|
||||
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
ItemBucket itembucket = (ItemBucket) itemstack.getItem();
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
|
||||
|
||||
// CraftBukkit start
|
||||
World world = isourceblock.getWorld();
|
||||
int x = blockposition.getX();
|
||||
int y = blockposition.getY();
|
||||
int z = blockposition.getZ();
|
||||
if (world.isEmpty(blockposition) || !world.getType(blockposition).getMaterial().isBuildable()) {
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
itembucket = (ItemBucket) CraftItemStack.asNMSCopy(event.getItem()).getItem();
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (itembucket.a((EntityHuman) null, isourceblock.getWorld(), blockposition)) {
|
||||
// CraftBukkit start - Handle stacked buckets
|
||||
Item item = Items.BUCKET;
|
||||
itemstack.subtract(1);
|
||||
if (itemstack.isEmpty()) {
|
||||
itemstack.setItem(Items.BUCKET);
|
||||
itemstack.setCount(1);
|
||||
} else if (((TileEntityDispenser) isourceblock.getTileEntity()).addItem(new ItemStack(item)) < 0) {
|
||||
this.b.a(isourceblock, new ItemStack(item));
|
||||
}
|
||||
// CraftBukkit end
|
||||
return itemstack;
|
||||
} else {
|
||||
return this.b.a(isourceblock, itemstack);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BlockDispenser.REGISTRY.a(Items.LAVA_BUCKET, dispensebehavioritem);
|
||||
BlockDispenser.REGISTRY.a(Items.WATER_BUCKET, dispensebehavioritem);
|
||||
BlockDispenser.REGISTRY.a(Items.BUCKET, new DispenseBehaviorItem() {
|
||||
private final DispenseBehaviorItem b = new DispenseBehaviorItem();
|
||||
|
||||
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
World world = isourceblock.getWorld();
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
|
||||
IBlockData iblockdata = world.getType(blockposition);
|
||||
Block block = iblockdata.getBlock();
|
||||
Material material = iblockdata.getMaterial();
|
||||
Item item;
|
||||
|
||||
if (Material.WATER.equals(material) && block instanceof BlockFluids && ((Integer) iblockdata.get(BlockFluids.LEVEL)).intValue() == 0) {
|
||||
item = Items.WATER_BUCKET;
|
||||
} else {
|
||||
if (!Material.LAVA.equals(material) || !(block instanceof BlockFluids) || ((Integer) iblockdata.get(BlockFluids.LEVEL)).intValue() != 0) {
|
||||
return super.b(isourceblock, itemstack);
|
||||
}
|
||||
|
||||
item = Items.LAVA_BUCKET;
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
world.setAir(blockposition);
|
||||
itemstack.subtract(1);
|
||||
if (itemstack.isEmpty()) {
|
||||
return new ItemStack(item);
|
||||
} else {
|
||||
if (((TileEntityDispenser) isourceblock.getTileEntity()).addItem(new ItemStack(item)) < 0) {
|
||||
this.b.a(isourceblock, new ItemStack(item));
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.FLINT_AND_STEEL, new DispenserRegistry.b() {
|
||||
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
World world = isourceblock.getWorld();
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
this.b = true;
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
|
||||
|
||||
if (world.isEmpty(blockposition)) {
|
||||
// CraftBukkit start - Ignition by dispensing flint and steel
|
||||
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()).isCancelled()) {
|
||||
world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData());
|
||||
if (itemstack.isDamaged(1, world.random, (EntityPlayer) null)) {
|
||||
itemstack.setCount(0);
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
} else if (world.getType(blockposition).getBlock() == Blocks.TNT) {
|
||||
Blocks.TNT.postBreak(world, blockposition, Blocks.TNT.getBlockData().set(BlockTNT.EXPLODE, Boolean.valueOf(true)));
|
||||
world.setAir(blockposition);
|
||||
} else {
|
||||
this.b = false;
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.DYE, new DispenserRegistry.b() {
|
||||
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
this.b = true;
|
||||
if (EnumColor.WHITE == EnumColor.fromInvColorIndex(itemstack.getData())) {
|
||||
World world = isourceblock.getWorld();
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
world.captureTreeGeneration = true;
|
||||
// CraftBukkit end
|
||||
|
||||
if (ItemDye.a(itemstack, world, blockposition)) {
|
||||
if (!world.isClientSide) {
|
||||
world.triggerEffect(2005, blockposition, 0);
|
||||
}
|
||||
} else {
|
||||
this.b = false;
|
||||
}
|
||||
// CraftBukkit start
|
||||
world.captureTreeGeneration = false;
|
||||
if (world.capturedBlockStates.size() > 0) {
|
||||
TreeType treeType = BlockSapling.treeType;
|
||||
BlockSapling.treeType = null;
|
||||
Location location = new Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
List<org.bukkit.block.BlockState> blocks = (List<org.bukkit.block.BlockState>) world.capturedBlockStates.clone();
|
||||
world.capturedBlockStates.clear();
|
||||
StructureGrowEvent structureEvent = null;
|
||||
if (treeType != null) {
|
||||
structureEvent = new StructureGrowEvent(location, treeType, false, null, blocks);
|
||||
org.bukkit.Bukkit.getPluginManager().callEvent(structureEvent);
|
||||
}
|
||||
if (structureEvent == null || !structureEvent.isCancelled()) {
|
||||
for (org.bukkit.block.BlockState blockstate : blocks) {
|
||||
blockstate.update(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
return itemstack;
|
||||
} else {
|
||||
return super.b(isourceblock, itemstack);
|
||||
}
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Item.getItemOf(Blocks.TNT), new DispenseBehaviorItem() {
|
||||
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
World world = isourceblock.getWorld();
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
|
||||
// EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, (EntityLiving) null);
|
||||
|
||||
// CraftBukkit start
|
||||
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
itemstack.add(1);
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
itemstack.add(1);
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), (EntityLiving) null);
|
||||
// CraftBukkit end
|
||||
|
||||
world.addEntity(entitytntprimed);
|
||||
world.a((EntityHuman) null, entitytntprimed.locX, entitytntprimed.locY, entitytntprimed.locZ, SoundEffects.hW, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
// itemstack.subtract(1); // CraftBukkit - handled above
|
||||
return itemstack;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.SKULL, new DispenserRegistry.b() {
|
||||
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
World world = isourceblock.getWorld();
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection);
|
||||
BlockSkull blockskull = Blocks.SKULL;
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
this.b = true;
|
||||
if (world.isEmpty(blockposition) && blockskull.b(world, blockposition, itemstack)) {
|
||||
if (!world.isClientSide) {
|
||||
world.setTypeAndData(blockposition, blockskull.getBlockData().set(BlockSkull.FACING, EnumDirection.UP), 3);
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntitySkull) {
|
||||
if (itemstack.getData() == 3) {
|
||||
GameProfile gameprofile = null;
|
||||
|
||||
if (itemstack.hasTag()) {
|
||||
NBTTagCompound nbttagcompound = itemstack.getTag();
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("SkullOwner", 10)) {
|
||||
gameprofile = GameProfileSerializer.deserialize(nbttagcompound.getCompound("SkullOwner"));
|
||||
} else if (nbttagcompound.hasKeyOfType("SkullOwner", 8)) {
|
||||
String s = nbttagcompound.getString("SkullOwner");
|
||||
|
||||
if (!UtilColor.b(s)) {
|
||||
gameprofile = new GameProfile((UUID) null, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
((TileEntitySkull) tileentity).setGameProfile(gameprofile);
|
||||
} else {
|
||||
((TileEntitySkull) tileentity).setSkullType(itemstack.getData());
|
||||
}
|
||||
|
||||
((TileEntitySkull) tileentity).setRotation(enumdirection.opposite().get2DRotationValue() * 4);
|
||||
Blocks.SKULL.a(world, blockposition, (TileEntitySkull) tileentity);
|
||||
}
|
||||
|
||||
itemstack.subtract(1);
|
||||
}
|
||||
} else if (ItemArmor.a(isourceblock, itemstack).isEmpty()) {
|
||||
this.b = false;
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Item.getItemOf(Blocks.PUMPKIN), new DispenserRegistry.b() {
|
||||
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
World world = isourceblock.getWorld();
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
|
||||
BlockPumpkin blockpumpkin = (BlockPumpkin) Blocks.PUMPKIN;
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
this.b = true;
|
||||
if (world.isEmpty(blockposition) && blockpumpkin.b(world, blockposition)) {
|
||||
if (!world.isClientSide) {
|
||||
world.setTypeAndData(blockposition, blockpumpkin.getBlockData(), 3);
|
||||
}
|
||||
|
||||
itemstack.subtract(1);
|
||||
} else {
|
||||
ItemStack itemstack1 = ItemArmor.a(isourceblock, itemstack);
|
||||
|
||||
if (itemstack1.isEmpty()) {
|
||||
this.b = false;
|
||||
}
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
});
|
||||
EnumColor[] aenumcolor = EnumColor.values();
|
||||
int i = aenumcolor.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumColor enumcolor = aenumcolor[j];
|
||||
|
||||
BlockDispenser.REGISTRY.a(Item.getItemOf(BlockShulkerBox.a(enumcolor)), new DispenserRegistry.c(null));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void c() {
|
||||
if (!DispenserRegistry.c) {
|
||||
DispenserRegistry.c = true;
|
||||
d();
|
||||
SoundEffect.b();
|
||||
Block.w();
|
||||
BlockFire.e();
|
||||
MobEffectList.k();
|
||||
Enchantment.g();
|
||||
Item.t();
|
||||
PotionRegistry.b();
|
||||
PotionBrewer.a();
|
||||
EntityTypes.c();
|
||||
BiomeBase.q();
|
||||
b();
|
||||
if (!CraftingManager.init()) {
|
||||
DispenserRegistry.b = true;
|
||||
DispenserRegistry.d.error("Errors with built-in recipes!");
|
||||
}
|
||||
|
||||
StatisticList.a();
|
||||
if (DispenserRegistry.d.isDebugEnabled()) {
|
||||
if ((new AdvancementDataWorld((File) null)).b()) {
|
||||
DispenserRegistry.b = true;
|
||||
DispenserRegistry.d.error("Errors with built-in advancements!");
|
||||
}
|
||||
|
||||
if (!LootTables.b()) {
|
||||
DispenserRegistry.b = true;
|
||||
DispenserRegistry.d.error("Errors with built-in loot tables");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void d() {
|
||||
if (DispenserRegistry.d.isDebugEnabled()) {
|
||||
System.setErr(new DebugOutputStream("STDERR", System.err));
|
||||
System.setOut(new DebugOutputStream("STDOUT", DispenserRegistry.a));
|
||||
} else {
|
||||
System.setErr(new RedirectStream("STDERR", System.err));
|
||||
System.setOut(new RedirectStream("STDOUT", DispenserRegistry.a));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class c extends DispenserRegistry.b {
|
||||
|
||||
private c() {}
|
||||
|
||||
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
Block block = Block.asBlock(itemstack.getItem());
|
||||
World world = isourceblock.getWorld();
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection);
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
this.b = world.a(block, blockposition, false, EnumDirection.DOWN, (Entity) null);
|
||||
if (this.b) {
|
||||
EnumDirection enumdirection1 = world.isEmpty(blockposition.down()) ? enumdirection : EnumDirection.UP;
|
||||
IBlockData iblockdata = block.getBlockData().set(BlockShulkerBox.a, enumdirection1);
|
||||
// Dionysus start - fix Dispenser crashes
|
||||
boolean wasPlaced = world.setTypeUpdate(blockposition, iblockdata);
|
||||
if (!wasPlaced) {
|
||||
return itemstack;
|
||||
}
|
||||
// Dionysus end
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
|
||||
|
||||
if (itemstack1.hasTag()) {
|
||||
((TileEntityShulkerBox) tileentity).e(itemstack1.getTag().getCompound("BlockEntityTag"));
|
||||
}
|
||||
|
||||
if (itemstack1.hasName()) {
|
||||
((TileEntityShulkerBox) tileentity).setCustomName(itemstack1.getName());
|
||||
}
|
||||
|
||||
world.updateAdjacentComparators(blockposition, iblockdata.getBlock());
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
c(Object object) {
|
||||
this();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class b extends DispenseBehaviorItem {
|
||||
|
||||
protected boolean b = true;
|
||||
|
||||
public b() {}
|
||||
|
||||
protected void a(ISourceBlock isourceblock) {
|
||||
isourceblock.getWorld().triggerEffect(this.b ? 1000 : 1001, isourceblock.getBlockPosition(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static class a extends DispenseBehaviorItem {
|
||||
|
||||
private final DispenseBehaviorItem b = new DispenseBehaviorItem();
|
||||
private final EntityBoat.EnumBoatType c;
|
||||
|
||||
public a(EntityBoat.EnumBoatType entityboat_enumboattype) {
|
||||
this.c = entityboat_enumboattype;
|
||||
}
|
||||
|
||||
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
World world = isourceblock.getWorld();
|
||||
double d0 = isourceblock.getX() + (double) ((float) enumdirection.getAdjacentX() * 1.125F);
|
||||
double d1 = isourceblock.getY() + (double) ((float) enumdirection.getAdjacentY() * 1.125F);
|
||||
double d2 = isourceblock.getZ() + (double) ((float) enumdirection.getAdjacentZ() * 1.125F);
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection);
|
||||
Material material = world.getType(blockposition).getMaterial();
|
||||
double d3;
|
||||
|
||||
if (Material.WATER.equals(material)) {
|
||||
d3 = 1.0D;
|
||||
} else {
|
||||
if (!Material.AIR.equals(material) || !Material.WATER.equals(world.getType(blockposition.down()).getMaterial())) {
|
||||
return this.b.a(isourceblock, itemstack);
|
||||
}
|
||||
|
||||
d3 = 0.0D;
|
||||
}
|
||||
|
||||
// EntityBoat entityboat = new EntityBoat(world, d0, d1 + d3, d2);
|
||||
// CraftBukkit start
|
||||
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1 + d3, d2));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
itemstack.add(1);
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
itemstack.add(1);
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
EntityBoat entityboat = new EntityBoat(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ());
|
||||
// CraftBukkit end
|
||||
|
||||
entityboat.setType(this.c);
|
||||
entityboat.yaw = enumdirection.l();
|
||||
if (!world.addEntity(entityboat)) itemstack.add(1); // CraftBukkit
|
||||
// itemstack.subtract(1); // CraftBukkit - handled during event processing
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
protected void a(ISourceBlock isourceblock) {
|
||||
isourceblock.getWorld().triggerEffect(1000, isourceblock.getBlockPosition(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,441 +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);
|
||||
EnchantmentManager.a.b = null; // Reaper - Fix MC-128547
|
||||
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());
|
||||
}
|
||||
|
||||
// Reaper start - Fix MC-128547
|
||||
EnchantmentManager.c.b = null;
|
||||
EnchantmentManager.c.a = null;
|
||||
// Reaper end
|
||||
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
// Reaper start - Fix MC-128547
|
||||
EnchantmentManager.d.b = null;
|
||||
EnchantmentManager.d.a = null;
|
||||
// Reaper end
|
||||
}
|
||||
|
||||
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
@@ -1,476 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.destroystokyo.paper.event.entity.EndermanEscapeEvent;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.bukkit.event.entity.EntityTargetEvent;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class EntityEnderman extends EntityMonster {
|
||||
|
||||
private static final UUID a = UUID.fromString("020E0DFB-87AE-4653-9556-831010E291A0");
|
||||
private static final AttributeModifier b = (new AttributeModifier(EntityEnderman.a, "Attacking speed boost", 0.15000000596046448D, 0)).a(false);
|
||||
private static final Set<Block> c = Sets.newIdentityHashSet();
|
||||
private static final DataWatcherObject<Optional<IBlockData>> bx = DataWatcher.a(EntityEnderman.class, DataWatcherRegistry.g);
|
||||
private static final DataWatcherObject<Boolean> by = DataWatcher.a(EntityEnderman.class, DataWatcherRegistry.h);
|
||||
private int bz;
|
||||
private int bA;
|
||||
|
||||
public EntityEnderman(World world) {
|
||||
super(world);
|
||||
this.setSize(0.6F, 2.9F);
|
||||
this.P = 1.0F;
|
||||
this.a(PathType.WATER, -1.0F);
|
||||
}
|
||||
|
||||
protected void r() {
|
||||
this.goalSelector.a(0, new PathfinderGoalFloat(this));
|
||||
this.goalSelector.a(2, new PathfinderGoalMeleeAttack(this, 1.0D, false));
|
||||
this.goalSelector.a(7, new PathfinderGoalRandomStrollLand(this, 1.0D, 0.0F));
|
||||
this.goalSelector.a(8, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F));
|
||||
this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this));
|
||||
this.goalSelector.a(10, new EntityEnderman.PathfinderGoalEndermanPlaceBlock(this));
|
||||
this.goalSelector.a(11, new EntityEnderman.PathfinderGoalEndermanPickupBlock(this));
|
||||
this.targetSelector.a(1, new EntityEnderman.PathfinderGoalPlayerWhoLookedAtTarget(this));
|
||||
this.targetSelector.a(2, new PathfinderGoalHurtByTarget(this, false, new Class[0]));
|
||||
this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget(this, EntityEndermite.class, 10, true, false, new Predicate() {
|
||||
public boolean a(@Nullable EntityEndermite entityendermite) {
|
||||
return entityendermite.p();
|
||||
}
|
||||
|
||||
public boolean apply(@Nullable Object object) {
|
||||
return this.a((EntityEndermite) object);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
protected void initAttributes() {
|
||||
super.initAttributes();
|
||||
this.getAttributeInstance(GenericAttributes.maxHealth).setValue(40.0D);
|
||||
this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.30000001192092896D);
|
||||
this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(7.0D);
|
||||
this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(64.0D);
|
||||
}
|
||||
|
||||
public void setGoalTarget(@Nullable EntityLiving entityliving) {
|
||||
// CraftBukkit start - fire event
|
||||
setGoalTarget(entityliving, EntityTargetEvent.TargetReason.UNKNOWN, true);
|
||||
}
|
||||
|
||||
// Paper start
|
||||
private boolean tryEscape(EndermanEscapeEvent.Reason reason) {
|
||||
return new EndermanEscapeEvent((org.bukkit.craftbukkit.entity.CraftEnderman) this.getBukkitEntity(), reason).callEvent();
|
||||
}
|
||||
// Paper end
|
||||
|
||||
@Override
|
||||
public boolean setGoalTarget(EntityLiving entityliving, org.bukkit.event.entity.EntityTargetEvent.TargetReason reason, boolean fireEvent) {
|
||||
if (!super.setGoalTarget(entityliving, reason, fireEvent)) {
|
||||
return false;
|
||||
}
|
||||
entityliving = getGoalTarget();
|
||||
// CraftBukkit end
|
||||
AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED);
|
||||
|
||||
if (entityliving == null) {
|
||||
this.bA = 0;
|
||||
this.datawatcher.set(EntityEnderman.by, Boolean.valueOf(false));
|
||||
attributeinstance.c(EntityEnderman.b);
|
||||
} else {
|
||||
this.bA = this.ticksLived;
|
||||
this.datawatcher.set(EntityEnderman.by, Boolean.valueOf(true));
|
||||
if (!attributeinstance.a(EntityEnderman.b)) {
|
||||
attributeinstance.b(EntityEnderman.b);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
protected void i() {
|
||||
super.i();
|
||||
this.datawatcher.register(EntityEnderman.bx, Optional.absent());
|
||||
this.datawatcher.register(EntityEnderman.by, Boolean.valueOf(false));
|
||||
}
|
||||
|
||||
public void p() {
|
||||
if (this.ticksLived >= this.bz + 400) {
|
||||
this.bz = this.ticksLived;
|
||||
if (!this.isSilent()) {
|
||||
this.world.a(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ, SoundEffects.bh, this.bK(), 2.5F, 1.0F, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(DataWatcherObject<?> datawatcherobject) {
|
||||
if (EntityEnderman.by.equals(datawatcherobject) && this.do_() && this.world.isClientSide) {
|
||||
this.p();
|
||||
}
|
||||
|
||||
super.a(datawatcherobject);
|
||||
}
|
||||
|
||||
public static void a(DataConverterManager dataconvertermanager) {
|
||||
EntityInsentient.a(dataconvertermanager, EntityEnderman.class);
|
||||
}
|
||||
|
||||
public void b(NBTTagCompound nbttagcompound) {
|
||||
super.b(nbttagcompound);
|
||||
IBlockData iblockdata = this.getCarried();
|
||||
|
||||
if (iblockdata != null) {
|
||||
nbttagcompound.setShort("carried", (short) Block.getId(iblockdata.getBlock()));
|
||||
nbttagcompound.setShort("carriedData", (short) iblockdata.getBlock().toLegacyData(iblockdata));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(NBTTagCompound nbttagcompound) {
|
||||
super.a(nbttagcompound);
|
||||
IBlockData iblockdata;
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("carried", 8)) {
|
||||
iblockdata = Block.getByName(nbttagcompound.getString("carried")).fromLegacyData(nbttagcompound.getShort("carriedData") & '\uffff');
|
||||
} else {
|
||||
iblockdata = Block.getById(nbttagcompound.getShort("carried")).fromLegacyData(nbttagcompound.getShort("carriedData") & '\uffff');
|
||||
}
|
||||
|
||||
if (iblockdata == null || iblockdata.getBlock() == null || iblockdata.getMaterial() == Material.AIR) {
|
||||
iblockdata = null;
|
||||
}
|
||||
|
||||
this.setCarried(iblockdata);
|
||||
}
|
||||
|
||||
// Paper start - OBFHELPER - ok not really, but verify this on updates
|
||||
private boolean f(EntityHuman entityhuman) {
|
||||
boolean shouldAttack = f_real(entityhuman);
|
||||
com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent event = new com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent((org.bukkit.entity.Enderman) getBukkitEntity(), (org.bukkit.entity.Player) entityhuman.getBukkitEntity());
|
||||
event.setCancelled(!shouldAttack);
|
||||
return event.callEvent();
|
||||
}
|
||||
private boolean f_real(EntityHuman entityhuman) {
|
||||
// Paper end
|
||||
ItemStack itemstack = (ItemStack) entityhuman.inventory.armor.get(3);
|
||||
|
||||
if (itemstack.getItem() == Item.getItemOf(Blocks.PUMPKIN)) {
|
||||
return false;
|
||||
} else {
|
||||
Vec3D vec3d = entityhuman.e(1.0F).a();
|
||||
Vec3D vec3d1 = new Vec3D(this.locX - entityhuman.locX, this.getBoundingBox().b + (double) this.getHeadHeight() - (entityhuman.locY + (double) entityhuman.getHeadHeight()), this.locZ - entityhuman.locZ);
|
||||
double d0 = vec3d1.b();
|
||||
|
||||
vec3d1 = vec3d1.a();
|
||||
double d1 = vec3d.b(vec3d1);
|
||||
|
||||
return d1 > 1.0D - 0.025D / d0 ? entityhuman.hasLineOfSight(this) : false;
|
||||
}
|
||||
}
|
||||
|
||||
public float getHeadHeight() {
|
||||
return 2.55F;
|
||||
}
|
||||
|
||||
public void n() {
|
||||
if (this.world.isClientSide) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
this.world.addParticle(EnumParticle.PORTAL, this.locX + (this.random.nextDouble() - 0.5D) * (double) this.width, this.locY + this.random.nextDouble() * (double) this.length - 0.25D, this.locZ + (this.random.nextDouble() - 0.5D) * (double) this.width, (this.random.nextDouble() - 0.5D) * 2.0D, -this.random.nextDouble(), (this.random.nextDouble() - 0.5D) * 2.0D, new int[0]);
|
||||
}
|
||||
}
|
||||
|
||||
this.bd = false;
|
||||
super.n();
|
||||
}
|
||||
|
||||
protected void M() {
|
||||
if (this.an()) {
|
||||
this.damageEntity(DamageSource.DROWN, 1.0F);
|
||||
}
|
||||
|
||||
if (this.world.D() && this.ticksLived >= this.bA + 600) {
|
||||
float f = this.aw();
|
||||
|
||||
if (f > 0.5F && this.world.h(new BlockPosition(this)) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && tryEscape(EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper
|
||||
this.setGoalTarget((EntityLiving) null);
|
||||
this.dm();
|
||||
}
|
||||
}
|
||||
|
||||
super.M();
|
||||
}
|
||||
|
||||
public boolean teleportRandomly() { return dm(); } // Paper - OBFHELPER
|
||||
protected boolean dm() {
|
||||
double d0 = this.locX + (this.random.nextDouble() - 0.5D) * 64.0D;
|
||||
double d1 = this.locY + (double) (this.random.nextInt(64) - 32);
|
||||
double d2 = this.locZ + (this.random.nextDouble() - 0.5D) * 64.0D;
|
||||
|
||||
return this.k(d0, d1, d2);
|
||||
}
|
||||
|
||||
protected boolean a(Entity entity) {
|
||||
Vec3D vec3d = new Vec3D(this.locX - entity.locX, this.getBoundingBox().b + (double) (this.length / 2.0F) - entity.locY + (double) entity.getHeadHeight(), this.locZ - entity.locZ);
|
||||
|
||||
vec3d = vec3d.a();
|
||||
double d0 = 16.0D;
|
||||
double d1 = this.locX + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.x * 16.0D;
|
||||
double d2 = this.locY + (double) (this.random.nextInt(16) - 8) - vec3d.y * 16.0D;
|
||||
double d3 = this.locZ + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.z * 16.0D;
|
||||
|
||||
return this.k(d1, d2, d3);
|
||||
}
|
||||
|
||||
private boolean k(double d0, double d1, double d2) {
|
||||
boolean flag = this.j(d0, d1, d2);
|
||||
|
||||
if (flag) {
|
||||
this.world.a((EntityHuman) null, this.lastX, this.lastY, this.lastZ, SoundEffects.bi, this.bK(), 1.0F, 1.0F);
|
||||
this.a(SoundEffects.bi, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
protected SoundEffect F() {
|
||||
return this.do_() ? SoundEffects.bg : SoundEffects.bd;
|
||||
}
|
||||
|
||||
protected SoundEffect d(DamageSource damagesource) {
|
||||
return SoundEffects.bf;
|
||||
}
|
||||
|
||||
protected SoundEffect cf() {
|
||||
return SoundEffects.be;
|
||||
}
|
||||
|
||||
protected void dropEquipment(boolean flag, int i) {
|
||||
super.dropEquipment(flag, i);
|
||||
IBlockData iblockdata = this.getCarried();
|
||||
|
||||
if (iblockdata != null) {
|
||||
Item item = Item.getItemOf(iblockdata.getBlock());
|
||||
int j = item.k() ? iblockdata.getBlock().toLegacyData(iblockdata) : 0;
|
||||
|
||||
this.a(new ItemStack(item, 1, j), 0.0F);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected MinecraftKey J() {
|
||||
return LootTables.w;
|
||||
}
|
||||
|
||||
public void setCarried(@Nullable IBlockData iblockdata) {
|
||||
this.datawatcher.set(EntityEnderman.bx, Optional.fromNullable(iblockdata));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getCarried() {
|
||||
return (IBlockData) ((Optional) this.datawatcher.get(EntityEnderman.bx)).orNull();
|
||||
}
|
||||
|
||||
public boolean damageEntity(DamageSource damagesource, float f) {
|
||||
if (this.isInvulnerable(damagesource)) {
|
||||
return false;
|
||||
} else if (damagesource instanceof EntityDamageSourceIndirect && tryEscape(EndermanEscapeEvent.Reason.INDIRECT)) { // Paper
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
if (this.dm()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
boolean flag = super.damageEntity(damagesource, f);
|
||||
|
||||
if (damagesource.ignoresArmor() && this.random.nextInt(10) != 0 && tryEscape(damagesource == DamageSource.DROWN ? EndermanEscapeEvent.Reason.DROWN : EndermanEscapeEvent.Reason.CRITICAL_HIT)) { // Paper
|
||||
this.dm();
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean do_() {
|
||||
return ((Boolean) this.datawatcher.get(EntityEnderman.by)).booleanValue();
|
||||
}
|
||||
|
||||
static {
|
||||
EntityEnderman.c.add(Blocks.GRASS);
|
||||
EntityEnderman.c.add(Blocks.DIRT);
|
||||
EntityEnderman.c.add(Blocks.SAND);
|
||||
EntityEnderman.c.add(Blocks.GRAVEL);
|
||||
EntityEnderman.c.add(Blocks.YELLOW_FLOWER);
|
||||
EntityEnderman.c.add(Blocks.RED_FLOWER);
|
||||
EntityEnderman.c.add(Blocks.BROWN_MUSHROOM);
|
||||
EntityEnderman.c.add(Blocks.RED_MUSHROOM);
|
||||
EntityEnderman.c.add(Blocks.TNT);
|
||||
EntityEnderman.c.add(Blocks.CACTUS);
|
||||
EntityEnderman.c.add(Blocks.CLAY);
|
||||
EntityEnderman.c.add(Blocks.PUMPKIN);
|
||||
EntityEnderman.c.add(Blocks.MELON_BLOCK);
|
||||
EntityEnderman.c.add(Blocks.MYCELIUM);
|
||||
EntityEnderman.c.add(Blocks.NETHERRACK);
|
||||
}
|
||||
|
||||
static class PathfinderGoalEndermanPickupBlock extends PathfinderGoal {
|
||||
|
||||
private final EntityEnderman enderman;
|
||||
|
||||
public PathfinderGoalEndermanPickupBlock(EntityEnderman entityenderman) {
|
||||
this.enderman = entityenderman;
|
||||
}
|
||||
|
||||
public boolean a() {
|
||||
return this.enderman.getCarried() != null ? false : (!this.enderman.world.getGameRules().getBoolean("mobGriefing") ? false : this.enderman.getRandom().nextInt(20) == 0);
|
||||
}
|
||||
|
||||
public void e() {
|
||||
Random random = this.enderman.getRandom();
|
||||
World world = this.enderman.world;
|
||||
int i = MathHelper.floor(this.enderman.locX - 2.0D + random.nextDouble() * 4.0D);
|
||||
int j = MathHelper.floor(this.enderman.locY + random.nextDouble() * 3.0D);
|
||||
int k = MathHelper.floor(this.enderman.locZ - 2.0D + random.nextDouble() * 4.0D);
|
||||
BlockPosition blockposition = new BlockPosition(i, j, k);
|
||||
IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // NeonPaper
|
||||
if (iblockdata == null) return; // NeonPaper
|
||||
Block block = iblockdata.getBlock();
|
||||
MovingObjectPosition movingobjectposition = world.rayTrace(new Vec3D((double) ((float) MathHelper.floor(this.enderman.locX) + 0.5F), (double) ((float) j + 0.5F), (double) ((float) MathHelper.floor(this.enderman.locZ) + 0.5F)), new Vec3D((double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F)), false, true, false);
|
||||
boolean flag = movingobjectposition != null && movingobjectposition.a().equals(blockposition);
|
||||
|
||||
if (EntityEnderman.c.contains(block) && flag) {
|
||||
// CraftBukkit start - Pickup event
|
||||
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.enderman, this.enderman.world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), org.bukkit.Material.AIR).isCancelled()) {
|
||||
this.enderman.setCarried(iblockdata);
|
||||
world.setAir(blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static class PathfinderGoalEndermanPlaceBlock extends PathfinderGoal {
|
||||
|
||||
private final EntityEnderman a;
|
||||
|
||||
public PathfinderGoalEndermanPlaceBlock(EntityEnderman entityenderman) {
|
||||
this.a = entityenderman;
|
||||
}
|
||||
|
||||
public boolean a() {
|
||||
return this.a.getCarried() == null ? false : (!this.a.world.getGameRules().getBoolean("mobGriefing") ? false : this.a.getRandom().nextInt(2000) == 0);
|
||||
}
|
||||
|
||||
public void e() {
|
||||
Random random = this.a.getRandom();
|
||||
World world = this.a.world;
|
||||
int i = MathHelper.floor(this.a.locX - 1.0D + random.nextDouble() * 2.0D);
|
||||
int j = MathHelper.floor(this.a.locY + random.nextDouble() * 2.0D);
|
||||
int k = MathHelper.floor(this.a.locZ - 1.0D + random.nextDouble() * 2.0D);
|
||||
BlockPosition blockposition = new BlockPosition(i, j, k);
|
||||
IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // NeonPaper
|
||||
if (iblockdata == null) return; // NeonPaper
|
||||
IBlockData iblockdata1 = world.getType(blockposition.down());
|
||||
IBlockData iblockdata2 = this.a.getCarried();
|
||||
|
||||
if (iblockdata2 != null && this.a(world, blockposition, iblockdata2.getBlock(), iblockdata, iblockdata1)) {
|
||||
// CraftBukkit start - Place event
|
||||
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.a, blockposition, this.a.getCarried().getBlock(), this.a.getCarried().getBlock().toLegacyData(this.a.getCarried())).isCancelled()) {
|
||||
world.setTypeAndData(blockposition, iblockdata2, 3);
|
||||
this.a.setCarried((IBlockData) null);
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean a(World world, BlockPosition blockposition, Block block, IBlockData iblockdata, IBlockData iblockdata1) {
|
||||
return !block.canPlace(world, blockposition) ? false : (iblockdata.getMaterial() != Material.AIR ? false : (iblockdata1.getMaterial() == Material.AIR ? false : iblockdata1.g()));
|
||||
}
|
||||
}
|
||||
|
||||
static class PathfinderGoalPlayerWhoLookedAtTarget extends PathfinderGoalNearestAttackableTarget<EntityHuman> {
|
||||
|
||||
private final EntityEnderman i; public EntityEnderman getEnderman() { return i; } // Paper - OBFHELPER
|
||||
private EntityHuman j;
|
||||
private int k;
|
||||
private int l;
|
||||
|
||||
public PathfinderGoalPlayerWhoLookedAtTarget(EntityEnderman entityenderman) {
|
||||
super(entityenderman, EntityHuman.class, false);
|
||||
this.i = entityenderman;
|
||||
}
|
||||
|
||||
public boolean a() {
|
||||
double d0 = this.i();
|
||||
|
||||
this.j = this.i.world.a(this.i.locX, this.i.locY, this.i.locZ, d0, d0, (Function) null, new Predicate() {
|
||||
public boolean a(@Nullable EntityHuman entityhuman) {
|
||||
return entityhuman != null && PathfinderGoalPlayerWhoLookedAtTarget.this.i.f(entityhuman);
|
||||
}
|
||||
|
||||
public boolean apply(@Nullable Object object) {
|
||||
return this.a((EntityHuman) object);
|
||||
}
|
||||
});
|
||||
return this.j != null;
|
||||
}
|
||||
|
||||
public void c() {
|
||||
this.k = 5;
|
||||
this.l = 0;
|
||||
}
|
||||
|
||||
public void d() {
|
||||
this.j = null;
|
||||
super.d();
|
||||
}
|
||||
|
||||
public boolean b() {
|
||||
if (this.j != null) {
|
||||
if (!this.i.f(this.j)) {
|
||||
return false;
|
||||
} else {
|
||||
this.i.a((Entity) this.j, 10.0F, 10.0F);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return this.d != null && ((EntityHuman) this.d).isAlive() ? true : super.b();
|
||||
}
|
||||
}
|
||||
|
||||
public void e() {
|
||||
if (this.j != null) {
|
||||
if (--this.k <= 0) {
|
||||
this.d = this.j;
|
||||
this.j = null;
|
||||
super.c();
|
||||
}
|
||||
} else {
|
||||
if (this.d != null) {
|
||||
if (this.i.f((EntityHuman) this.d)) {
|
||||
if (((EntityHuman) this.d).h(this.i) < 16.0D && this.getEnderman().tryEscape(EndermanEscapeEvent.Reason.STARE)) { // Paper
|
||||
this.i.dm();
|
||||
}
|
||||
|
||||
this.l = 0;
|
||||
} else if (((EntityHuman) this.d).h(this.i) > 256.0D && this.l++ >= 30 && this.i.a((Entity) this.d)) {
|
||||
this.l = 0;
|
||||
}
|
||||
}
|
||||
|
||||
super.e();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,538 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
// CraftBukkit start
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Fish;
|
||||
import org.bukkit.event.player.PlayerFishEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class EntityFishingHook extends Entity {
|
||||
|
||||
private static final DataWatcherObject<Integer> b = DataWatcher.a(EntityFishingHook.class, DataWatcherRegistry.b);
|
||||
private boolean isInGround;
|
||||
private int d;
|
||||
public EntityHuman owner;
|
||||
private int f;
|
||||
private int g;
|
||||
private int h;
|
||||
private int at;
|
||||
private float au;
|
||||
public Entity hooked;
|
||||
private EntityFishingHook.HookState av;
|
||||
private int aw;
|
||||
private int ax;
|
||||
|
||||
public EntityFishingHook(World world, EntityHuman entityhuman) {
|
||||
super(world);
|
||||
this.av = EntityFishingHook.HookState.FLYING;
|
||||
this.a(entityhuman);
|
||||
this.n();
|
||||
}
|
||||
|
||||
private void a(EntityHuman entityhuman) {
|
||||
this.setSize(0.25F, 0.25F);
|
||||
this.ah = true;
|
||||
this.owner = entityhuman;
|
||||
this.owner.hookedFish = this;
|
||||
}
|
||||
|
||||
public void a(int i) {
|
||||
this.ax = i;
|
||||
}
|
||||
|
||||
public void c(int i) {
|
||||
this.aw = i;
|
||||
}
|
||||
|
||||
private void n() {
|
||||
float f = this.owner.lastPitch + (this.owner.pitch - this.owner.lastPitch);
|
||||
float f1 = this.owner.lastYaw + (this.owner.yaw - this.owner.lastYaw);
|
||||
float f2 = MathHelper.cos(-f1 * 0.017453292F - 3.1415927F);
|
||||
float f3 = MathHelper.sin(-f1 * 0.017453292F - 3.1415927F);
|
||||
float f4 = -MathHelper.cos(-f * 0.017453292F);
|
||||
float f5 = MathHelper.sin(-f * 0.017453292F);
|
||||
double d0 = this.owner.lastX + (this.owner.locX - this.owner.lastX) - (double) f3 * 0.3D;
|
||||
double d1 = this.owner.lastY + (this.owner.locY - this.owner.lastY) + (double) this.owner.getHeadHeight();
|
||||
double d2 = this.owner.lastZ + (this.owner.locZ - this.owner.lastZ) - (double) f2 * 0.3D;
|
||||
|
||||
this.setPositionRotation(d0, d1, d2, f1, f);
|
||||
this.motX = (double) (-f3);
|
||||
this.motY = (double) MathHelper.a(-(f5 / f4), -5.0F, 5.0F);
|
||||
this.motZ = (double) (-f2);
|
||||
float f6 = MathHelper.sqrt(this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ);
|
||||
|
||||
this.motX *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D;
|
||||
this.motY *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D;
|
||||
this.motZ *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D;
|
||||
float f7 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ);
|
||||
|
||||
this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D);
|
||||
this.pitch = (float) (MathHelper.c(this.motY, (double) f7) * 57.2957763671875D);
|
||||
this.lastYaw = this.yaw;
|
||||
this.lastPitch = this.pitch;
|
||||
}
|
||||
|
||||
protected void i() {
|
||||
this.getDataWatcher().register(EntityFishingHook.b, Integer.valueOf(0));
|
||||
}
|
||||
|
||||
public void a(DataWatcherObject<?> datawatcherobject) {
|
||||
if (EntityFishingHook.b.equals(datawatcherobject)) {
|
||||
int i = ((Integer) this.getDataWatcher().get(EntityFishingHook.b)).intValue();
|
||||
|
||||
this.hooked = i > 0 ? this.world.getEntity(i - 1) : null;
|
||||
}
|
||||
|
||||
super.a(datawatcherobject);
|
||||
}
|
||||
|
||||
public void B_() {
|
||||
super.B_();
|
||||
if (this.owner == null) {
|
||||
this.die();
|
||||
} else if (this.world.isClientSide || !this.p()) {
|
||||
if (this.isInGround) {
|
||||
++this.d;
|
||||
if (this.d >= 1200) {
|
||||
this.die();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float f = 0.0F;
|
||||
BlockPosition blockposition = new BlockPosition(this);
|
||||
IBlockData iblockdata = this.world.getType(blockposition);
|
||||
|
||||
if (iblockdata.getMaterial() == Material.WATER) {
|
||||
f = BlockFluids.g(iblockdata, this.world, blockposition);
|
||||
}
|
||||
|
||||
double d0;
|
||||
|
||||
if (this.av == EntityFishingHook.HookState.FLYING) {
|
||||
if (this.hooked != null) {
|
||||
this.motX = 0.0D;
|
||||
this.motY = 0.0D;
|
||||
this.motZ = 0.0D;
|
||||
this.av = EntityFishingHook.HookState.HOOKED_IN_ENTITY;
|
||||
return;
|
||||
}
|
||||
|
||||
if (f > 0.0F) {
|
||||
this.motX *= 0.3D;
|
||||
this.motY *= 0.2D;
|
||||
this.motZ *= 0.3D;
|
||||
this.av = EntityFishingHook.HookState.BOBBING;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.world.isClientSide) {
|
||||
this.r();
|
||||
}
|
||||
|
||||
if (!this.isInGround && !this.onGround && !this.positionChanged) {
|
||||
++this.f;
|
||||
} else {
|
||||
this.f = 0;
|
||||
this.motX = 0.0D;
|
||||
this.motY = 0.0D;
|
||||
this.motZ = 0.0D;
|
||||
}
|
||||
} else {
|
||||
if (this.av == EntityFishingHook.HookState.HOOKED_IN_ENTITY) {
|
||||
if (this.hooked != null) {
|
||||
if (this.hooked.dead) {
|
||||
this.hooked = null;
|
||||
this.av = EntityFishingHook.HookState.FLYING;
|
||||
} else {
|
||||
this.locX = this.hooked.locX;
|
||||
double d1 = (double) this.hooked.length;
|
||||
|
||||
this.locY = this.hooked.getBoundingBox().b + d1 * 0.8D;
|
||||
this.locZ = this.hooked.locZ;
|
||||
this.setPosition(this.locX, this.locY, this.locZ);
|
||||
if (this.ak) this.die(); // NeonPaper - Prevent going through portals
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.av == EntityFishingHook.HookState.BOBBING) {
|
||||
this.motX *= 0.9D;
|
||||
this.motZ *= 0.9D;
|
||||
d0 = this.locY + this.motY - (double) blockposition.getY() - (double) f;
|
||||
if (Math.abs(d0) < 0.01D) {
|
||||
d0 += Math.signum(d0) * 0.1D;
|
||||
}
|
||||
|
||||
this.motY -= d0 * (double) this.random.nextFloat() * 0.2D;
|
||||
if (!this.world.isClientSide && f > 0.0F) {
|
||||
this.a(blockposition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iblockdata.getMaterial() != Material.WATER) {
|
||||
this.motY -= 0.03D;
|
||||
}
|
||||
|
||||
this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ);
|
||||
this.q();
|
||||
d0 = 0.92D;
|
||||
this.motX *= 0.92D;
|
||||
this.motY *= 0.92D;
|
||||
this.motZ *= 0.92D;
|
||||
this.setPosition(this.locX, this.locY, this.locZ);
|
||||
|
||||
// Paper start - These shouldn't be going through portals
|
||||
if (this.inPortal()) {
|
||||
this.die();
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
}
|
||||
|
||||
private boolean p() {
|
||||
ItemStack itemstack = this.owner.getItemInMainHand();
|
||||
ItemStack itemstack1 = this.owner.getItemInOffHand();
|
||||
boolean flag = itemstack.getItem() == Items.FISHING_ROD;
|
||||
boolean flag1 = itemstack1.getItem() == Items.FISHING_ROD;
|
||||
|
||||
if (!this.owner.dead && this.owner.isAlive() && (flag || flag1) && this.h(this.owner) <= 1024.0D) {
|
||||
return false;
|
||||
} else {
|
||||
this.die();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void q() {
|
||||
float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ);
|
||||
|
||||
this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D);
|
||||
|
||||
for (this.pitch = (float) (MathHelper.c(this.motY, (double) f) * 57.2957763671875D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) {
|
||||
;
|
||||
}
|
||||
|
||||
while (this.pitch - this.lastPitch >= 180.0F) {
|
||||
this.lastPitch += 360.0F;
|
||||
}
|
||||
|
||||
while (this.yaw - this.lastYaw < -180.0F) {
|
||||
this.lastYaw -= 360.0F;
|
||||
}
|
||||
|
||||
while (this.yaw - this.lastYaw >= 180.0F) {
|
||||
this.lastYaw += 360.0F;
|
||||
}
|
||||
|
||||
this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F;
|
||||
this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F;
|
||||
}
|
||||
|
||||
private void r() {
|
||||
Vec3D vec3d = new Vec3D(this.locX, this.locY, this.locZ);
|
||||
Vec3D vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ);
|
||||
MovingObjectPosition movingobjectposition = this.world.rayTrace(vec3d, vec3d1, false, true, false);
|
||||
|
||||
vec3d = new Vec3D(this.locX, this.locY, this.locZ);
|
||||
vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ);
|
||||
|
||||
// Paper start - Call ProjectileCollideEvent
|
||||
if (movingobjectposition != null && movingobjectposition.entity != null) {
|
||||
com.destroystokyo.paper.event.entity.ProjectileCollideEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileCollideEvent(this, movingobjectposition);
|
||||
if (event.isCancelled()) {
|
||||
movingobjectposition = null;
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
|
||||
if (movingobjectposition != null) {
|
||||
vec3d1 = new Vec3D(movingobjectposition.pos.x, movingobjectposition.pos.y, movingobjectposition.pos.z);
|
||||
}
|
||||
|
||||
Entity entity = null;
|
||||
List list = this.world.getEntities(this, this.getBoundingBox().b(this.motX, this.motY, this.motZ).g(1.0D));
|
||||
double d0 = 0.0D;
|
||||
Iterator iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entity entity1 = (Entity) iterator.next();
|
||||
|
||||
if (this.a(entity1) && (entity1 != this.owner || this.f >= 5)) {
|
||||
AxisAlignedBB axisalignedbb = entity1.getBoundingBox().g(0.30000001192092896D);
|
||||
MovingObjectPosition movingobjectposition1 = axisalignedbb.b(vec3d, vec3d1);
|
||||
|
||||
if (movingobjectposition1 != null) {
|
||||
double d1 = vec3d.distanceSquared(movingobjectposition1.pos);
|
||||
|
||||
if (d1 < d0 || d0 == 0.0D) {
|
||||
entity = entity1;
|
||||
d0 = d1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entity != null) {
|
||||
movingobjectposition = new MovingObjectPosition(entity);
|
||||
}
|
||||
|
||||
if (movingobjectposition != null && movingobjectposition.type != MovingObjectPosition.EnumMovingObjectType.MISS) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition); // Craftbukkit - Call event
|
||||
if (movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.ENTITY) {
|
||||
this.hooked = movingobjectposition.entity;
|
||||
this.s();
|
||||
} else {
|
||||
this.isInGround = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void s() {
|
||||
this.getDataWatcher().set(EntityFishingHook.b, Integer.valueOf(this.hooked.getId() + 1));
|
||||
}
|
||||
|
||||
private void a(BlockPosition blockposition) {
|
||||
WorldServer worldserver = (WorldServer) this.world;
|
||||
int i = 1;
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
|
||||
if (this.random.nextFloat() < 0.25F && this.world.isRainingAt(blockposition1)) {
|
||||
++i;
|
||||
}
|
||||
|
||||
if (this.random.nextFloat() < 0.5F && !this.world.h(blockposition1)) {
|
||||
--i;
|
||||
}
|
||||
|
||||
if (this.g > 0) {
|
||||
--this.g;
|
||||
if (this.g <= 0) {
|
||||
this.h = 0;
|
||||
this.at = 0;
|
||||
// CraftBukkit start
|
||||
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT);
|
||||
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
|
||||
// CraftBukkit end
|
||||
} else {
|
||||
this.motY -= 0.2D * (double) this.random.nextFloat() * (double) this.random.nextFloat();
|
||||
}
|
||||
} else {
|
||||
float f;
|
||||
float f1;
|
||||
float f2;
|
||||
double d0;
|
||||
double d1;
|
||||
double d2;
|
||||
Block block;
|
||||
|
||||
if (this.at > 0) {
|
||||
this.at -= i;
|
||||
if (this.at > 0) {
|
||||
this.au = (float) ((double) this.au + this.random.nextGaussian() * 4.0D);
|
||||
f = this.au * 0.017453292F;
|
||||
f1 = MathHelper.sin(f);
|
||||
f2 = MathHelper.cos(f);
|
||||
d0 = this.locX + (double) (f1 * (float) this.at * 0.1F);
|
||||
d1 = (double) ((float) MathHelper.floor(this.getBoundingBox().b) + 1.0F);
|
||||
d2 = this.locZ + (double) (f2 * (float) this.at * 0.1F);
|
||||
block = worldserver.getType(new BlockPosition(d0, d1 - 1.0D, d2)).getBlock();
|
||||
if (block == Blocks.WATER || block == Blocks.FLOWING_WATER) {
|
||||
if (this.random.nextFloat() < 0.15F) {
|
||||
worldserver.a(EnumParticle.WATER_BUBBLE, d0, d1 - 0.10000000149011612D, d2, 1, (double) f1, 0.1D, (double) f2, 0.0D, new int[0]);
|
||||
}
|
||||
|
||||
float f3 = f1 * 0.04F;
|
||||
float f4 = f2 * 0.04F;
|
||||
|
||||
worldserver.a(EnumParticle.WATER_WAKE, d0, d1, d2, 0, (double) f4, 0.01D, (double) (-f3), 1.0D, new int[0]);
|
||||
worldserver.a(EnumParticle.WATER_WAKE, d0, d1, d2, 0, (double) (-f4), 0.01D, (double) f3, 1.0D, new int[0]);
|
||||
}
|
||||
} else {
|
||||
// CraftBukkit start
|
||||
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.BITE);
|
||||
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
|
||||
if (playerFishEvent.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.motY = (double) (-0.4F * MathHelper.a(this.random, 0.6F, 1.0F));
|
||||
this.a(SoundEffects.K, 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
|
||||
double d3 = this.getBoundingBox().b + 0.5D;
|
||||
|
||||
worldserver.a(EnumParticle.WATER_BUBBLE, this.locX, d3, this.locZ, (int) (1.0F + this.width * 20.0F), (double) this.width, 0.0D, (double) this.width, 0.20000000298023224D, new int[0]);
|
||||
worldserver.a(EnumParticle.WATER_WAKE, this.locX, d3, this.locZ, (int) (1.0F + this.width * 20.0F), (double) this.width, 0.0D, (double) this.width, 0.20000000298023224D, new int[0]);
|
||||
this.g = MathHelper.nextInt(this.random, 20, 40);
|
||||
}
|
||||
} else if (this.h > 0) {
|
||||
this.h -= i;
|
||||
f = 0.15F;
|
||||
if (this.h < 20) {
|
||||
f = (float) ((double) f + (double) (20 - this.h) * 0.05D);
|
||||
} else if (this.h < 40) {
|
||||
f = (float) ((double) f + (double) (40 - this.h) * 0.02D);
|
||||
} else if (this.h < 60) {
|
||||
f = (float) ((double) f + (double) (60 - this.h) * 0.01D);
|
||||
}
|
||||
|
||||
if (this.random.nextFloat() < f) {
|
||||
f1 = MathHelper.a(this.random, 0.0F, 360.0F) * 0.017453292F;
|
||||
f2 = MathHelper.a(this.random, 25.0F, 60.0F);
|
||||
d0 = this.locX + (double) (MathHelper.sin(f1) * f2 * 0.1F);
|
||||
d1 = (double) ((float) MathHelper.floor(this.getBoundingBox().b) + 1.0F);
|
||||
d2 = this.locZ + (double) (MathHelper.cos(f1) * f2 * 0.1F);
|
||||
block = worldserver.getType(new BlockPosition((int) d0, (int) d1 - 1, (int) d2)).getBlock();
|
||||
if (block == Blocks.WATER || block == Blocks.FLOWING_WATER) {
|
||||
worldserver.a(EnumParticle.WATER_SPLASH, d0, d1, d2, 2 + this.random.nextInt(2), 0.10000000149011612D, 0.0D, 0.10000000149011612D, 0.0D, new int[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.h <= 0) {
|
||||
this.au = MathHelper.a(this.random, 0.0F, 360.0F);
|
||||
this.at = MathHelper.nextInt(this.random, 20, 80);
|
||||
}
|
||||
} else {
|
||||
this.h = MathHelper.nextInt(this.random, world.paperConfig.fishingMinTicks, world.paperConfig.fishingMaxTicks); // Paper
|
||||
this.h -= this.ax * 20 * 5;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected boolean a(Entity entity) {
|
||||
return entity.isInteractable() || entity instanceof EntityItem;
|
||||
}
|
||||
|
||||
public void b(NBTTagCompound nbttagcompound) {}
|
||||
|
||||
public void a(NBTTagCompound nbttagcompound) {}
|
||||
|
||||
public int j() {
|
||||
if (!this.world.isClientSide && this.owner != null) {
|
||||
int i = 0;
|
||||
|
||||
if (this.hooked != null) {
|
||||
// CraftBukkit start
|
||||
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), this.hooked.getBukkitEntity(), (Fish) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_ENTITY);
|
||||
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
|
||||
|
||||
if (playerFishEvent.isCancelled()) {
|
||||
return 0;
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.k();
|
||||
this.world.broadcastEntityEffect(this, (byte) 31);
|
||||
i = this.hooked instanceof EntityItem ? 3 : 5;
|
||||
} else if (this.g > 0) {
|
||||
LootTableInfo.a loottableinfo_a = new LootTableInfo.a((WorldServer) this.world);
|
||||
|
||||
loottableinfo_a.a((float) this.aw + this.owner.du());
|
||||
Iterator iterator = this.world.getLootTableRegistry().a(LootTables.aA).a(this.random, loottableinfo_a.a()).iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ItemStack itemstack = (ItemStack) iterator.next();
|
||||
EntityItem entityitem = new EntityItem(this.world, this.locX, this.locY, this.locZ, itemstack);
|
||||
// CraftBukkit start
|
||||
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), entityitem.getBukkitEntity(), (Fish) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_FISH);
|
||||
playerFishEvent.setExpToDrop(this.random.nextInt(6) + 1);
|
||||
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
|
||||
|
||||
if (playerFishEvent.isCancelled()) {
|
||||
return 0;
|
||||
}
|
||||
// CraftBukkit end
|
||||
double d0 = this.owner.locX - this.locX;
|
||||
double d1 = this.owner.locY - this.locY;
|
||||
double d2 = this.owner.locZ - this.locZ;
|
||||
double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
|
||||
double d4 = 0.1D;
|
||||
|
||||
entityitem.motX = d0 * 0.1D;
|
||||
entityitem.motY = d1 * 0.1D + (double) MathHelper.sqrt(d3) * 0.08D;
|
||||
entityitem.motZ = d2 * 0.1D;
|
||||
this.world.addEntity(entityitem);
|
||||
// CraftBukkit start - this.random.nextInt(6) + 1 -> playerFishEvent.getExpToDrop()
|
||||
if (playerFishEvent.getExpToDrop() > 0) {
|
||||
this.owner.world.addEntity(new EntityExperienceOrb(this.owner.world, this.owner.locX, this.owner.locY + 0.5D, this.owner.locZ + 0.5D, playerFishEvent.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.FISHING, this.owner, this)); // Paper
|
||||
}
|
||||
// CraftBukkit end
|
||||
Item item = itemstack.getItem();
|
||||
|
||||
if (item == Items.FISH || item == Items.COOKED_FISH) {
|
||||
this.owner.a(StatisticList.E, 1);
|
||||
}
|
||||
}
|
||||
|
||||
i = 1;
|
||||
}
|
||||
|
||||
if (this.isInGround) {
|
||||
// CraftBukkit start
|
||||
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.IN_GROUND);
|
||||
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
|
||||
|
||||
if (playerFishEvent.isCancelled()) {
|
||||
return 0;
|
||||
}
|
||||
// CraftBukkit end
|
||||
i = 2;
|
||||
}
|
||||
// CraftBukkit start
|
||||
if (i == 0) {
|
||||
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT);
|
||||
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
|
||||
if (playerFishEvent.isCancelled()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
this.die();
|
||||
return i;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected void k() {
|
||||
if (this.owner != null) {
|
||||
double d0 = this.owner.locX - this.locX;
|
||||
double d1 = this.owner.locY - this.locY;
|
||||
double d2 = this.owner.locZ - this.locZ;
|
||||
double d3 = 0.1D;
|
||||
|
||||
this.hooked.motX += d0 * 0.1D;
|
||||
this.hooked.motY += d1 * 0.1D;
|
||||
this.hooked.motZ += d2 * 0.1D;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean playStepSound() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void die() {
|
||||
super.die();
|
||||
if (this.owner != null) {
|
||||
this.owner.hookedFish = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public EntityHuman l() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
static enum HookState {
|
||||
|
||||
FLYING, HOOKED_IN_ENTITY, BOBBING;
|
||||
|
||||
private HookState() {}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
s = "Maximum (" + s + ")";
|
||||
}
|
||||
|
||||
return s;
|
||||
if (i == Integer.MAX_VALUE) {
|
||||
s = "Maximum (" + s + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return this.a();
|
||||
}
|
||||
return s;
|
||||
});
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,654 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerVelocityEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class EntityTrackerEntry {
|
||||
|
||||
private static final Logger c = LogManager.getLogger();
|
||||
private final Entity tracker;
|
||||
private final int e;
|
||||
private int f;
|
||||
private final int g;
|
||||
private long xLoc;
|
||||
private long yLoc;
|
||||
private long zLoc;
|
||||
private int yRot;
|
||||
private int xRot;
|
||||
private int headYaw;
|
||||
private double n;
|
||||
private double o;
|
||||
private double p;
|
||||
public int a;
|
||||
private double q;
|
||||
private double r;
|
||||
private double s;
|
||||
private boolean isMoving;
|
||||
private final boolean u;
|
||||
private int v;
|
||||
private List<Entity> w = Collections.emptyList();
|
||||
private boolean x;
|
||||
private boolean y;
|
||||
public boolean b;
|
||||
// Paper start
|
||||
// Replace trackedPlayers Set with a Map. The value is true until the player receives
|
||||
// their first update (which is forced to have absolute coordinates), false afterward.
|
||||
public java.util.Map<EntityPlayer, Boolean> trackedPlayerMap = new java.util.HashMap<EntityPlayer, Boolean>();
|
||||
public Set<EntityPlayer> trackedPlayers = trackedPlayerMap.keySet();
|
||||
// Paper end
|
||||
|
||||
public EntityTrackerEntry(Entity entity, int i, int j, int k, boolean flag) {
|
||||
entity.tracker = this; // Paper
|
||||
this.tracker = entity;
|
||||
this.e = i;
|
||||
this.f = j;
|
||||
this.g = k;
|
||||
this.u = flag;
|
||||
this.xLoc = EntityTracker.a(entity.locX);
|
||||
this.yLoc = EntityTracker.a(entity.locY);
|
||||
this.zLoc = EntityTracker.a(entity.locZ);
|
||||
this.yRot = MathHelper.d(entity.yaw * 256.0F / 360.0F);
|
||||
this.xRot = MathHelper.d(entity.pitch * 256.0F / 360.0F);
|
||||
this.headYaw = MathHelper.d(entity.getHeadRotation() * 256.0F / 360.0F);
|
||||
this.y = entity.onGround;
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
return object instanceof EntityTrackerEntry ? ((EntityTrackerEntry) object).tracker.getId() == this.tracker.getId() : false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.tracker.getId();
|
||||
}
|
||||
|
||||
public void track(List<EntityHuman> list) {
|
||||
this.b = false;
|
||||
if (!this.isMoving || this.tracker.d(this.q, this.r, this.s) > 16.0D) {
|
||||
this.q = this.tracker.locX;
|
||||
this.r = this.tracker.locY;
|
||||
this.s = this.tracker.locZ;
|
||||
this.isMoving = true;
|
||||
this.b = true;
|
||||
this.scanPlayers(list);
|
||||
}
|
||||
|
||||
List list1 = this.tracker.bF();
|
||||
|
||||
if (!list1.equals(this.w)) {
|
||||
this.w = list1;
|
||||
this.broadcastIncludingSelf(new PacketPlayOutMount(this.tracker)); // CraftBukkit
|
||||
}
|
||||
|
||||
// PAIL : rename
|
||||
if (this.tracker instanceof EntityItemFrame && this.a % 20 == 0) { // Paper
|
||||
EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker;
|
||||
ItemStack itemstack = entityitemframe.getItem();
|
||||
|
||||
if (itemstack != null && itemstack.getItem() instanceof ItemWorldMap) { // Paper - moved back up
|
||||
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, this.tracker.world);
|
||||
Iterator iterator = this.trackedPlayers.iterator(); // CraftBukkit
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityHuman entityhuman = (EntityHuman) iterator.next();
|
||||
EntityPlayer entityplayer = (EntityPlayer) entityhuman;
|
||||
|
||||
worldmap.a(entityplayer, itemstack);
|
||||
Packet packet = Items.FILLED_MAP.a(itemstack, this.tracker.world, (EntityHuman) entityplayer);
|
||||
|
||||
if (packet != null) {
|
||||
entityplayer.playerConnection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.d();
|
||||
}
|
||||
|
||||
if (this.a % this.g == 0 || this.tracker.impulse || this.tracker.getDataWatcher().a()) {
|
||||
int i;
|
||||
|
||||
if (this.tracker.isPassenger()) {
|
||||
i = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F);
|
||||
int j = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F);
|
||||
boolean flag = Math.abs(i - this.yRot) >= 1 || Math.abs(j - this.xRot) >= 1;
|
||||
|
||||
if (flag) {
|
||||
this.broadcast(new PacketPlayOutEntity.PacketPlayOutEntityLook(this.tracker.getId(), (byte) i, (byte) j, this.tracker.onGround));
|
||||
this.yRot = i;
|
||||
this.xRot = j;
|
||||
}
|
||||
|
||||
this.xLoc = EntityTracker.a(this.tracker.locX);
|
||||
this.yLoc = EntityTracker.a(this.tracker.locY);
|
||||
this.zLoc = EntityTracker.a(this.tracker.locZ);
|
||||
this.d();
|
||||
this.x = true;
|
||||
} else {
|
||||
++this.v;
|
||||
long k = EntityTracker.a(this.tracker.locX);
|
||||
long l = EntityTracker.a(this.tracker.locY);
|
||||
long i1 = EntityTracker.a(this.tracker.locZ);
|
||||
int j1 = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F);
|
||||
int k1 = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F);
|
||||
long l1 = k - this.xLoc;
|
||||
long i2 = l - this.yLoc;
|
||||
long j2 = i1 - this.zLoc;
|
||||
Object object = null;
|
||||
boolean flag1 = l1 * l1 + i2 * i2 + j2 * j2 >= 128L || this.a % 60 == 0;
|
||||
boolean flag2 = Math.abs(j1 - this.yRot) >= 1 || Math.abs(k1 - this.xRot) >= 1;
|
||||
|
||||
if (this.a > 0 || this.tracker instanceof EntityArrow) { // Paper - Moved up
|
||||
// CraftBukkit start - Code moved from below
|
||||
if (flag1) {
|
||||
this.xLoc = k;
|
||||
this.yLoc = l;
|
||||
this.zLoc = i1;
|
||||
}
|
||||
|
||||
if (flag2) {
|
||||
this.yRot = j1;
|
||||
this.xRot = k1;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (l1 >= -32768L && l1 < 32768L && i2 >= -32768L && i2 < 32768L && j2 >= -32768L && j2 < 32768L && this.v <= 400 && !this.x && this.y == this.tracker.onGround) {
|
||||
if ((!flag1 || !flag2) && !(this.tracker instanceof EntityArrow)) {
|
||||
if (flag1) {
|
||||
object = new PacketPlayOutEntity.PacketPlayOutRelEntityMove(this.tracker.getId(), l1, i2, j2, this.tracker.onGround);
|
||||
} else if (flag2) {
|
||||
object = new PacketPlayOutEntity.PacketPlayOutEntityLook(this.tracker.getId(), (byte) j1, (byte) k1, this.tracker.onGround);
|
||||
}
|
||||
} else {
|
||||
object = new PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook(this.tracker.getId(), l1, i2, j2, (byte) j1, (byte) k1, this.tracker.onGround);
|
||||
}
|
||||
} else {
|
||||
this.y = this.tracker.onGround;
|
||||
this.v = 0;
|
||||
// CraftBukkit start - Refresh list of who can see a player before sending teleport packet
|
||||
if (this.tracker instanceof EntityPlayer) {
|
||||
this.scanPlayers(new java.util.ArrayList(this.trackedPlayers));
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.c();
|
||||
object = new PacketPlayOutEntityTeleport(this.tracker);
|
||||
}
|
||||
}
|
||||
|
||||
boolean flag3 = this.u;
|
||||
|
||||
if (this.tracker instanceof EntityLiving && ((EntityLiving) this.tracker).cP()) {
|
||||
flag3 = true;
|
||||
}
|
||||
|
||||
if (flag3 && this.a > 0) {
|
||||
double d0 = this.tracker.motX - this.n;
|
||||
double d1 = this.tracker.motY - this.o;
|
||||
double d2 = this.tracker.motZ - this.p;
|
||||
double d3 = 0.02D;
|
||||
double d4 = d0 * d0 + d1 * d1 + d2 * d2;
|
||||
|
||||
if (d4 > 4.0E-4D || d4 > 0.0D && this.tracker.motX == 0.0D && this.tracker.motY == 0.0D && this.tracker.motZ == 0.0D) {
|
||||
this.n = this.tracker.motX;
|
||||
this.o = this.tracker.motY;
|
||||
this.p = this.tracker.motZ;
|
||||
this.broadcast(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.n, this.o, this.p));
|
||||
}
|
||||
}
|
||||
|
||||
if (object != null) {
|
||||
// Paper start - ensure fresh viewers get an absolute position on their first update,
|
||||
// since we can't be certain what position they received in the spawn packet.
|
||||
if (object instanceof PacketPlayOutEntityTeleport) {
|
||||
this.broadcast((Packet) object);
|
||||
} else {
|
||||
PacketPlayOutEntityTeleport teleportPacket = null;
|
||||
|
||||
for (java.util.Map.Entry<EntityPlayer, Boolean> viewer : trackedPlayerMap.entrySet()) {
|
||||
if (viewer.getValue()) {
|
||||
viewer.setValue(false);
|
||||
if (teleportPacket == null) {
|
||||
teleportPacket = new PacketPlayOutEntityTeleport(this.tracker);
|
||||
}
|
||||
viewer.getKey().playerConnection.sendPacket(teleportPacket);
|
||||
} else {
|
||||
viewer.getKey().playerConnection.sendPacket((Packet) object);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
|
||||
this.d();
|
||||
/* CraftBukkit start - Code moved up
|
||||
if (flag1) {
|
||||
this.xLoc = k;
|
||||
this.yLoc = l;
|
||||
this.zLoc = i1;
|
||||
}
|
||||
|
||||
if (flag2) {
|
||||
this.yRot = j1;
|
||||
this.xRot = k1;
|
||||
}
|
||||
// CraftBukkit end */
|
||||
|
||||
this.x = false;
|
||||
}
|
||||
|
||||
i = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
|
||||
if (Math.abs(i - this.headYaw) >= 1) {
|
||||
this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i));
|
||||
this.headYaw = i;
|
||||
}
|
||||
|
||||
this.tracker.impulse = false;
|
||||
}
|
||||
|
||||
++this.a;
|
||||
if (this.tracker.velocityChanged) {
|
||||
// CraftBukkit start - Create PlayerVelocity event
|
||||
boolean cancelled = false;
|
||||
|
||||
if (this.tracker instanceof EntityPlayer) {
|
||||
Player player = (Player) this.tracker.getBukkitEntity();
|
||||
org.bukkit.util.Vector velocity = player.getVelocity();
|
||||
|
||||
PlayerVelocityEvent event = new PlayerVelocityEvent(player, velocity.clone());
|
||||
this.tracker.world.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
cancelled = true;
|
||||
} else if (!velocity.equals(event.getVelocity())) {
|
||||
player.setVelocity(event.getVelocity());
|
||||
}
|
||||
}
|
||||
|
||||
if (!cancelled) {
|
||||
this.broadcastIncludingSelf(new PacketPlayOutEntityVelocity(this.tracker));
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.tracker.velocityChanged = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void d() {
|
||||
DataWatcher datawatcher = this.tracker.getDataWatcher();
|
||||
|
||||
if (datawatcher.a()) {
|
||||
this.broadcastIncludingSelf(new PacketPlayOutEntityMetadata(this.tracker.getId(), datawatcher, false));
|
||||
}
|
||||
|
||||
if (this.tracker instanceof EntityLiving) {
|
||||
AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap();
|
||||
Set set = attributemapserver.getAttributes();
|
||||
|
||||
if (!set.isEmpty()) {
|
||||
// CraftBukkit start - Send scaled max health
|
||||
if (this.tracker instanceof EntityPlayer) {
|
||||
((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(set, false);
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.broadcastIncludingSelf(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set));
|
||||
}
|
||||
|
||||
set.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void broadcast(Packet<?> packet) {
|
||||
Iterator iterator = this.trackedPlayers.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
|
||||
|
||||
entityplayer.playerConnection.sendPacket(packet);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void broadcastIncludingSelf(Packet<?> packet) {
|
||||
this.broadcast(packet);
|
||||
if (this.tracker instanceof EntityPlayer) {
|
||||
((EntityPlayer) this.tracker).playerConnection.sendPacket(packet);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a() {
|
||||
Iterator iterator = this.trackedPlayers.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
|
||||
|
||||
this.tracker.c(entityplayer);
|
||||
entityplayer.c(this.tracker);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(EntityPlayer entityplayer) {
|
||||
if (this.trackedPlayers.contains(entityplayer)) {
|
||||
this.tracker.c(entityplayer);
|
||||
entityplayer.c(this.tracker);
|
||||
this.trackedPlayers.remove(entityplayer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void updatePlayer(EntityPlayer entityplayer) {
|
||||
org.spigotmc.AsyncCatcher.catchOp( "player tracker update"); // Spigot
|
||||
if (entityplayer != this.tracker) {
|
||||
if (this.c(entityplayer)) {
|
||||
if (!this.trackedPlayers.contains(entityplayer) && (this.e(entityplayer) || this.tracker.attachedToPlayer)) {
|
||||
// CraftBukkit start - respect vanish API
|
||||
if (this.tracker instanceof EntityPlayer) {
|
||||
Player player = ((EntityPlayer) this.tracker).getBukkitEntity();
|
||||
if (!entityplayer.getBukkitEntity().canSee(player)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
entityplayer.removeQueue.remove(Integer.valueOf(this.tracker.getId()));
|
||||
// CraftBukkit end
|
||||
this.trackedPlayerMap.put(entityplayer, true); // Paper
|
||||
Packet packet = this.e();
|
||||
|
||||
entityplayer.playerConnection.sendPacket(packet);
|
||||
if (!this.tracker.getDataWatcher().d()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityMetadata(this.tracker.getId(), this.tracker.getDataWatcher(), true));
|
||||
}
|
||||
|
||||
boolean flag = this.u;
|
||||
|
||||
if (this.tracker instanceof EntityLiving) {
|
||||
AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap();
|
||||
Collection collection = attributemapserver.c();
|
||||
|
||||
// CraftBukkit start - If sending own attributes send scaled health instead of current maximum health
|
||||
if (this.tracker.getId() == entityplayer.getId()) {
|
||||
((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(collection, false);
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (!collection.isEmpty()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), collection));
|
||||
}
|
||||
|
||||
if (((EntityLiving) this.tracker).cP()) {
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.n = this.tracker.motX;
|
||||
this.o = this.tracker.motY;
|
||||
this.p = this.tracker.motZ;
|
||||
if (flag && !(packet instanceof PacketPlayOutSpawnEntityLiving)) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.tracker.motX, this.tracker.motY, this.tracker.motZ));
|
||||
}
|
||||
|
||||
if (this.tracker instanceof EntityLiving) {
|
||||
EnumItemSlot[] aenumitemslot = EnumItemSlot.values();
|
||||
int i = aenumitemslot.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumItemSlot enumitemslot = aenumitemslot[j];
|
||||
ItemStack itemstack = ((EntityLiving) this.tracker).getEquipment(enumitemslot);
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEquipment(this.tracker.getId(), enumitemslot, itemstack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.tracker instanceof EntityHuman) {
|
||||
EntityHuman entityhuman = (EntityHuman) this.tracker;
|
||||
|
||||
if (entityhuman.isSleeping()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutBed(entityhuman, new BlockPosition(this.tracker)));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.tracker instanceof EntityLiving) {
|
||||
// CraftBukkit start - Fix for nonsensical head yaw
|
||||
this.headYaw = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
|
||||
this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) headYaw));
|
||||
// CraftBukkit end
|
||||
EntityLiving entityliving = (EntityLiving) this.tracker;
|
||||
Iterator iterator = entityliving.getEffects().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
MobEffect mobeffect = (MobEffect) iterator.next();
|
||||
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.tracker.getId(), mobeffect));
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.tracker.bF().isEmpty()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker));
|
||||
}
|
||||
|
||||
if (this.tracker.isPassenger()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker.bJ()));
|
||||
}
|
||||
|
||||
this.tracker.b(entityplayer);
|
||||
entityplayer.d(this.tracker);
|
||||
updatePassengers(entityplayer); // Paper
|
||||
}
|
||||
} else if (this.trackedPlayers.contains(entityplayer)) {
|
||||
this.trackedPlayers.remove(entityplayer);
|
||||
this.tracker.c(entityplayer);
|
||||
entityplayer.c(this.tracker);
|
||||
updatePassengers(entityplayer); // Paper
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public boolean c(EntityPlayer entityplayer) {
|
||||
// Paper start
|
||||
if (tracker.isPassenger()) {
|
||||
return isTrackedBy(tracker.getVehicle(), entityplayer);
|
||||
} else if (hasPassengerInRange(tracker, entityplayer)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isInRangeOfPlayer(entityplayer);
|
||||
}
|
||||
private static boolean hasPassengerInRange(Entity entity, EntityPlayer entityplayer) {
|
||||
if (!entity.isVehicle()) {
|
||||
return false;
|
||||
}
|
||||
for (Entity passenger : entity.passengers) {
|
||||
if (passenger.tracker != null && passenger.tracker.isInRangeOfPlayer(entityplayer)) {
|
||||
return true;
|
||||
}
|
||||
if (passenger.isVehicle()) {
|
||||
if (hasPassengerInRange(passenger, entityplayer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private static boolean isTrackedBy(Entity entity, EntityPlayer entityplayer) {
|
||||
return entity == entityplayer || entity.tracker != null && entity.tracker.trackedPlayers.contains(entityplayer);
|
||||
}
|
||||
private void updatePassengers(EntityPlayer player) {
|
||||
if (tracker.isVehicle()) {
|
||||
tracker.passengers.forEach((e) -> {
|
||||
if (e.tracker != null) {
|
||||
e.tracker.updatePlayer(player);
|
||||
}
|
||||
});
|
||||
player.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker));
|
||||
}
|
||||
}
|
||||
private boolean isInRangeOfPlayer(EntityPlayer entityplayer) {
|
||||
// Paper end
|
||||
double d0 = entityplayer.locX - (double) this.xLoc / 4096.0D;
|
||||
double d1 = entityplayer.locZ - (double) this.zLoc / 4096.0D;
|
||||
int i = Math.min(this.e, this.f);
|
||||
|
||||
return d0 >= (double) (-i) && d0 <= (double) i && d1 >= (double) (-i) && d1 <= (double) i && this.tracker.a(entityplayer);
|
||||
}
|
||||
|
||||
private boolean e(EntityPlayer entityplayer) {
|
||||
return entityplayer.x().getPlayerChunkMap().a(entityplayer, this.tracker.ab, this.tracker.ad);
|
||||
}
|
||||
|
||||
public void scanPlayers(List<EntityHuman> list) {
|
||||
for (int i = 0; i < list.size(); ++i) {
|
||||
this.updatePlayer((EntityPlayer) list.get(i));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Packet<?> e() {
|
||||
if (this.tracker.dead) {
|
||||
// CraftBukkit start - Remove useless error spam, just return
|
||||
// EntityTrackerEntry.d.warn("Fetching addPacket for removed entity");
|
||||
return null;
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
if (this.tracker instanceof EntityPlayer) {
|
||||
return new PacketPlayOutNamedEntitySpawn((EntityHuman) this.tracker);
|
||||
} else if (this.tracker instanceof IAnimal) {
|
||||
this.headYaw = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
|
||||
return new PacketPlayOutSpawnEntityLiving((EntityLiving) this.tracker);
|
||||
} else if (this.tracker instanceof EntityPainting) {
|
||||
return new PacketPlayOutSpawnEntityPainting((EntityPainting) this.tracker);
|
||||
} else if (this.tracker instanceof EntityItem) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 2, 1);
|
||||
} else if (this.tracker instanceof EntityMinecartAbstract) {
|
||||
EntityMinecartAbstract entityminecartabstract = (EntityMinecartAbstract) this.tracker;
|
||||
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 10, entityminecartabstract.v().a());
|
||||
} else if (this.tracker instanceof EntityBoat) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 1);
|
||||
} else if (this.tracker instanceof EntityExperienceOrb) {
|
||||
return new PacketPlayOutSpawnEntityExperienceOrb((EntityExperienceOrb) this.tracker);
|
||||
} else if (this.tracker instanceof EntityFishingHook) {
|
||||
EntityHuman entityhuman = ((EntityFishingHook) this.tracker).l();
|
||||
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 90, entityhuman == null ? this.tracker.getId() : entityhuman.getId());
|
||||
} else {
|
||||
Entity entity;
|
||||
|
||||
if (this.tracker instanceof EntitySpectralArrow) {
|
||||
entity = ((EntitySpectralArrow) this.tracker).shooter;
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 91, 1 + (entity == null ? this.tracker.getId() : entity.getId()));
|
||||
} else if (this.tracker instanceof EntityTippedArrow) {
|
||||
entity = ((EntityArrow) this.tracker).shooter;
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 60, 1 + (entity == null ? this.tracker.getId() : entity.getId()));
|
||||
} else if (this.tracker instanceof EntitySnowball) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 61);
|
||||
} else if (this.tracker instanceof EntityLlamaSpit) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 68);
|
||||
} else if (this.tracker instanceof EntityPotion) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 73);
|
||||
} else if (this.tracker instanceof EntityThrownExpBottle) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 75);
|
||||
} else if (this.tracker instanceof EntityEnderPearl) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 65);
|
||||
} else if (this.tracker instanceof EntityEnderSignal) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 72);
|
||||
} else if (this.tracker instanceof EntityFireworks) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 76);
|
||||
} else if (this.tracker instanceof EntityFireball) {
|
||||
EntityFireball entityfireball = (EntityFireball) this.tracker;
|
||||
PacketPlayOutSpawnEntity packetplayoutspawnentity = null;
|
||||
byte b0 = 63;
|
||||
|
||||
if (this.tracker instanceof EntitySmallFireball) {
|
||||
b0 = 64;
|
||||
} else if (this.tracker instanceof EntityDragonFireball) {
|
||||
b0 = 93;
|
||||
} else if (this.tracker instanceof EntityWitherSkull) {
|
||||
b0 = 66;
|
||||
}
|
||||
|
||||
if (entityfireball.shooter != null) {
|
||||
packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, b0, ((EntityFireball) this.tracker).shooter.getId());
|
||||
} else {
|
||||
packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, b0, 0);
|
||||
}
|
||||
|
||||
packetplayoutspawnentity.a((int) (entityfireball.dirX * 8000.0D));
|
||||
packetplayoutspawnentity.b((int) (entityfireball.dirY * 8000.0D));
|
||||
packetplayoutspawnentity.c((int) (entityfireball.dirZ * 8000.0D));
|
||||
return packetplayoutspawnentity;
|
||||
} else if (this.tracker instanceof EntityShulkerBullet) {
|
||||
PacketPlayOutSpawnEntity packetplayoutspawnentity1 = new PacketPlayOutSpawnEntity(this.tracker, 67, 0);
|
||||
|
||||
packetplayoutspawnentity1.a((int) (this.tracker.motX * 8000.0D));
|
||||
packetplayoutspawnentity1.b((int) (this.tracker.motY * 8000.0D));
|
||||
packetplayoutspawnentity1.c((int) (this.tracker.motZ * 8000.0D));
|
||||
return packetplayoutspawnentity1;
|
||||
} else if (this.tracker instanceof EntityEgg) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 62);
|
||||
} else if (this.tracker instanceof EntityEvokerFangs) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 79);
|
||||
} else if (this.tracker instanceof EntityTNTPrimed) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 50);
|
||||
} else if (this.tracker instanceof EntityEnderCrystal) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 51);
|
||||
} else if (this.tracker instanceof EntityFallingBlock) {
|
||||
EntityFallingBlock entityfallingblock = (EntityFallingBlock) this.tracker;
|
||||
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 70, Block.getCombinedId(entityfallingblock.getBlock()));
|
||||
} else if (this.tracker instanceof EntityArmorStand) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 78);
|
||||
} else if (this.tracker instanceof EntityItemFrame) {
|
||||
EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker;
|
||||
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 71, entityitemframe.direction.get2DRotationValue(), entityitemframe.getBlockPosition());
|
||||
} else if (this.tracker instanceof EntityLeash) {
|
||||
EntityLeash entityleash = (EntityLeash) this.tracker;
|
||||
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 77, 0, entityleash.getBlockPosition());
|
||||
} else if (this.tracker instanceof EntityAreaEffectCloud) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 3);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don\'t know how to add " + this.tracker.getClass() + "!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear(EntityPlayer entityplayer) {
|
||||
org.spigotmc.AsyncCatcher.catchOp( "player tracker clear"); // Spigot
|
||||
if (this.trackedPlayers.contains(entityplayer)) {
|
||||
this.trackedPlayers.remove(entityplayer);
|
||||
this.tracker.c(entityplayer);
|
||||
entityplayer.c(this.tracker);
|
||||
updatePassengers(entityplayer); // Paper
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Entity b() {
|
||||
return this.tracker;
|
||||
}
|
||||
|
||||
public void a(int i) {
|
||||
this.f = i;
|
||||
}
|
||||
|
||||
public void c() {
|
||||
this.isMoving = false;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
try {
|
||||
Thread.sleep(this.e ? 0L : 2L);
|
||||
} catch (InterruptedException interruptedexception) {
|
||||
interruptedexception.printStackTrace();
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) { // Paper
|
||||
try {
|
||||
Thread.sleep(this.f ? 0L : 1L); // Paper
|
||||
} catch (InterruptedException interruptedexception) {
|
||||
interruptedexception.printStackTrace();
|
||||
}} // 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||
// CraftBukkit start
|
||||
import java.net.InetAddress;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
// CraftBukkit end
|
||||
|
||||
public class HandshakeListener implements PacketHandshakingInListener {
|
||||
|
||||
private static final com.google.gson.Gson gson = new com.google.gson.Gson(); // Spigot
|
||||
// CraftBukkit start - add fields
|
||||
private static final Object2LongOpenHashMap<InetAddress> throttleTracker = new Object2LongOpenHashMap<>();
|
||||
private static int throttleCounter = 0;
|
||||
// CraftBukkit end
|
||||
|
||||
private final MinecraftServer a;
|
||||
private final NetworkManager b;
|
||||
private NetworkManager getNetworkManager() { return b; } // Paper - OBFHELPER
|
||||
|
||||
public HandshakeListener(MinecraftServer minecraftserver, NetworkManager networkmanager) {
|
||||
this.a = minecraftserver;
|
||||
this.b = networkmanager;
|
||||
}
|
||||
|
||||
public void a(PacketHandshakingInSetProtocol packethandshakinginsetprotocol) {
|
||||
switch (packethandshakinginsetprotocol.a()) {
|
||||
case LOGIN:
|
||||
this.b.setProtocol(EnumProtocol.LOGIN);
|
||||
ChatMessage chatmessage;
|
||||
|
||||
// CraftBukkit start - Connection throttle
|
||||
try {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long connectionThrottle = MinecraftServer.getServer().server.getConnectionThrottle();
|
||||
InetAddress address = ((java.net.InetSocketAddress) this.b.getSocketAddress()).getAddress();
|
||||
|
||||
synchronized (throttleTracker) {
|
||||
if (throttleTracker.containsKey(address) && !"127.0.0.1".equals(address.getHostAddress()) && currentTime - throttleTracker.getLong(address) < connectionThrottle) {
|
||||
throttleTracker.put(address, currentTime);
|
||||
chatmessage = new ChatMessage("Connection throttled! Please wait before reconnecting.");
|
||||
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
|
||||
this.b.close(chatmessage);
|
||||
return;
|
||||
}
|
||||
|
||||
throttleTracker.put(address, currentTime);
|
||||
throttleCounter++;
|
||||
if (throttleCounter > 200) {
|
||||
throttleCounter = 0;
|
||||
|
||||
// Cleanup stale entries
|
||||
throttleTracker.object2LongEntrySet().removeIf(entry -> entry.getLongValue() > connectionThrottle); // Dionysus
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
org.apache.logging.log4j.LogManager.getLogger().debug("Failed to check connection throttle", t);
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (packethandshakinginsetprotocol.b() > 340) {
|
||||
chatmessage = new ChatMessage( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedServerMessage.replaceAll("'", "''"), "1.12.2" ) ); // Spigot
|
||||
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
|
||||
this.b.close(chatmessage);
|
||||
} else if (packethandshakinginsetprotocol.b() < 340) {
|
||||
chatmessage = new ChatMessage( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedClientMessage.replaceAll("'", "''"), "1.12.2" ) ); // Spigot
|
||||
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
|
||||
this.b.close(chatmessage);
|
||||
} else {
|
||||
this.b.setPacketListener(new LoginListener(this.a, this.b));
|
||||
// Paper start - handshake event
|
||||
boolean proxyLogicEnabled = org.spigotmc.SpigotConfig.bungee;
|
||||
boolean handledByEvent = false;
|
||||
// Try and handle the handshake through the event
|
||||
if (com.destroystokyo.paper.event.player.PlayerHandshakeEvent.getHandlerList().getRegisteredListeners().length != 0) { // Hello? Can you hear me?
|
||||
com.destroystokyo.paper.event.player.PlayerHandshakeEvent event = new com.destroystokyo.paper.event.player.PlayerHandshakeEvent(packethandshakinginsetprotocol.hostname, !proxyLogicEnabled);
|
||||
if (event.callEvent()) {
|
||||
// If we've failed somehow, let the client know so and go no further.
|
||||
if (event.isFailed()) {
|
||||
chatmessage = new ChatMessage(event.getFailMessage());
|
||||
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
|
||||
this.b.close(chatmessage);
|
||||
return;
|
||||
}
|
||||
|
||||
packethandshakinginsetprotocol.hostname = event.getServerHostname();
|
||||
this.b.l = new java.net.InetSocketAddress(event.getSocketAddressHostname(), ((java.net.InetSocketAddress) this.b.getSocketAddress()).getPort());
|
||||
this.b.spoofedUUID = event.getUniqueId();
|
||||
this.b.spoofedProfile = gson.fromJson(event.getPropertiesJson(), com.mojang.authlib.properties.Property[].class);
|
||||
handledByEvent = true; // Hooray, we did it!
|
||||
}
|
||||
}
|
||||
// Don't try and handle default logic if it's been handled by the event.
|
||||
if (!handledByEvent && proxyLogicEnabled) {
|
||||
// Paper end
|
||||
// Spigot Start
|
||||
//if (org.spigotmc.SpigotConfig.bungee) { // Paper - comment out, we check above!
|
||||
String[] split = packethandshakinginsetprotocol.hostname.split("\00");
|
||||
if ( split.length == 3 || split.length == 4 ) {
|
||||
packethandshakinginsetprotocol.hostname = split[0];
|
||||
b.l = new java.net.InetSocketAddress(split[1], ((java.net.InetSocketAddress) b.getSocketAddress()).getPort());
|
||||
b.spoofedUUID = com.mojang.util.UUIDTypeAdapter.fromString( split[2] );
|
||||
} else
|
||||
{
|
||||
chatmessage = new ChatMessage("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!");
|
||||
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
|
||||
this.b.close(chatmessage);
|
||||
return;
|
||||
}
|
||||
if ( split.length == 4 )
|
||||
{
|
||||
b.spoofedProfile = gson.fromJson(split[3], com.mojang.authlib.properties.Property[].class);
|
||||
}
|
||||
}
|
||||
// Spigot End
|
||||
((LoginListener) this.b.i()).hostname = packethandshakinginsetprotocol.hostname + ":" + packethandshakinginsetprotocol.port; // CraftBukkit - set hostname
|
||||
}
|
||||
break;
|
||||
|
||||
case STATUS:
|
||||
this.b.setProtocol(EnumProtocol.STATUS);
|
||||
this.b.setPacketListener(new PacketStatusListener(this.a, this.b));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new UnsupportedOperationException("Invalid intention " + packethandshakinginsetprotocol.a());
|
||||
}
|
||||
|
||||
// Paper start - NetworkClient implementation
|
||||
this.getNetworkManager().protocolVersion = packethandshakinginsetprotocol.getProtocolVersion();
|
||||
this.getNetworkManager().virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(packethandshakinginsetprotocol.hostname, packethandshakinginsetprotocol.port);
|
||||
// Paper end
|
||||
}
|
||||
|
||||
public void a(IChatBaseComponent ichatbasecomponent) {}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
// NeonPaper start
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
// NeonPaper end
|
||||
|
||||
public class IntCache {
|
||||
// NeonPaper start - Refactored IntCache to be thread local instead of static
|
||||
private static final ThreadLocal<IntCache> caches = new ThreadLocal<IntCache>() {
|
||||
@Override
|
||||
protected IntCache initialValue() {
|
||||
IntCache cache = new IntCache();
|
||||
synchronized (ALL_CACHES) {
|
||||
ALL_CACHES.add(new WeakReference<>(cache));
|
||||
}
|
||||
return new IntCache();
|
||||
}
|
||||
};
|
||||
|
||||
private static final List<WeakReference<IntCache>> ALL_CACHES = new ObjectArrayList<>();
|
||||
|
||||
private int a = 256;
|
||||
private final List<int[]> b = new ObjectArrayList<>();
|
||||
private final List<int[]> c = new ObjectArrayList<>();
|
||||
private final List<int[]> d = new ObjectArrayList<>();
|
||||
private final List<int[]> e = new ObjectArrayList<>();
|
||||
|
||||
private final int cacheLimit = org.spigotmc.SpigotConfig.intCacheLimit;
|
||||
|
||||
public static int[] a(int i) {
|
||||
return caches.get().aNonStatic(i);
|
||||
}
|
||||
|
||||
public int[] aNonStatic(int i) {
|
||||
int[] aint;
|
||||
|
||||
if (i <= 256) {
|
||||
if (this.b.isEmpty()) {
|
||||
aint = new int[256];
|
||||
if (c.size() < cacheLimit) this.c.add(aint);
|
||||
return aint;
|
||||
} else {
|
||||
aint = this.b.remove(this.b.size() - 1);
|
||||
if (c.size() < cacheLimit) this.c.add(aint);
|
||||
return aint;
|
||||
}
|
||||
} else if (i > this.a) {
|
||||
this.a = i;
|
||||
this.d.clear();
|
||||
this.e.clear();
|
||||
aint = new int[this.a];
|
||||
if (e.size() < cacheLimit) this.e.add(aint);
|
||||
return aint;
|
||||
} else if (this.d.isEmpty()) {
|
||||
aint = new int[this.a];
|
||||
if (e.size() < cacheLimit) this.e.add(aint);
|
||||
return aint;
|
||||
} else {
|
||||
aint = this.d.remove(this.d.size() - 1);
|
||||
if (e.size() < cacheLimit) this.e.add(aint);
|
||||
return aint;
|
||||
}
|
||||
}
|
||||
|
||||
public static void a() {
|
||||
caches.get().aNonStatic();
|
||||
}
|
||||
|
||||
public void aNonStatic() {
|
||||
if (!this.d.isEmpty()) {
|
||||
this.d.remove(this.d.size() - 1);
|
||||
}
|
||||
|
||||
if (!this.b.isEmpty()) {
|
||||
this.b.remove(this.b.size() - 1);
|
||||
}
|
||||
|
||||
this.d.addAll(this.e);
|
||||
this.b.addAll(this.c);
|
||||
this.e.clear();
|
||||
this.c.clear();
|
||||
}
|
||||
|
||||
public static String b() {
|
||||
int cache = 0;
|
||||
int tcache = 0;
|
||||
int allocated = 0;
|
||||
int tallocated = 0;
|
||||
int numberOfCaches;
|
||||
|
||||
synchronized (ALL_CACHES) {
|
||||
numberOfCaches = ALL_CACHES.size();
|
||||
Iterator<WeakReference<IntCache>> iter = ALL_CACHES.iterator();
|
||||
while (iter.hasNext()) {
|
||||
WeakReference<IntCache> reference = iter.next();
|
||||
IntCache intcache = reference.get();
|
||||
if (intcache != null) {
|
||||
cache += intcache.d.size();
|
||||
tcache += intcache.b.size();
|
||||
allocated += intcache.e.size();
|
||||
tallocated += intcache.c.size();
|
||||
} else {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
return numberOfCaches + " IntCaches. In Total => cache: " + cache + ", tcache: " + tcache + ", allocated: " + allocated + ", tallocated: " + tallocated;
|
||||
}
|
||||
// NeonPaper end
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,121 +1,81 @@
|
||||
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));
|
||||
tileentity.update();
|
||||
world.notify(blockposition, iblockdata, iblockdata, 3);
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack.subtract(1);
|
||||
if (entitytypes != null) {
|
||||
mobspawnerabstract.setMobName(entitytypes);
|
||||
tileentity.update();
|
||||
world.notify(blockposition, iblockdata, iblockdata, 3);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
if (iblockdata.h(world, blockposition).b()) {
|
||||
blockposition1 = blockposition;
|
||||
} else {
|
||||
blockposition1 = blockposition.shift(enumdirection);
|
||||
}
|
||||
|
||||
a(world, entityhuman, itemstack, entity);
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack.subtract(1);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
} else {
|
||||
double d0 = axisalignedbb.b;
|
||||
|
||||
AxisAlignedBB axisalignedbb1;
|
||||
|
||||
for (Iterator iterator = list.iterator(); iterator.hasNext(); d0 = Math.max(axisalignedbb1.e, d0)) {
|
||||
axisalignedbb1 = (AxisAlignedBB) iterator.next();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public InteractionResultWrapper<ItemStack> a(World world, EntityHuman entityhuman, EnumHand enumhand) {
|
||||
ItemStack itemstack = entityhuman.b(enumhand);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
return minecraftkey;
|
||||
if (nbttagcompound1.hasKeyOfType("id", 8)) {
|
||||
return EntityTypes.a(nbttagcompound1.getString("id"));
|
||||
}
|
||||
}
|
||||
|
||||
return this.d;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,367 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Multisets;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.server.MapInitializeEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class ItemWorldMap extends ItemWorldMapBase {
|
||||
|
||||
protected ItemWorldMap() {
|
||||
this.a(true);
|
||||
}
|
||||
|
||||
public static ItemStack a(World world, double d0, double d1, byte b0, boolean flag, boolean flag1) {
|
||||
World worldMain = world.getServer().getServer().worlds.get(0); // CraftBukkit - store reference to primary world
|
||||
ItemStack itemstack = new ItemStack(Items.FILLED_MAP, 1, worldMain.b("map")); // CraftBukkit - use primary world for maps
|
||||
String s = "map_" + itemstack.getData();
|
||||
WorldMap worldmap = new WorldMap(s);
|
||||
|
||||
worldMain.a(s, (PersistentBase) worldmap); // CraftBukkit
|
||||
worldmap.scale = b0;
|
||||
worldmap.a(d0, d1, worldmap.scale);
|
||||
worldmap.map = (byte) ((WorldServer) world).dimension; // CraftBukkit - use bukkit dimension
|
||||
worldmap.track = flag;
|
||||
worldmap.unlimitedTracking = flag1;
|
||||
worldmap.c();
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.callEvent(new org.bukkit.event.server.MapInitializeEvent(worldmap.mapView)); // CraftBukkit
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WorldMap getSavedMap(ItemStack itemstack, World world) {
|
||||
World worldMain = world.getServer().getServer().worlds.get(0); // CraftBukkit - store reference to primary world
|
||||
String s = "map_" + itemstack.getData();
|
||||
WorldMap worldmap = (WorldMap) worldMain.a(WorldMap.class, s); // CraftBukkit - use primary world for maps
|
||||
|
||||
if (worldmap == null && !world.isClientSide) {
|
||||
itemstack.setData(worldMain.b("map")); // CraftBukkit - use primary world for maps
|
||||
s = "map_" + itemstack.getData();
|
||||
worldmap = new WorldMap(s);
|
||||
worldmap.scale = 3;
|
||||
worldmap.a((double) world.getWorldData().b(), (double) world.getWorldData().d(), worldmap.scale);
|
||||
worldmap.map = (byte) ((WorldServer) world).dimension; // CraftBukkit - fixes Bukkit multiworld maps
|
||||
worldmap.c();
|
||||
worldMain.a(s, (PersistentBase) worldmap); // CraftBukkit - use primary world for maps
|
||||
|
||||
// CraftBukkit start
|
||||
MapInitializeEvent event = new MapInitializeEvent(worldmap.mapView);
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
return worldmap;
|
||||
}
|
||||
|
||||
public void a(World world, Entity entity, WorldMap worldmap) {
|
||||
// CraftBukkit - world.worldProvider -> ((WorldServer) world)
|
||||
if (((WorldServer) world).dimension == worldmap.map && entity instanceof EntityHuman) {
|
||||
int i = 1 << worldmap.scale;
|
||||
int j = worldmap.centerX;
|
||||
int k = worldmap.centerZ;
|
||||
int l = MathHelper.floor(entity.locX - (double) j) / i + 64;
|
||||
int i1 = MathHelper.floor(entity.locZ - (double) k) / i + 64;
|
||||
int j1 = 128 / i;
|
||||
|
||||
if (world.worldProvider.n()) {
|
||||
j1 /= 2;
|
||||
}
|
||||
|
||||
WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = worldmap.a((EntityHuman) entity);
|
||||
|
||||
++worldmap_worldmaphumantracker.b;
|
||||
boolean flag = false;
|
||||
|
||||
for (int k1 = l - j1 + 1; k1 < l + j1; ++k1) {
|
||||
if ((k1 & 15) == (worldmap_worldmaphumantracker.b & 15) || flag) {
|
||||
flag = false;
|
||||
double d0 = 0.0D;
|
||||
|
||||
for (int l1 = i1 - j1 - 1; l1 < i1 + j1; ++l1) {
|
||||
if (k1 >= 0 && l1 >= -1 && k1 < 128 && l1 < 128) {
|
||||
int i2 = k1 - l;
|
||||
int j2 = l1 - i1;
|
||||
boolean flag1 = i2 * i2 + j2 * j2 > (j1 - 2) * (j1 - 2);
|
||||
int k2 = (j / i + k1 - 64) * i;
|
||||
int l2 = (k / i + l1 - 64) * i;
|
||||
HashMultiset hashmultiset = HashMultiset.create();
|
||||
Chunk chunk = world.getChunkIfLoaded(new BlockPosition(k2, 0, l2)); // NeonPaper - Maps shouldn't load chunks
|
||||
|
||||
if (chunk != null && !chunk.isEmpty()) { // NeonPaper - Maps shouldn't load chunks
|
||||
int i3 = k2 & 15;
|
||||
int j3 = l2 & 15;
|
||||
int k3 = 0;
|
||||
double d1 = 0.0D;
|
||||
|
||||
if (world.worldProvider.n()) {
|
||||
int l3 = k2 + l2 * 231871;
|
||||
|
||||
l3 = l3 * l3 * 31287121 + l3 * 11;
|
||||
if ((l3 >> 20 & 1) == 0) {
|
||||
hashmultiset.add(Blocks.DIRT.getBlockData().set(BlockDirt.VARIANT, BlockDirt.EnumDirtVariant.DIRT).a((IBlockAccess) world, BlockPosition.ZERO), 10);
|
||||
} else {
|
||||
hashmultiset.add(Blocks.STONE.getBlockData().set(BlockStone.VARIANT, BlockStone.EnumStoneVariant.STONE).a((IBlockAccess) world, BlockPosition.ZERO), 100);
|
||||
}
|
||||
|
||||
d1 = 100.0D;
|
||||
} else {
|
||||
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
|
||||
|
||||
for (int i4 = 0; i4 < i; ++i4) {
|
||||
for (int j4 = 0; j4 < i; ++j4) {
|
||||
int k4 = chunk.b(i4 + i3, j4 + j3) + 1;
|
||||
IBlockData iblockdata = Blocks.AIR.getBlockData();
|
||||
|
||||
if (k4 > 1) {
|
||||
do {
|
||||
--k4;
|
||||
iblockdata = chunk.a(i4 + i3, k4, j4 + j3);
|
||||
blockposition_mutableblockposition.c((chunk.locX << 4) + i4 + i3, k4, (chunk.locZ << 4) + j4 + j3);
|
||||
} while (iblockdata.a((IBlockAccess) world, blockposition_mutableblockposition) == MaterialMapColor.c && k4 > 0);
|
||||
|
||||
if (k4 > 0 && iblockdata.getMaterial().isLiquid()) {
|
||||
int l4 = k4 - 1;
|
||||
|
||||
IBlockData iblockdata1;
|
||||
|
||||
do {
|
||||
iblockdata1 = chunk.a(i4 + i3, l4--, j4 + j3);
|
||||
++k3;
|
||||
} while (l4 > 0 && iblockdata1.getMaterial().isLiquid());
|
||||
}
|
||||
} else {
|
||||
iblockdata = Blocks.BEDROCK.getBlockData();
|
||||
}
|
||||
|
||||
d1 += (double) k4 / (double) (i * i);
|
||||
hashmultiset.add(iblockdata.a((IBlockAccess) world, blockposition_mutableblockposition));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
k3 /= i * i;
|
||||
double d2 = (d1 - d0) * 4.0D / (double) (i + 4) + ((double) (k1 + l1 & 1) - 0.5D) * 0.4D;
|
||||
byte b0 = 1;
|
||||
|
||||
if (d2 > 0.6D) {
|
||||
b0 = 2;
|
||||
}
|
||||
|
||||
if (d2 < -0.6D) {
|
||||
b0 = 0;
|
||||
}
|
||||
|
||||
MaterialMapColor materialmapcolor = (MaterialMapColor) Iterables.getFirst(Multisets.copyHighestCountFirst(hashmultiset), MaterialMapColor.c);
|
||||
|
||||
if (materialmapcolor == MaterialMapColor.o) {
|
||||
d2 = (double) k3 * 0.1D + (double) (k1 + l1 & 1) * 0.2D;
|
||||
b0 = 1;
|
||||
if (d2 < 0.5D) {
|
||||
b0 = 2;
|
||||
}
|
||||
|
||||
if (d2 > 0.9D) {
|
||||
b0 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
d0 = d1;
|
||||
if (l1 >= 0 && i2 * i2 + j2 * j2 < j1 * j1 && (!flag1 || (k1 + l1 & 1) != 0)) {
|
||||
byte b1 = worldmap.colors[k1 + l1 * 128];
|
||||
byte b2 = (byte) (materialmapcolor.ad * 4 + b0);
|
||||
|
||||
if (b1 != b2) {
|
||||
worldmap.colors[k1 + l1 * 128] = b2;
|
||||
worldmap.flagDirty(k1, l1);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void a(World world, ItemStack itemstack) {
|
||||
if (itemstack.getItem() == Items.FILLED_MAP) {
|
||||
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, world);
|
||||
|
||||
if (worldmap != null) {
|
||||
if (world.worldProvider.getDimensionManager().getDimensionID() == worldmap.map) {
|
||||
int i = 1 << worldmap.scale;
|
||||
int j = worldmap.centerX;
|
||||
int k = worldmap.centerZ;
|
||||
BiomeBase[] abiomebase = world.getWorldChunkManager().a((BiomeBase[]) null, (j / i - 64) * i, (k / i - 64) * i, 128 * i, 128 * i, false);
|
||||
|
||||
for (int l = 0; l < 128; ++l) {
|
||||
for (int i1 = 0; i1 < 128; ++i1) {
|
||||
int j1 = l * i;
|
||||
int k1 = i1 * i;
|
||||
BiomeBase biomebase = abiomebase[j1 + k1 * 128 * i];
|
||||
MaterialMapColor materialmapcolor = MaterialMapColor.c;
|
||||
int l1 = 3;
|
||||
int i2 = 8;
|
||||
|
||||
if (l > 0 && i1 > 0 && l < 127 && i1 < 127) {
|
||||
if (abiomebase[(l - 1) * i + (i1 - 1) * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[(l - 1) * i + (i1 + 1) * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[(l - 1) * i + i1 * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[(l + 1) * i + (i1 - 1) * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[(l + 1) * i + (i1 + 1) * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[(l + 1) * i + i1 * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[l * i + (i1 - 1) * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[l * i + (i1 + 1) * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (biomebase.j() < 0.0F) {
|
||||
materialmapcolor = MaterialMapColor.r;
|
||||
if (i2 > 7 && i1 % 2 == 0) {
|
||||
l1 = (l + (int) (MathHelper.sin((float) i1 + 0.0F) * 7.0F)) / 8 % 5;
|
||||
if (l1 == 3) {
|
||||
l1 = 1;
|
||||
} else if (l1 == 4) {
|
||||
l1 = 0;
|
||||
}
|
||||
} else if (i2 > 7) {
|
||||
materialmapcolor = MaterialMapColor.c;
|
||||
} else if (i2 > 5) {
|
||||
l1 = 1;
|
||||
} else if (i2 > 3) {
|
||||
l1 = 0;
|
||||
} else if (i2 > 1) {
|
||||
l1 = 0;
|
||||
}
|
||||
} else if (i2 > 0) {
|
||||
materialmapcolor = MaterialMapColor.C;
|
||||
if (i2 > 3) {
|
||||
l1 = 1;
|
||||
} else {
|
||||
l1 = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (materialmapcolor != MaterialMapColor.c) {
|
||||
worldmap.colors[l + i1 * 128] = (byte) (materialmapcolor.ad * 4 + l1);
|
||||
worldmap.flagDirty(l, i1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void a(ItemStack itemstack, World world, Entity entity, int i, boolean flag) {
|
||||
if (!world.isClientSide) {
|
||||
WorldMap worldmap = this.getSavedMap(itemstack, world);
|
||||
|
||||
if (entity instanceof EntityHuman) {
|
||||
EntityHuman entityhuman = (EntityHuman) entity;
|
||||
|
||||
worldmap.a(entityhuman, itemstack);
|
||||
}
|
||||
|
||||
if (flag || entity instanceof EntityHuman && ((EntityHuman) entity).getItemInOffHand() == itemstack) {
|
||||
this.a(world, entity, worldmap);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Packet<?> a(ItemStack itemstack, World world, EntityHuman entityhuman) {
|
||||
return this.getSavedMap(itemstack, world).a(itemstack, world, entityhuman);
|
||||
}
|
||||
|
||||
public void b(ItemStack itemstack, World world, EntityHuman entityhuman) {
|
||||
NBTTagCompound nbttagcompound = itemstack.getTag();
|
||||
|
||||
if (nbttagcompound != null) {
|
||||
if (nbttagcompound.hasKeyOfType("map_scale_direction", 99)) {
|
||||
a(itemstack, world, nbttagcompound.getInt("map_scale_direction"));
|
||||
nbttagcompound.remove("map_scale_direction");
|
||||
} else if (nbttagcompound.getBoolean("map_tracking_position")) {
|
||||
b(itemstack, world);
|
||||
nbttagcompound.remove("map_tracking_position");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static void a(ItemStack itemstack, World world, int i) {
|
||||
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, world);
|
||||
|
||||
world = world.getServer().getServer().worlds.get(0); // CraftBukkit - use primary world for maps
|
||||
itemstack.setData(world.b("map"));
|
||||
WorldMap worldmap1 = new WorldMap("map_" + itemstack.getData());
|
||||
|
||||
if (worldmap != null) {
|
||||
worldmap1.scale = (byte) MathHelper.clamp(worldmap.scale + i, 0, 4);
|
||||
worldmap1.track = worldmap.track;
|
||||
worldmap1.a((double) worldmap.centerX, (double) worldmap.centerZ, worldmap1.scale);
|
||||
worldmap1.map = worldmap.map;
|
||||
worldmap1.c();
|
||||
world.a("map_" + itemstack.getData(), (PersistentBase) worldmap1);
|
||||
// CraftBukkit start
|
||||
MapInitializeEvent event = new MapInitializeEvent(worldmap1.mapView);
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static void b(ItemStack itemstack, World world) {
|
||||
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, world);
|
||||
|
||||
world = world.getServer().getServer().worlds.get(0); // CraftBukkit - use primary world for maps
|
||||
itemstack.setData(world.b("map"));
|
||||
WorldMap worldmap1 = new WorldMap("map_" + itemstack.getData());
|
||||
|
||||
worldmap1.track = true;
|
||||
if (worldmap != null) {
|
||||
worldmap1.centerX = worldmap.centerX;
|
||||
worldmap1.centerZ = worldmap.centerZ;
|
||||
worldmap1.scale = worldmap.scale;
|
||||
worldmap1.map = worldmap.map;
|
||||
worldmap1.c();
|
||||
world.a("map_" + itemstack.getData(), (PersistentBase) worldmap1);
|
||||
// CraftBukkit start
|
||||
MapInitializeEvent event = new MapInitializeEvent(worldmap1.mapView);
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class MethodProfiler {
|
||||
|
||||
public static final boolean ENABLED = Boolean.getBoolean("enableDebugMethodProfiler"); // CraftBukkit - disable unless specified in JVM arguments
|
||||
private static final Logger b = LogManager.getLogger();
|
||||
private final ObjectArrayList<String> c = new ObjectArrayList<>(); // Dionysus
|
||||
private final LongArrayList d = new LongArrayList(); // Dionysus
|
||||
public boolean a;
|
||||
private String e = "";
|
||||
private final Object2LongOpenHashMap<String> f = new Object2LongOpenHashMap<>();
|
||||
|
||||
public MethodProfiler() {}
|
||||
|
||||
public void a() {
|
||||
if (!ENABLED) return; // CraftBukkit
|
||||
this.f.clear();
|
||||
this.e = "";
|
||||
this.c.clear();
|
||||
}
|
||||
|
||||
public void a(String s) {
|
||||
if (!ENABLED) return; // CraftBukkit
|
||||
if (this.a) {
|
||||
if (!this.e.isEmpty()) {
|
||||
this.e = this.e + ".";
|
||||
}
|
||||
|
||||
this.e = this.e + s;
|
||||
this.c.add(this.e);
|
||||
this.d.add(Long.valueOf(System.nanoTime()));
|
||||
}
|
||||
}
|
||||
|
||||
public void a(Supplier<String> supplier) {
|
||||
if (!ENABLED) return; // CraftBukkit
|
||||
if (this.a) {
|
||||
this.a((String) supplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
public void b() {
|
||||
if (!ENABLED) return; // CraftBukkit
|
||||
if (this.a) {
|
||||
long i = System.nanoTime();
|
||||
long j = this.d.removeLong(this.d.size() - 1);
|
||||
|
||||
this.c.remove(this.c.size() - 1);
|
||||
long k = i - j;
|
||||
|
||||
if (this.f.containsKey(this.e)) {
|
||||
this.f.put(this.e, this.f.get(this.e) + k);
|
||||
} else {
|
||||
this.f.put(this.e, k);
|
||||
}
|
||||
|
||||
if (k > 100000000L) {
|
||||
MethodProfiler.b.warn("Something\'s taking too long! \'{}\' took aprox {} ms", this.e, Double.valueOf((double) k / 1000000.0D));
|
||||
}
|
||||
|
||||
this.e = this.c.isEmpty() ? "" : (String) this.c.get(this.c.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public List<MethodProfiler.ProfilerInfo> b(String s) {
|
||||
if (!ENABLED || !this.a) { // CraftBukkit
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
long i = this.f.getOrDefault("root", 0L);
|
||||
long j = this.f.getOrDefault(s, -1L);
|
||||
ArrayList<MethodProfiler.ProfilerInfo> arraylist = Lists.newArrayList();
|
||||
|
||||
if (!s.isEmpty()) {
|
||||
s = s + ".";
|
||||
}
|
||||
|
||||
long k = 0L;
|
||||
for (String s1 : this.f.keySet()) {
|
||||
if (s1.length() > s.length() && s1.startsWith(s) && s1.indexOf(".", s.length() + 1) < 0) {
|
||||
k += this.f.getLong(s1);
|
||||
}
|
||||
}
|
||||
|
||||
float f = (float) k;
|
||||
|
||||
if (k < j) {
|
||||
k = j;
|
||||
}
|
||||
|
||||
if (i < k) {
|
||||
i = k;
|
||||
}
|
||||
|
||||
for (Object2LongMap.Entry<String> entry : this.f.object2LongEntrySet()) {
|
||||
String s2 = entry.getKey();
|
||||
if (s2.length() > s.length() && s2.startsWith(s) && s2.indexOf(".", s.length() + 1) < 0) {
|
||||
long l = this.f.getLong(s2);
|
||||
double d0 = (double) l * 100.0D / (double) k;
|
||||
double d1 = (double) l * 100.0D / (double) i;
|
||||
String s3 = s2.substring(s.length());
|
||||
|
||||
arraylist.add(new MethodProfiler.ProfilerInfo(s3, d0, d1));
|
||||
}
|
||||
entry.setValue(entry.getLongValue() * 999L / 1000L);
|
||||
}
|
||||
|
||||
if ((float) k > f) {
|
||||
arraylist.add(new MethodProfiler.ProfilerInfo("unspecified", (double) ((float) k - f) * 100.0D / (double) k, (double) ((float) k - f) * 100.0D / (double) i));
|
||||
}
|
||||
|
||||
Collections.sort(arraylist);
|
||||
arraylist.add(0, new MethodProfiler.ProfilerInfo(s, 100.0D, (double) k * 100.0D / (double) i));
|
||||
return arraylist;
|
||||
}
|
||||
}
|
||||
|
||||
public void c(String s) {
|
||||
if (!ENABLED) return; // CraftBukkit
|
||||
this.b();
|
||||
this.a(s);
|
||||
}
|
||||
|
||||
public String c() {
|
||||
if (!ENABLED) return "[DISABLED]"; // CraftBukkit
|
||||
return this.c.isEmpty() ? "[UNKNOWN]" : (String) this.c.get(this.c.size() - 1);
|
||||
}
|
||||
|
||||
public static final class ProfilerInfo implements Comparable<MethodProfiler.ProfilerInfo> {
|
||||
|
||||
public double a;
|
||||
public double b;
|
||||
public String c;
|
||||
|
||||
public ProfilerInfo(String s, double d0, double d1) {
|
||||
this.c = s;
|
||||
this.a = d0;
|
||||
this.b = d1;
|
||||
}
|
||||
|
||||
public int a(MethodProfiler.ProfilerInfo methodprofiler_profilerinfo) {
|
||||
return methodprofiler_profilerinfo.a < this.a ? -1 : (methodprofiler_profilerinfo.a > this.a ? 1 : methodprofiler_profilerinfo.c.compareTo(this.c));
|
||||
}
|
||||
|
||||
public int compareTo(MethodProfiler.ProfilerInfo object) { // CraftBukkit: decompile error
|
||||
return this.a((MethodProfiler.ProfilerInfo) object);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,16 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
|
||||
import io.akarin.api.internal.utils.CheckedConcurrentLinkedQueue;
|
||||
|
||||
import com.google.common.collect.Queues;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
|
||||
|
||||
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;
|
||||
@@ -21,20 +19,17 @@ import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
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;
|
||||
import org.apache.logging.log4j.Marker;
|
||||
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,35 +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() {
|
||||
return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build());
|
||||
}
|
||||
|
||||
protected Object init() {
|
||||
return this.a();
|
||||
}
|
||||
};
|
||||
public static final LazyInitVar<EpollEventLoopGroup> e = new LazyInitVar() {
|
||||
protected EpollEventLoopGroup a() {
|
||||
return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).build());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
protected Object init() {
|
||||
return this.a();
|
||||
}
|
||||
};
|
||||
public static final LazyInitVar<NioEventLoopGroup> d = new LazyInitVar(() -> {
|
||||
return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build());
|
||||
});
|
||||
public static final LazyInitVar<EpollEventLoopGroup> e = new LazyInitVar(() -> {
|
||||
return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).build());
|
||||
});
|
||||
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
|
||||
@@ -83,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;
|
||||
@@ -119,40 +102,54 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
this.close(new ChatMessage("disconnect.endOfStream", new Object[0]));
|
||||
}
|
||||
|
||||
public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) throws Exception {
|
||||
// Paper start
|
||||
if (throwable instanceof io.netty.handler.codec.EncoderException && throwable.getCause() instanceof PacketEncoder.PacketTooLargeException) {
|
||||
if (((PacketEncoder.PacketTooLargeException) throwable.getCause()).getPacket().packetTooLarge(this)) {
|
||||
return;
|
||||
} else {
|
||||
throwable = throwable.getCause();
|
||||
public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) {
|
||||
if (throwable instanceof SkipEncodeException) {
|
||||
NetworkManager.g.debug("Skipping packet due to errors", throwable.getCause());
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
ChatMessage chatmessage;
|
||||
|
||||
if (throwable instanceof TimeoutException) {
|
||||
chatmessage = new ChatMessage("disconnect.timeout", new Object[0]);
|
||||
} else {
|
||||
chatmessage = new ChatMessage("disconnect.genericReason", new Object[] { "Internal Exception: " + throwable});
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -160,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();
|
||||
}
|
||||
@@ -175,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);
|
||||
@@ -208,43 +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() {
|
||||
public void run() {
|
||||
if (enumprotocol != enumprotocol1) {
|
||||
NetworkManager.this.setProtocol(enumprotocol);
|
||||
}
|
||||
|
||||
ChannelFuture channelfuture = NetworkManager.this.channel.writeAndFlush(packet);
|
||||
|
||||
if (agenericfuturelistener != null) {
|
||||
channelfuture.addListeners(agenericfuturelistener);
|
||||
}
|
||||
|
||||
channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
this.channel.eventLoop().execute(() -> {
|
||||
if (enumprotocol != enumprotocol1) {
|
||||
this.setProtocol(enumprotocol);
|
||||
}
|
||||
|
||||
ChannelFuture channelfuture = this.channel.writeAndFlush(packet);
|
||||
|
||||
if (genericfuturelistener != null) {
|
||||
channelfuture.addListener(genericfuturelistener);
|
||||
}
|
||||
|
||||
channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
});
|
||||
}
|
||||
|
||||
// Paper start
|
||||
java.util.List<Packet> extraPackets = packet.getExtraPackets();
|
||||
if (extraPackets != null && !extraPackets.isEmpty()) {
|
||||
for (Packet extraPacket : extraPackets) {
|
||||
this.dispatchPacket(extraPacket, agenericfuturelistener);
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -254,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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,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() {
|
||||
@@ -324,6 +306,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
return this.m;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IChatBaseComponent j() {
|
||||
return this.n;
|
||||
}
|
||||
@@ -375,20 +358,18 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
}
|
||||
|
||||
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet object) throws Exception { // CraftBukkit - fix decompile error
|
||||
// FlamePaper - Check if channel is opened before reading packet
|
||||
if (isConnected()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
return entityplayer != null && !entityplayer.isSpectator();
|
||||
}
|
||||
|
||||
public boolean apply(@Nullable Object object) {
|
||||
return this.a((EntityPlayer) object);
|
||||
}
|
||||
private static final Predicate<EntityPlayer> a = (entityplayer) -> {
|
||||
return entityplayer != null && !entityplayer.isSpectator();
|
||||
};
|
||||
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,7 +366,13 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -1,568 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
// CraftBukkit start
|
||||
import java.util.ArrayList;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class PlayerInteractManager {
|
||||
|
||||
public World world;
|
||||
public EntityPlayer player;
|
||||
private EnumGamemode gamemode;
|
||||
private boolean d;
|
||||
private int lastDigTick;
|
||||
private BlockPosition f;
|
||||
private int currentTick;
|
||||
private boolean h;
|
||||
private BlockPosition i;
|
||||
private int j;
|
||||
private int k;
|
||||
|
||||
public PlayerInteractManager(World world) {
|
||||
this.gamemode = EnumGamemode.NOT_SET;
|
||||
this.f = BlockPosition.ZERO;
|
||||
this.i = BlockPosition.ZERO;
|
||||
this.k = -1;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public void setGameMode(EnumGamemode enumgamemode) {
|
||||
this.gamemode = enumgamemode;
|
||||
enumgamemode.a(this.player.abilities);
|
||||
this.player.updateAbilities();
|
||||
this.player.server.getPlayerList().sendAll(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_GAME_MODE, new EntityPlayer[] { this.player}), this.player); // CraftBukkit
|
||||
this.world.everyoneSleeping();
|
||||
}
|
||||
|
||||
public EnumGamemode getGameMode() {
|
||||
return this.gamemode;
|
||||
}
|
||||
|
||||
public boolean c() {
|
||||
return this.gamemode.e();
|
||||
}
|
||||
|
||||
public boolean isCreative() {
|
||||
return this.gamemode.isCreative();
|
||||
}
|
||||
|
||||
public void b(EnumGamemode enumgamemode) {
|
||||
if (this.gamemode == EnumGamemode.NOT_SET) {
|
||||
this.gamemode = enumgamemode;
|
||||
}
|
||||
|
||||
this.setGameMode(this.gamemode);
|
||||
}
|
||||
|
||||
public void a() {
|
||||
this.currentTick = MinecraftServer.currentTick; // CraftBukkit;
|
||||
float f;
|
||||
int i;
|
||||
|
||||
if (this.h) {
|
||||
int j = this.currentTick - this.j;
|
||||
IBlockData iblockdata = this.world.getType(this.i);
|
||||
|
||||
if (iblockdata.getMaterial() == Material.AIR) {
|
||||
this.h = false;
|
||||
} else {
|
||||
f = iblockdata.a((EntityHuman) this.player, this.player.world, this.i) * (float) (j + 1);
|
||||
i = (int) (f * 10.0F);
|
||||
if (i != this.k) {
|
||||
this.world.c(this.player.getId(), this.i, i);
|
||||
this.k = i;
|
||||
}
|
||||
|
||||
if (f >= 1.0F) {
|
||||
this.h = false;
|
||||
this.breakBlock(this.i);
|
||||
}
|
||||
}
|
||||
} else if (this.d) {
|
||||
IBlockData iblockdata1 = this.world.getType(this.f);
|
||||
|
||||
if (iblockdata1.getMaterial() == Material.AIR) {
|
||||
this.world.c(this.player.getId(), this.f, -1);
|
||||
this.k = -1;
|
||||
this.d = false;
|
||||
} else {
|
||||
int k = this.currentTick - this.lastDigTick;
|
||||
|
||||
f = iblockdata1.a((EntityHuman) this.player, this.player.world, this.i) * (float) (k + 1);
|
||||
i = (int) (f * 10.0F);
|
||||
if (i != this.k) {
|
||||
this.world.c(this.player.getId(), this.f, i);
|
||||
this.k = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
// CraftBukkit start
|
||||
PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockposition, enumdirection, this.player.inventory.getItemInHand(), EnumHand.MAIN_HAND);
|
||||
if (event.isCancelled()) {
|
||||
// Let the client know the block still exists
|
||||
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
|
||||
cancelBreakBlock(blockposition, this.world.getType(blockposition)); // Paper - Avoid visual issues on the client
|
||||
// Update any tile entity data for this block
|
||||
TileEntity tileentity = this.world.getTileEntity(blockposition);
|
||||
if (tileentity != null) {
|
||||
this.player.playerConnection.sendPacket(tileentity.getUpdatePacket());
|
||||
}
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
if (this.isCreative()) {
|
||||
if (!this.world.douseFire((EntityHuman) null, blockposition, enumdirection)) {
|
||||
this.breakBlock(blockposition);
|
||||
}
|
||||
|
||||
} else {
|
||||
IBlockData iblockdata = this.world.getType(blockposition);
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
if (this.gamemode.c()) {
|
||||
if (this.gamemode == EnumGamemode.SPECTATOR) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.player.dk()) {
|
||||
ItemStack itemstack = this.player.getItemInMainHand();
|
||||
|
||||
if (itemstack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!itemstack.a(block)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this.world.douseFire((EntityHuman) null, blockposition, enumdirection); // CraftBukkit - Moved down
|
||||
this.lastDigTick = this.currentTick;
|
||||
float f = 1.0F;
|
||||
|
||||
// CraftBukkit start - Swings at air do *NOT* exist.
|
||||
if (event.useInteractedBlock() == Event.Result.DENY) {
|
||||
// If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door.
|
||||
IBlockData data = this.world.getType(blockposition);
|
||||
if (block == Blocks.WOODEN_DOOR) {
|
||||
// For some reason *BOTH* the bottom/top part have to be marked updated.
|
||||
boolean bottom = data.get(BlockDoor.HALF) == BlockDoor.EnumDoorHalf.LOWER;
|
||||
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
|
||||
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, bottom ? blockposition.up() : blockposition.down()));
|
||||
} else if (block == Blocks.TRAPDOOR) {
|
||||
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
|
||||
}
|
||||
} else if (iblockdata.getMaterial() != Material.AIR) {
|
||||
block.attack(this.world, blockposition, this.player);
|
||||
f = iblockdata.a((EntityHuman) this.player, this.player.world, blockposition);
|
||||
// Allow fire punching to be blocked
|
||||
this.world.douseFire((EntityHuman) null, blockposition, enumdirection);
|
||||
}
|
||||
|
||||
if (event.useItemInHand() == Event.Result.DENY) {
|
||||
// If we 'insta destroyed' then the client needs to be informed.
|
||||
if (f > 1.0f) {
|
||||
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
|
||||
}
|
||||
return;
|
||||
}
|
||||
org.bukkit.event.block.BlockDamageEvent blockEvent = CraftEventFactory.callBlockDamageEvent(this.player, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this.player.inventory.getItemInHand(), f >= 1.0f);
|
||||
|
||||
if (blockEvent.isCancelled()) {
|
||||
// Let the client know the block still exists
|
||||
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
|
||||
return;
|
||||
}
|
||||
|
||||
if (blockEvent.getInstaBreak()) {
|
||||
f = 2.0f;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (iblockdata.getMaterial() != Material.AIR && f >= 1.0F) {
|
||||
this.breakBlock(blockposition);
|
||||
} else {
|
||||
this.d = true;
|
||||
this.f = blockposition;
|
||||
int i = (int) (f * 10.0F);
|
||||
|
||||
this.world.c(this.player.getId(), blockposition, i);
|
||||
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, blockposition)); // Paper - MC-54026 - backport from 1.13
|
||||
this.k = i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.world.chunkPacketBlockController.updateNearbyBlocks(this.world, blockposition); // Paper - Anti-Xray
|
||||
}
|
||||
|
||||
public void a(BlockPosition blockposition) {
|
||||
if (blockposition.equals(this.f)) {
|
||||
this.currentTick = MinecraftServer.currentTick; // CraftBukkit
|
||||
int i = this.currentTick - this.lastDigTick;
|
||||
IBlockData iblockdata = this.world.getType(blockposition);
|
||||
|
||||
if (iblockdata.getMaterial() != Material.AIR) {
|
||||
float f = iblockdata.a((EntityHuman) this.player, this.player.world, blockposition) * (float) (i + 1);
|
||||
|
||||
if (f >= 0.7F) {
|
||||
this.d = false;
|
||||
this.world.c(this.player.getId(), blockposition, -1);
|
||||
this.breakBlock(blockposition);
|
||||
} else if (!this.h) {
|
||||
this.d = false;
|
||||
this.h = true;
|
||||
this.i = blockposition;
|
||||
this.j = this.lastDigTick;
|
||||
}
|
||||
}
|
||||
// CraftBukkit start - Force block reset to client
|
||||
} else {
|
||||
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void e() {
|
||||
this.d = false;
|
||||
this.world.c(this.player.getId(), this.f, -1);
|
||||
}
|
||||
|
||||
private boolean c(BlockPosition blockposition) {
|
||||
IBlockData iblockdata = this.world.getType(blockposition);
|
||||
|
||||
iblockdata.getBlock().a(this.world, blockposition, iblockdata, (EntityHuman) this.player);
|
||||
boolean flag = this.world.setAir(blockposition);
|
||||
|
||||
if (flag) {
|
||||
iblockdata.getBlock().postBreak(this.world, blockposition, iblockdata);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
// Paper start - Extra method to avoid visual issues on the client when cancelling block breaks
|
||||
private void cancelBreakBlock(BlockPosition position, IBlockData data) {
|
||||
Block block = data.getBlock();
|
||||
// Send other half of the door
|
||||
if (block instanceof BlockDoor) {
|
||||
boolean bottom = data.get(BlockDoor.HALF) == BlockDoor.EnumDoorHalf.LOWER;
|
||||
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, bottom ? position.up() : position.down()));
|
||||
} else if (block instanceof BlockTallPlant) {
|
||||
boolean bottom = data.get(BlockTallPlant.HALF) == BlockTallPlant.EnumTallPlantHalf.LOWER;
|
||||
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, bottom ? position.up() : position.down()));
|
||||
} else if (block instanceof BlockPistonExtension) {
|
||||
BlockPosition piston = position.shift(data.get(BlockPistonExtension.FACING).opposite());
|
||||
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, piston));
|
||||
} else if (block instanceof BlockBed) {
|
||||
if (data.get(BlockBed.PART) == BlockBed.EnumBedPart.FOOT) {
|
||||
// Restore head of bed
|
||||
BlockPosition head = position.shift(data.get(BlockBed.FACING));
|
||||
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, head));
|
||||
|
||||
TileEntity tileentity = this.world.getTileEntity(head);
|
||||
if (tileentity != null) {
|
||||
this.player.playerConnection.sendPacket(tileentity.getUpdatePacket());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public boolean breakBlock(BlockPosition blockposition) {
|
||||
// CraftBukkit start - fire BlockBreakEvent
|
||||
BlockBreakEvent event = null;
|
||||
|
||||
if (this.player instanceof EntityPlayer) {
|
||||
org.bukkit.block.Block block = this.world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
|
||||
// Sword + Creative mode pre-cancel
|
||||
boolean isSwordNoBreak = this.gamemode.isCreative() && !this.player.getItemInMainHand().isEmpty() && this.player.getItemInMainHand().getItem() instanceof ItemSword;
|
||||
|
||||
// Tell client the block is gone immediately then process events
|
||||
// Don't tell the client if its a creative sword break because its not broken!
|
||||
if (world.getTileEntity(blockposition) == null && !isSwordNoBreak) {
|
||||
PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(this.world, blockposition);
|
||||
packet.block = Blocks.AIR.getBlockData();
|
||||
((EntityPlayer) this.player).playerConnection.sendPacket(packet);
|
||||
}
|
||||
|
||||
event = new BlockBreakEvent(block, this.player.getBukkitEntity());
|
||||
|
||||
// Sword + Creative mode pre-cancel
|
||||
event.setCancelled(isSwordNoBreak);
|
||||
|
||||
// Calculate default block experience
|
||||
IBlockData nmsData = this.world.getType(blockposition);
|
||||
Block nmsBlock = nmsData.getBlock();
|
||||
|
||||
ItemStack itemstack = this.player.getEquipment(EnumItemSlot.MAINHAND);
|
||||
|
||||
if (nmsBlock != null && !event.isCancelled() && !this.isCreative() && this.player.hasBlock(nmsBlock.getBlockData())) {
|
||||
// Copied from block.a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack)
|
||||
// PAIL: checkme each update
|
||||
if (!(nmsBlock.o() && EnchantmentManager.getEnchantmentLevel(Enchantments.SILK_TOUCH, itemstack) > 0)) {
|
||||
int bonusLevel = EnchantmentManager.getEnchantmentLevel(Enchantments.LOOT_BONUS_BLOCKS, itemstack);
|
||||
|
||||
event.setExpToDrop(nmsBlock.getExpDrop(this.world, nmsData, bonusLevel));
|
||||
}
|
||||
}
|
||||
|
||||
this.world.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
if (isSwordNoBreak) {
|
||||
return false;
|
||||
}
|
||||
// Let the client know the block still exists
|
||||
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
|
||||
cancelBreakBlock(blockposition, nmsData); // Paper - Move cancellation code to extra "cancelBreakBlock" method
|
||||
// Update any tile entity data for this block
|
||||
TileEntity tileentity = this.world.getTileEntity(blockposition);
|
||||
if (tileentity != null) {
|
||||
this.player.playerConnection.sendPacket(tileentity.getUpdatePacket());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (false && this.gamemode.isCreative() && !this.player.getItemInMainHand().isEmpty() && this.player.getItemInMainHand().getItem() instanceof ItemSword) { // CraftBukkit - false
|
||||
return false;
|
||||
} else {
|
||||
IBlockData iblockdata = this.world.getType(blockposition);
|
||||
if (iblockdata.getBlock() == Blocks.AIR) return false; // CraftBukkit - A plugin set block to air without cancelling
|
||||
TileEntity tileentity = this.world.getTileEntity(blockposition);
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
// CraftBukkit start - Special case skulls, their item data comes from a tile entity (Also check if block should drop items)
|
||||
if (iblockdata.getBlock() == Blocks.SKULL && !this.isCreative() && event.isDropItems()) {
|
||||
iblockdata.getBlock().dropNaturally(world, blockposition, iblockdata, 1.0F, 0);
|
||||
return this.c(blockposition);
|
||||
}
|
||||
|
||||
// And shulker boxes too for duplication on cancel reasons (Also check if block should drop items)
|
||||
if (iblockdata.getBlock() instanceof BlockShulkerBox && event.isDropItems()) {
|
||||
iblockdata.getBlock().dropNaturally(world, blockposition, iblockdata, 1.0F, 0);
|
||||
return this.c(blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if ((block instanceof BlockCommand || block instanceof BlockStructure) && !this.player.isCreativeAndOp()) {
|
||||
this.world.notify(blockposition, iblockdata, iblockdata, 3);
|
||||
return false;
|
||||
} else {
|
||||
if (this.gamemode.c()) {
|
||||
if (this.gamemode == EnumGamemode.SPECTATOR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.player.dk()) {
|
||||
ItemStack itemstack = this.player.getItemInMainHand();
|
||||
|
||||
if (itemstack.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!itemstack.a(block)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.world.a(this.player, 2001, blockposition, Block.getCombinedId(iblockdata));
|
||||
// CraftBukkit start
|
||||
world.captureDrops = new ArrayList<>();
|
||||
boolean flag = this.c(blockposition);
|
||||
if (event.isDropItems()) {
|
||||
for (EntityItem item : world.captureDrops) {
|
||||
world.addEntity(item);
|
||||
}
|
||||
}
|
||||
world.captureDrops = null;
|
||||
// CraftBukkit end
|
||||
|
||||
if (this.isCreative()) {
|
||||
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
|
||||
} else {
|
||||
ItemStack itemstack1 = this.player.getItemInMainHand();
|
||||
//ItemStack itemstack2 = itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack(); //NeonPaper move up
|
||||
boolean flag1 = this.player.hasBlock(iblockdata);
|
||||
ItemStack itemstack2 = flag && flag1 && event.isDropItems() && !itemstack1.isEmpty() ? itemstack1.cloneItemStack() : ItemStack.a; // NeonPaper - clone before use
|
||||
|
||||
if (!itemstack1.isEmpty()) {
|
||||
itemstack1.a(this.world, iblockdata, blockposition, this.player);
|
||||
}
|
||||
|
||||
// CraftBukkit start - Check if block should drop items
|
||||
if (flag && flag1 && event.isDropItems()) {
|
||||
iblockdata.getBlock().a(this.world, this.player, blockposition, iblockdata, tileentity, itemstack2);
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
// CraftBukkit start - Drop event experience
|
||||
if (flag && event != null) {
|
||||
iblockdata.getBlock().dropExperience(this.world, blockposition, event.getExpToDrop(), this.player); // Paper
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EnumInteractionResult a(EntityHuman entityhuman, World world, ItemStack itemstack, EnumHand enumhand) {
|
||||
if (this.gamemode == EnumGamemode.SPECTATOR) {
|
||||
return EnumInteractionResult.PASS;
|
||||
} else if (entityhuman.getCooldownTracker().a(itemstack.getItem())) {
|
||||
return EnumInteractionResult.PASS;
|
||||
} else {
|
||||
int i = itemstack.getCount();
|
||||
int j = itemstack.getData();
|
||||
InteractionResultWrapper interactionresultwrapper = itemstack.a(world, entityhuman, enumhand);
|
||||
ItemStack itemstack1 = (ItemStack) interactionresultwrapper.b();
|
||||
|
||||
if (itemstack1 == itemstack && itemstack1.getCount() == i && itemstack1.m() <= 0 && itemstack1.getData() == j) {
|
||||
return interactionresultwrapper.a();
|
||||
} else if (interactionresultwrapper.a() == EnumInteractionResult.FAIL && itemstack1.m() > 0 && !entityhuman.isHandRaised()) {
|
||||
return interactionresultwrapper.a();
|
||||
} else {
|
||||
entityhuman.a(enumhand, itemstack1);
|
||||
if (this.isCreative()) {
|
||||
itemstack1.setCount(i);
|
||||
if (itemstack1.f()) {
|
||||
itemstack1.setData(j);
|
||||
}
|
||||
}
|
||||
|
||||
if (itemstack1.isEmpty()) {
|
||||
entityhuman.a(enumhand, ItemStack.a);
|
||||
}
|
||||
|
||||
if (!entityhuman.isHandRaised()) {
|
||||
((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer);
|
||||
}
|
||||
|
||||
return interactionresultwrapper.a();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CraftBukkit start - whole method
|
||||
public boolean interactResult = false;
|
||||
public boolean firedInteract = false;
|
||||
public EnumInteractionResult a(EntityHuman entityhuman, World world, ItemStack itemstack, EnumHand enumhand, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
IBlockData blockdata = world.getType(blockposition);
|
||||
EnumInteractionResult enuminteractionresult = EnumInteractionResult.FAIL;
|
||||
if (blockdata.getBlock() != Blocks.AIR) {
|
||||
boolean cancelledBlock = false;
|
||||
|
||||
if (this.gamemode == EnumGamemode.SPECTATOR) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
cancelledBlock = !(tileentity instanceof ITileInventory || tileentity instanceof IInventory);
|
||||
}
|
||||
|
||||
if (entityhuman.getCooldownTracker().a(itemstack.getItem())) {
|
||||
cancelledBlock = true;
|
||||
}
|
||||
|
||||
if (itemstack.getItem() instanceof ItemBlock && !entityhuman.isCreativeAndOp()) {
|
||||
Block block1 = ((ItemBlock) itemstack.getItem()).getBlock();
|
||||
|
||||
if (block1 instanceof BlockCommand || block1 instanceof BlockStructure) {
|
||||
cancelledBlock = true;
|
||||
}
|
||||
}
|
||||
|
||||
PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(entityhuman, Action.RIGHT_CLICK_BLOCK, blockposition, enumdirection, itemstack, cancelledBlock, enumhand);
|
||||
firedInteract = true;
|
||||
interactResult = event.useItemInHand() == Event.Result.DENY;
|
||||
|
||||
if (event.useInteractedBlock() == Event.Result.DENY) {
|
||||
// If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door.
|
||||
if (blockdata.getBlock() instanceof BlockDoor) {
|
||||
boolean bottom = blockdata.get(BlockDoor.HALF) == BlockDoor.EnumDoorHalf.LOWER;
|
||||
((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutBlockChange(world, bottom ? blockposition.up() : blockposition.down()));
|
||||
} else if (blockdata.getBlock() instanceof BlockCake) {
|
||||
((EntityPlayer) entityhuman).getBukkitEntity().sendHealthUpdate(); // SPIGOT-1341 - reset health for cake
|
||||
// Paper start - extend Player Interact cancellation
|
||||
} else if (blockdata.getBlock() instanceof BlockStructure) {
|
||||
((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutCloseWindow());
|
||||
} else if (blockdata.getBlock() instanceof BlockCommand) {
|
||||
((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutCloseWindow());
|
||||
} else if (blockdata.getBlock() instanceof BlockFlowerPot) {
|
||||
// Send a block change to air and then send back the correct block, just to make the client happy
|
||||
PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(this.world, blockposition);
|
||||
packet.block = Blocks.AIR.getBlockData();
|
||||
this.player.playerConnection.sendPacket(packet);
|
||||
|
||||
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
|
||||
|
||||
TileEntity tileentity = this.world.getTileEntity(blockposition);
|
||||
if (tileentity != null) {
|
||||
player.playerConnection.sendPacket(tileentity.getUpdatePacket());
|
||||
}
|
||||
// Paper end - extend Player Interact cancellation
|
||||
}
|
||||
((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); // SPIGOT-2867
|
||||
enuminteractionresult = (event.useItemInHand() != Event.Result.ALLOW) ? EnumInteractionResult.SUCCESS : EnumInteractionResult.PASS;
|
||||
} else if (this.gamemode == EnumGamemode.SPECTATOR) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof ITileInventory) {
|
||||
Block block = world.getType(blockposition).getBlock();
|
||||
ITileInventory itileinventory = (ITileInventory) tileentity;
|
||||
|
||||
if (itileinventory instanceof TileEntityChest && block instanceof BlockChest) {
|
||||
itileinventory = ((BlockChest) block).getInventory(world, blockposition);
|
||||
}
|
||||
|
||||
if (itileinventory != null) {
|
||||
entityhuman.openContainer(itileinventory);
|
||||
return EnumInteractionResult.SUCCESS;
|
||||
}
|
||||
} else if (tileentity instanceof IInventory) {
|
||||
entityhuman.openContainer((IInventory) tileentity);
|
||||
return EnumInteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
return EnumInteractionResult.PASS;
|
||||
} else {
|
||||
if (!entityhuman.isSneaking() || entityhuman.getItemInMainHand().isEmpty() && entityhuman.getItemInOffHand().isEmpty()) {
|
||||
IBlockData iblockdata = world.getType(blockposition);
|
||||
|
||||
enuminteractionresult = iblockdata.getBlock().interact(world, blockposition, iblockdata, entityhuman, enumhand, enumdirection, f, f1, f2) ? EnumInteractionResult.SUCCESS : EnumInteractionResult.PASS;
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemstack.isEmpty() && enuminteractionresult != EnumInteractionResult.SUCCESS && !interactResult) { // add !interactResult SPIGOT-764
|
||||
int i = itemstack.getData();
|
||||
int j = itemstack.getCount();
|
||||
|
||||
enuminteractionresult = itemstack.placeItem(entityhuman, world, blockposition, enumhand, enumdirection, f, f1, f2);
|
||||
|
||||
// The item count should not decrement in Creative mode.
|
||||
if (this.isCreative()) {
|
||||
itemstack.setData(i);
|
||||
itemstack.setCount(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
return enuminteractionresult;
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
public void a(WorldServer worldserver) {
|
||||
this.world = worldserver;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,506 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.destroystokyo.paper.exception.ServerInternalException;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.List;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class RegionFile {
|
||||
|
||||
// Spigot start
|
||||
// Minecraft is limited to 256 sections per chunk. So 1MB. This can easily be overriden.
|
||||
// So we extend this to use the REAL size when the count is maxed by seeking to that section and reading the length.
|
||||
private static final boolean ENABLE_EXTENDED_SAVE = Boolean.parseBoolean(System.getProperty("net.minecraft.server.RegionFile.enableExtendedSave", "true"));
|
||||
// Spigot end
|
||||
private static final byte[] a = new byte[4096];
|
||||
private final File b;private File getFile() { return b; } // Paper - OBFHELPER
|
||||
private RandomAccessFile c;private RandomAccessFile getDataFile() { return c; } // Paper - OBFHELPER
|
||||
private final int[] d = new int[1024];private int[] offsets = d; // Paper - OBFHELPER
|
||||
private final int[] e = new int[1024];private int[] timestamps = e; // Paper - OBFHELPER
|
||||
private List<Boolean> f;
|
||||
private int g;
|
||||
private long h;
|
||||
|
||||
public RegionFile(File file) {
|
||||
this.b = file;
|
||||
this.g = 0;
|
||||
|
||||
try {
|
||||
if (file.exists()) {
|
||||
this.h = file.lastModified();
|
||||
}
|
||||
|
||||
|
||||
this.c = new RandomAccessFile(file, "rw");
|
||||
if (this.c.length() < 8192L) { // Paper - headers should be 8192
|
||||
this.c.write(a);
|
||||
this.c.write(a);
|
||||
this.g += 8192;
|
||||
}
|
||||
|
||||
int i;
|
||||
|
||||
if ((this.c.length() & 4095L) != 0L) {
|
||||
for (i = 0; (long) i < (this.c.length() & 4095L); ++i) {
|
||||
this.c.write(0);
|
||||
}
|
||||
}
|
||||
|
||||
i = (int) this.c.length() / 4096;
|
||||
this.f = Lists.newArrayListWithCapacity(i);
|
||||
|
||||
int j;
|
||||
|
||||
for (j = 0; j < i; ++j) {
|
||||
this.f.add(Boolean.valueOf(true));
|
||||
}
|
||||
|
||||
this.f.set(0, Boolean.valueOf(false));
|
||||
this.f.set(1, Boolean.valueOf(false));
|
||||
this.c.seek(0L);
|
||||
|
||||
int k;
|
||||
|
||||
// Paper Start
|
||||
ByteBuffer header = ByteBuffer.allocate(8192);
|
||||
while (header.hasRemaining()) {
|
||||
if (this.c.getChannel().read(header) == -1) throw new EOFException();
|
||||
}
|
||||
((Buffer) header).clear();
|
||||
IntBuffer headerAsInts = header.asIntBuffer();
|
||||
initOversizedState();
|
||||
// Paper End
|
||||
for (j = 0; j < 1024; ++j) {
|
||||
k = headerAsInts.get(); // Paper
|
||||
this.d[j] = k;
|
||||
// Spigot start
|
||||
int length = k & 255;
|
||||
if (length == 255) {
|
||||
if ((k >> 8) <= this.f.size()) {
|
||||
// We're maxed out, so we need to read the proper length from the section
|
||||
this.c.seek((k >> 8) * 4096);
|
||||
length = (this.c.readInt() + 4) / 4096 + 1;
|
||||
this.c.seek(j * 4 + 4); // Go back to where we were
|
||||
}
|
||||
}
|
||||
if (k > 0 && (k >> 8) > 1 && (k >> 8) + (length) <= this.f.size()) { // Paper >= 1 as 0/1 are the headers, and negative isnt valid
|
||||
for (int l = 0; l < (length); ++l) {
|
||||
// Spigot end
|
||||
this.f.set((k >> 8) + l, Boolean.valueOf(false));
|
||||
}
|
||||
}
|
||||
// Spigot start
|
||||
else if (k != 0) { // Paper
|
||||
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Length: {3} runs off end file. {4}", new Object[]{j % 32, (int) (j / 32), k >> 8, length, file});
|
||||
deleteChunk(j); // Paper
|
||||
}
|
||||
// Spigot end
|
||||
}
|
||||
|
||||
for (j = 0; j < 1024; ++j) {
|
||||
k = headerAsInts.get(); // Paper
|
||||
if (offsets[j] != 0) this.timestamps[j] = k; // Paper - don't set timestamp if it got 0'd above due to corruption
|
||||
}
|
||||
} catch (IOException ioexception) {
|
||||
ioexception.printStackTrace();
|
||||
ServerInternalException.reportInternalException(ioexception); // Paper
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public synchronized DataInputStream getReadStream(int i, int j) { return a(i, j); } @Nullable public synchronized DataInputStream a(int i, int j) { // Paper - OBFHELPER
|
||||
if (this.d(i, j)) {
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
int k = this.getOffset(i, j);
|
||||
|
||||
if (k == 0) {
|
||||
return null;
|
||||
} else {
|
||||
int l = k >> 8;
|
||||
int i1 = k & 255;
|
||||
// Spigot start
|
||||
if (i1 == 255) {
|
||||
this.c.seek(l * 4096);
|
||||
i1 = (this.c.readInt() + 4) / 4096 + 1;
|
||||
}
|
||||
// Spigot end
|
||||
|
||||
if (l + i1 > this.f.size()) {
|
||||
return null;
|
||||
} else {
|
||||
this.c.seek((long) (l * 4096));
|
||||
int j1 = this.c.readInt();
|
||||
|
||||
if (j1 > 4096 * i1) {
|
||||
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Invalid Size: {3}>{4} {5}", new Object[]{i, j, l, j1, i1 * 4096, this.b}); // Spigot
|
||||
return null;
|
||||
} else if (j1 <= 0) {
|
||||
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Invalid Size: {3} {4}", new Object[]{i, j, l, j1, this.b}); // Spigot
|
||||
return null;
|
||||
} else {
|
||||
byte b0 = this.c.readByte();
|
||||
byte[] abyte;
|
||||
|
||||
if (b0 == 1) {
|
||||
abyte = new byte[j1 - 1];
|
||||
this.c.read(abyte);
|
||||
return new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(abyte))));
|
||||
} else if (b0 == 2) {
|
||||
abyte = new byte[j1 - 1];
|
||||
this.c.read(abyte);
|
||||
return new DataInputStream(new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(abyte))));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ioexception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DataOutputStream getWriteStream(int i, int j) { return b(i, j); } @Nullable public DataOutputStream b(int i, int j) { // Paper - OBFHELPER
|
||||
return this.d(i, j) ? null : new DataOutputStream(new RegionFile.ChunkBuffer(i, j)); // Paper - remove middleware, move deflate to .close() for dynamic levels
|
||||
}
|
||||
|
||||
protected synchronized void a(int i, int j, byte[] abyte, int k) {
|
||||
try {
|
||||
int l = this.getOffset(i, j);
|
||||
int i1 = l >> 8;
|
||||
int j1 = l & 255;
|
||||
// Spigot start
|
||||
if (j1 == 255) {
|
||||
this.c.seek(i1 * 4096);
|
||||
j1 = (this.c.readInt() + 4) / 4096 + 1;
|
||||
}
|
||||
// Spigot end
|
||||
int k1 = (k + 5) / 4096 + 1;
|
||||
|
||||
if (k1 >= 256) {
|
||||
// Spigot start
|
||||
if (!USE_SPIGOT_OVERSIZED_METHOD && !RegionFileCache.isOverzealous()) throw new ChunkTooLargeException(i, j, k1); // Paper - throw error instead
|
||||
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING,"Large Chunk Detected: ({0}, {1}) Size: {2} {3}", new Object[]{i, j, k1, this.b});
|
||||
if (!ENABLE_EXTENDED_SAVE) return;
|
||||
// Spigot end
|
||||
}
|
||||
|
||||
if (i1 != 0 && j1 == k1) {
|
||||
this.a(i1, abyte, k);
|
||||
} else {
|
||||
int l1;
|
||||
|
||||
for (l1 = 0; l1 < j1; ++l1) {
|
||||
this.f.set(i1 + l1, Boolean.valueOf(true));
|
||||
}
|
||||
|
||||
l1 = this.f.indexOf(Boolean.valueOf(true));
|
||||
int i2 = 0;
|
||||
int j2;
|
||||
|
||||
if (l1 != -1) {
|
||||
for (j2 = l1; j2 < this.f.size(); ++j2) {
|
||||
if (i2 != 0) {
|
||||
if (((Boolean) this.f.get(j2)).booleanValue()) {
|
||||
++i2;
|
||||
} else {
|
||||
i2 = 0;
|
||||
}
|
||||
} else if (((Boolean) this.f.get(j2)).booleanValue()) {
|
||||
l1 = j2;
|
||||
i2 = 1;
|
||||
}
|
||||
|
||||
if (i2 >= k1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i2 >= k1) {
|
||||
i1 = l1;
|
||||
this.a(i, j, l1 << 8 | (k1 > 255 ? 255 : k1)); // Spigot
|
||||
|
||||
for (j2 = 0; j2 < k1; ++j2) {
|
||||
this.f.set(i1 + j2, Boolean.valueOf(false));
|
||||
}
|
||||
|
||||
this.a(i1, abyte, k);
|
||||
} else {
|
||||
this.c.seek(this.c.length());
|
||||
i1 = this.f.size();
|
||||
|
||||
for (j2 = 0; j2 < k1; ++j2) {
|
||||
this.c.write(RegionFile.a);
|
||||
this.f.add(Boolean.valueOf(false));
|
||||
}
|
||||
|
||||
this.g += 4096 * k1;
|
||||
this.a(i1, abyte, k);
|
||||
this.a(i, j, i1 << 8 | (k1 > 255 ? 255 : k1)); // Spigot
|
||||
}
|
||||
}
|
||||
|
||||
this.b(i, j, (int) (MinecraftServer.aw() / 1000L));
|
||||
} catch (IOException ioexception) {
|
||||
org.spigotmc.SneakyThrow.sneaky(ioexception); // Paper - we want the upper try/catch to retry this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void a(int i, byte[] abyte, int j) throws IOException {
|
||||
this.c.seek((long) (i * 4096));
|
||||
this.c.writeInt(j + 1);
|
||||
this.c.writeByte(2);
|
||||
this.c.write(abyte, 0, j);
|
||||
}
|
||||
|
||||
private boolean d(int i, int j) {
|
||||
return i < 0 || i >= 32 || j < 0 || j >= 32;
|
||||
}
|
||||
|
||||
private synchronized int getOffset(int i, int j) {
|
||||
return this.d[i + j * 32];
|
||||
}
|
||||
|
||||
public boolean c(int i, int j) {
|
||||
return this.getOffset(i, j) != 0;
|
||||
}
|
||||
|
||||
private void a(int i, int j, int k) throws IOException {
|
||||
this.d[i + j * 32] = k;
|
||||
this.c.seek((long) ((i + j * 32) * 4));
|
||||
this.c.writeInt(k);
|
||||
}
|
||||
|
||||
private void b(int i, int j, int k) throws IOException {
|
||||
this.e[i + j * 32] = k;
|
||||
this.c.seek((long) (4096 + (i + j * 32) * 4));
|
||||
this.c.writeInt(k);
|
||||
}
|
||||
|
||||
public void c() throws IOException {
|
||||
if (this.c != null) {
|
||||
this.c.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Paper start
|
||||
public synchronized void deleteChunk(int j1) {
|
||||
backup();
|
||||
int k = offsets[j1];
|
||||
int x = j1 & 1024;
|
||||
int z = j1 >> 2;
|
||||
int offset = (k >> 8);
|
||||
int len = (k & 255);
|
||||
org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger();
|
||||
String debug = "idx:" + + j1 + " - " + x + "," + z + " - offset: " + offset + " - len: " + len;
|
||||
try {
|
||||
RandomAccessFile file = getDataFile();
|
||||
file.seek(j1 * 4);
|
||||
file.writeInt(0);
|
||||
// clear the timestamp
|
||||
file.seek(4096 + j1 * 4);
|
||||
file.writeInt(0);
|
||||
timestamps[j1] = 0;
|
||||
offsets[j1] = 0;
|
||||
logger.error("Deleted corrupt chunk (" + debug + ") " + getFile().getAbsolutePath(), e);
|
||||
} catch (IOException e) {
|
||||
|
||||
logger.error("Error deleting corrupt chunk (" + debug + ") " + getFile().getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
private boolean backedUp = false;
|
||||
private synchronized void backup() {
|
||||
if (backedUp) {
|
||||
return;
|
||||
}
|
||||
backedUp = true;
|
||||
File file = this.getFile();
|
||||
java.text.DateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
||||
java.util.Date today = new java.util.Date();
|
||||
File corrupt = new File(file.getParentFile(), file.getName() + "." + formatter.format(today) + ".corrupt");
|
||||
if (corrupt.exists()) {
|
||||
return;
|
||||
}
|
||||
org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger();
|
||||
logger.error("Region file " + file.getAbsolutePath() + " was corrupt. Backing up to " + corrupt.getAbsolutePath() + " and repairing");
|
||||
try {
|
||||
java.nio.file.Files.copy(file.toPath(), corrupt.toPath());
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.error("Error backing up corrupt file" + file.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private final byte[] oversized = new byte[1024];
|
||||
private int oversizedCount = 0;
|
||||
|
||||
private synchronized void initOversizedState() throws IOException {
|
||||
File metaFile = getOversizedMetaFile();
|
||||
if (metaFile.exists()) {
|
||||
final byte[] read = java.nio.file.Files.readAllBytes(metaFile.toPath());
|
||||
System.arraycopy(read, 0, oversized, 0, oversized.length);
|
||||
for (byte temp : oversized) {
|
||||
oversizedCount += temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getChunkIndex(int x, int z) {
|
||||
return (x & 31) + (z & 31) * 32;
|
||||
}
|
||||
synchronized boolean isOversized(int x, int z) {
|
||||
return this.oversized[getChunkIndex(x, z)] == 1;
|
||||
}
|
||||
synchronized void setOversized(int x, int z, boolean oversized) throws IOException {
|
||||
final int offset = getChunkIndex(x, z);
|
||||
boolean previous = this.oversized[offset] == 1;
|
||||
this.oversized[offset] = (byte) (oversized ? 1 : 0);
|
||||
if (!previous && oversized) {
|
||||
oversizedCount++;
|
||||
} else if (!oversized && previous) {
|
||||
oversizedCount--;
|
||||
}
|
||||
if (previous && !oversized) {
|
||||
File oversizedFile = getOversizedFile(x, z);
|
||||
if (oversizedFile.exists()) {
|
||||
oversizedFile.delete();
|
||||
}
|
||||
}
|
||||
if (oversizedCount > 0) {
|
||||
if (previous != oversized) {
|
||||
writeOversizedMeta();
|
||||
}
|
||||
} else if (previous) {
|
||||
File oversizedMetaFile = getOversizedMetaFile();
|
||||
if (oversizedMetaFile.exists()) {
|
||||
oversizedMetaFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeOversizedMeta() throws IOException {
|
||||
java.nio.file.Files.write(getOversizedMetaFile().toPath(), oversized);
|
||||
}
|
||||
|
||||
private File getOversizedMetaFile() {
|
||||
return new File(getFile().getParentFile(), getFile().getName().replaceAll("\\.mca$", "") + ".oversized.nbt");
|
||||
}
|
||||
|
||||
private File getOversizedFile(int x, int z) {
|
||||
return new File(this.getFile().getParentFile(), this.getFile().getName().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt");
|
||||
}
|
||||
|
||||
void writeOversizedData(int x, int z, NBTTagCompound oversizedData) throws IOException {
|
||||
File file = getOversizedFile(x, z);
|
||||
try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new DeflaterOutputStream(new java.io.FileOutputStream(file), new java.util.zip.Deflater(java.util.zip.Deflater.BEST_COMPRESSION), 32 * 1024), 32 * 1024))) {
|
||||
NBTCompressedStreamTools.writeNBT(oversizedData, out);
|
||||
}
|
||||
this.setOversized(x, z, true);
|
||||
|
||||
}
|
||||
|
||||
synchronized NBTTagCompound getOversizedData(int x, int z) throws IOException {
|
||||
File file = getOversizedFile(x, z);
|
||||
try (DataInputStream out = new DataInputStream(new BufferedInputStream(new InflaterInputStream(new java.io.FileInputStream(file))))) {
|
||||
return NBTCompressedStreamTools.readNBT(out);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final boolean USE_SPIGOT_OVERSIZED_METHOD = Boolean.getBoolean("Paper.useSpigotExtendedSaveMethod"); // Paper
|
||||
static {
|
||||
if (USE_SPIGOT_OVERSIZED_METHOD) {
|
||||
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "====================================");
|
||||
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Using Spigot Oversized Chunk save method. Warning this will result in extremely fragmented chunks, as well as making the entire region file unable to be to used in any other software but Forge or Spigot (not usable in Vanilla or CraftBukkit). Paper's method is highly recommended.");
|
||||
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "====================================");
|
||||
}
|
||||
}
|
||||
|
||||
public class ChunkTooLargeException extends RuntimeException {
|
||||
public ChunkTooLargeException(int x, int z, int sectors) {
|
||||
super("Chunk " + x + "," + z + " of " + getFile().toString() + " is too large (" + sectors + "/256)");
|
||||
}
|
||||
}
|
||||
private static class DirectByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
public DirectByteArrayOutputStream() {
|
||||
super();
|
||||
}
|
||||
|
||||
public DirectByteArrayOutputStream(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
public byte[] getBuffer() {
|
||||
return this.buf;
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
|
||||
class ChunkBuffer extends ByteArrayOutputStream {
|
||||
|
||||
private final int b;
|
||||
private final int c;
|
||||
|
||||
public ChunkBuffer(int i, int j) {
|
||||
super(8096);
|
||||
this.b = i;
|
||||
this.c = j;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
// Paper start - apply dynamic compression
|
||||
int origLength = this.count;
|
||||
byte[] buf = this.buf;
|
||||
DirectByteArrayOutputStream out = compressData(buf, origLength);
|
||||
byte[] bytes = out.getBuffer();
|
||||
int length = out.size();
|
||||
|
||||
RegionFile.this.a(this.b, this.c, bytes, length); // Paper - change to bytes/length
|
||||
}
|
||||
}
|
||||
|
||||
private static final byte[] compressionBuffer = new byte[1024 * 64]; // 64k fits most standard chunks input size even, ideally 1 pass through zlib
|
||||
private static final java.util.zip.Deflater deflater = new java.util.zip.Deflater();
|
||||
// since file IO is single threaded, no benefit to using per-region file buffers/synchronization, we can change that later if it becomes viable.
|
||||
private static DirectByteArrayOutputStream compressData(byte[] buf, int length) throws IOException {
|
||||
synchronized (deflater) {
|
||||
deflater.setInput(buf, 0, length);
|
||||
deflater.finish();
|
||||
|
||||
|
||||
DirectByteArrayOutputStream out = new DirectByteArrayOutputStream(length);
|
||||
while (!deflater.finished()) {
|
||||
out.write(compressionBuffer, 0, deflater.deflate(compressionBuffer));
|
||||
}
|
||||
out.close();
|
||||
deflater.reset();
|
||||
return out;
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,356 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
// CraftBukkit start
|
||||
import com.destroystokyo.paper.exception.ServerInternalException;
|
||||
import org.bukkit.craftbukkit.util.LongHash;
|
||||
import org.bukkit.craftbukkit.util.LongHashSet;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||
// CraftBukkit end
|
||||
|
||||
public final class SpawnerCreature {
|
||||
|
||||
private static final int a = (int) Math.pow(17.0D, 2.0D);
|
||||
private final LongHashSet b = new LongHashSet(); // CraftBukkit
|
||||
|
||||
public SpawnerCreature() {}
|
||||
|
||||
// Spigot start - get entity count only from chunks being processed in b
|
||||
private int getEntityCount(WorldServer server, Class oClass)
|
||||
{
|
||||
// Paper start - use entire world, not just active chunks. Spigot broke vanilla expectations.
|
||||
if (true) {
|
||||
int sum = 0;
|
||||
for (Chunk c : server.getChunkProviderServer().chunks.values()) {
|
||||
sum += c.entityCount.get(oClass);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
// Paper end
|
||||
int i = 0;
|
||||
Iterator<Long> it = this.b.iterator();
|
||||
while ( it.hasNext() )
|
||||
{
|
||||
Long coord = it.next();
|
||||
int x = LongHash.msw( coord );
|
||||
int z = LongHash.lsw( coord );
|
||||
if ( !((ChunkProviderServer)server.chunkProvider).unloadQueue.contains( coord ) && server.isChunkLoaded( x, z, true ) )
|
||||
{
|
||||
i += server.getChunkAt( x, z ).entityCount.get( oClass );
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
// Spigot end
|
||||
|
||||
public int a(WorldServer worldserver, boolean flag, boolean flag1, boolean flag2) {
|
||||
org.spigotmc.AsyncCatcher.catchOp("check for eligible spawn chunks"); // Paper - At least until we figure out what is calling this async
|
||||
if (!flag && !flag1) {
|
||||
return 0;
|
||||
} else {
|
||||
this.b.clear();
|
||||
int i = 0;
|
||||
Iterator iterator = worldserver.players.iterator();
|
||||
|
||||
int j;
|
||||
int k;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityHuman entityhuman = (EntityHuman) iterator.next();
|
||||
|
||||
if (!entityhuman.isSpectator() && entityhuman.affectsSpawning) {
|
||||
int l = MathHelper.floor(entityhuman.locX / 16.0D);
|
||||
|
||||
j = MathHelper.floor(entityhuman.locZ / 16.0D);
|
||||
boolean flag3 = true;
|
||||
// Spigot Start
|
||||
byte b0 = worldserver.spigotConfig.mobSpawnRange;
|
||||
b0 = ( b0 > worldserver.spigotConfig.viewDistance ) ? (byte) worldserver.spigotConfig.viewDistance : b0;
|
||||
b0 = ( b0 > 8 ) ? 8 : b0;
|
||||
// Paper start
|
||||
com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event;
|
||||
event = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(
|
||||
(org.bukkit.entity.Player) entityhuman.getBukkitEntity(), b0);
|
||||
if (!event.callEvent()) {
|
||||
continue;
|
||||
}
|
||||
b0 = event.getSpawnRadius();
|
||||
// Paperr end
|
||||
|
||||
for (int i1 = -b0; i1 <= b0; ++i1) {
|
||||
for (k = -b0; k <= b0; ++k) {
|
||||
boolean flag4 = i1 == -b0 || i1 == b0 || k == -b0 || k == b0;
|
||||
// Spigot End
|
||||
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i1 + l, k + j);
|
||||
|
||||
// CraftBukkit start - use LongHash and LongHashSet
|
||||
long chunkCoords = LongHash.toLong(chunkcoordintpair.x, chunkcoordintpair.z);
|
||||
if (!this.b.contains(chunkCoords)) {
|
||||
++i;
|
||||
if (!flag4 && worldserver.isChunkLoaded(chunkcoordintpair.x, chunkcoordintpair.z, true) && worldserver.getWorldBorder().isInBounds(chunkcoordintpair)) {
|
||||
PlayerChunk playerchunk = worldserver.getPlayerChunkMap().getChunk(chunkcoordintpair.x, chunkcoordintpair.z);
|
||||
|
||||
if (playerchunk != null && playerchunk.e()) {
|
||||
this.b.add(chunkCoords);
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int j1 = 0;
|
||||
BlockPosition blockposition = worldserver.getSpawn();
|
||||
EnumCreatureType[] aenumcreaturetype = EnumCreatureType.values();
|
||||
|
||||
j = aenumcreaturetype.length;
|
||||
|
||||
for (int k1 = 0; k1 < j; ++k1) {
|
||||
EnumCreatureType enumcreaturetype = aenumcreaturetype[k1];
|
||||
|
||||
// CraftBukkit start - Use per-world spawn limits
|
||||
int limit = enumcreaturetype.b();
|
||||
switch (enumcreaturetype) {
|
||||
case MONSTER:
|
||||
limit = worldserver.getWorld().getMonsterSpawnLimit();
|
||||
break;
|
||||
case CREATURE:
|
||||
limit = worldserver.getWorld().getAnimalSpawnLimit();
|
||||
break;
|
||||
case WATER_CREATURE:
|
||||
limit = worldserver.getWorld().getWaterAnimalSpawnLimit();
|
||||
break;
|
||||
case AMBIENT:
|
||||
limit = worldserver.getWorld().getAmbientSpawnLimit();
|
||||
break;
|
||||
}
|
||||
|
||||
if (limit == 0) {
|
||||
continue;
|
||||
}
|
||||
int mobcnt = 0; // Spigot
|
||||
// CraftBukkit end
|
||||
|
||||
if ((!enumcreaturetype.d() || flag1) && (enumcreaturetype.d() || flag) && (!enumcreaturetype.e() || flag2)) {
|
||||
/* Paper start - As far as I can tell neither of these are even used
|
||||
k = worldserver.a(enumcreaturetype.a());
|
||||
int l1 = limit * i / a; // CraftBukkit - use per-world limits
|
||||
*/ // Paper end
|
||||
|
||||
if ((mobcnt = getEntityCount(worldserver, enumcreaturetype.a())) <= limit * i / 289) { // Paper - use 17x17 like vanilla (a at top of file)
|
||||
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
|
||||
Iterator iterator1 = this.b.iterator();
|
||||
|
||||
int moblimit = (limit * i / 256) - mobcnt + 1; // Spigot - up to 1 more than limit
|
||||
label120:
|
||||
while (iterator1.hasNext() && (moblimit > 0)) { // Spigot - while more allowed
|
||||
// CraftBukkit start = use LongHash and LongObjectHashMap
|
||||
long key = ((Long) iterator1.next()).longValue();
|
||||
BlockPosition blockposition1 = getRandomPosition(worldserver, LongHash.msw(key), LongHash.lsw(key));
|
||||
// CraftBukkit
|
||||
int i2 = blockposition1.getX();
|
||||
int j2 = blockposition1.getY();
|
||||
int k2 = blockposition1.getZ();
|
||||
IBlockData iblockdata = worldserver.getWorldBorder().isInBounds(blockposition1) ? worldserver.getTypeIfLoaded(blockposition1) : null; // Paper
|
||||
|
||||
if (iblockdata != null && !iblockdata.l()) { // Paper
|
||||
int l2 = 0;
|
||||
int i3 = 0;
|
||||
|
||||
while (i3 < 3) {
|
||||
int j3 = i2;
|
||||
int k3 = j2;
|
||||
int l3 = k2;
|
||||
boolean flag5 = true;
|
||||
BiomeBase.BiomeMeta biomebase_biomemeta = null;
|
||||
GroupDataEntity groupdataentity = null;
|
||||
int i4 = MathHelper.f(Math.random() * 4.0D);
|
||||
int j4 = 0;
|
||||
|
||||
while (true) {
|
||||
if (j4 < i4) {
|
||||
label113: {
|
||||
j3 += worldserver.random.nextInt(6) - worldserver.random.nextInt(6);
|
||||
k3 += worldserver.random.nextInt(1) - worldserver.random.nextInt(1);
|
||||
l3 += worldserver.random.nextInt(6) - worldserver.random.nextInt(6);
|
||||
blockposition_mutableblockposition.c(j3, k3, l3);
|
||||
float f = (float) j3 + 0.5F;
|
||||
float f1 = (float) l3 + 0.5F;
|
||||
|
||||
if (worldserver.getWorldBorder().isInBounds(blockposition_mutableblockposition) && worldserver.getChunkIfLoaded(blockposition_mutableblockposition) != null && !worldserver.isPlayerNearby((double) f, (double) k3, (double) f1, 24.0D) && blockposition.distanceSquared((double) f, (double) k3, (double) f1) >= 576.0D) { // Paper - Prevent mob spawning from loading/generating chunks
|
||||
if (biomebase_biomemeta == null) {
|
||||
biomebase_biomemeta = worldserver.a(enumcreaturetype, (BlockPosition) blockposition_mutableblockposition);
|
||||
if (biomebase_biomemeta == null) {
|
||||
break label113;
|
||||
}
|
||||
}
|
||||
|
||||
if (worldserver.a(enumcreaturetype, biomebase_biomemeta, (BlockPosition) blockposition_mutableblockposition) && a(EntityPositionTypes.a(biomebase_biomemeta.b), worldserver, blockposition_mutableblockposition)) {
|
||||
// Paper start
|
||||
com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event;
|
||||
Class<? extends EntityInsentient> cls = biomebase_biomemeta.b;
|
||||
org.bukkit.entity.EntityType type = EntityTypes.clsToTypeMap.get(cls);
|
||||
if (type != null) {
|
||||
event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
|
||||
MCUtil.toLocation(worldserver, blockposition_mutableblockposition),
|
||||
type, SpawnReason.NATURAL
|
||||
);
|
||||
if (!event.callEvent()) {
|
||||
if (event.shouldAbortSpawn()) {
|
||||
continue label120;
|
||||
}
|
||||
j1 += l2;
|
||||
++j4;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
EntityInsentient entityinsentient;
|
||||
|
||||
try {
|
||||
entityinsentient = (EntityInsentient) biomebase_biomemeta.b.getConstructor(new Class[] { World.class}).newInstance(new Object[] { worldserver});
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
ServerInternalException.reportInternalException(exception); // Paper
|
||||
return j1;
|
||||
}
|
||||
|
||||
entityinsentient.setPositionRotation((double) f, (double) k3, (double) f1, worldserver.random.nextFloat() * 360.0F, 0.0F);
|
||||
if (entityinsentient.P() && entityinsentient.canSpawn()) {
|
||||
groupdataentity = entityinsentient.prepare(worldserver.D(new BlockPosition(entityinsentient)), groupdataentity);
|
||||
if (entityinsentient.canSpawn()) {
|
||||
// CraftBukkit start
|
||||
if (worldserver.addEntity(entityinsentient, SpawnReason.NATURAL)) {
|
||||
++l2;
|
||||
moblimit--; // Spigot
|
||||
}
|
||||
// CraftBukkit end
|
||||
} else {
|
||||
entityinsentient.die();
|
||||
}
|
||||
|
||||
// Spigot start
|
||||
if ( moblimit <= 0 ) {
|
||||
// If we're past limit, stop spawn
|
||||
// Spigot end
|
||||
continue label120;
|
||||
}
|
||||
}
|
||||
|
||||
j1 += l2;
|
||||
}
|
||||
}
|
||||
|
||||
++j4;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
++i3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return j1;
|
||||
}
|
||||
}
|
||||
|
||||
private static BlockPosition getRandomPosition(World world, int i, int j) {
|
||||
Chunk chunk = world.getChunkAt(i, j);
|
||||
int k = i * 16 + world.random.nextInt(16);
|
||||
int l = j * 16 + world.random.nextInt(16);
|
||||
int i1 = MathHelper.c(chunk.e(new BlockPosition(k, 0, l)) + 1, 16);
|
||||
int j1 = world.random.nextInt(i1 > 0 ? i1 : chunk.g() + 16 - 1);
|
||||
|
||||
return new BlockPosition(k, j1, l);
|
||||
}
|
||||
|
||||
public static boolean a(IBlockData iblockdata) {
|
||||
return iblockdata.k() ? false : (iblockdata.m() ? false : (iblockdata.getMaterial().isLiquid() ? false : !BlockMinecartTrackAbstract.i(iblockdata)));
|
||||
}
|
||||
|
||||
public static boolean a(EntityInsentient.EnumEntityPositionType entityinsentient_enumentitypositiontype, World world, BlockPosition blockposition) {
|
||||
if (!world.getWorldBorder().a(blockposition)) {
|
||||
return false;
|
||||
} else {
|
||||
IBlockData iblockdata = world.getType(blockposition);
|
||||
|
||||
if (entityinsentient_enumentitypositiontype == EntityInsentient.EnumEntityPositionType.IN_WATER) {
|
||||
return iblockdata.getMaterial() == Material.WATER && world.getType(blockposition.down()).getMaterial() == Material.WATER && !world.getType(blockposition.up()).l();
|
||||
} else {
|
||||
BlockPosition blockposition1 = blockposition.down();
|
||||
|
||||
if (!world.getType(blockposition1).q()) {
|
||||
return false;
|
||||
} else {
|
||||
Block block = world.getType(blockposition1).getBlock();
|
||||
boolean flag = block != Blocks.BEDROCK && block != Blocks.BARRIER;
|
||||
|
||||
return flag && a(iblockdata) && a(world.getType(blockposition.up()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void a(World world, BiomeBase biomebase, int i, int j, int k, int l, Random random) {
|
||||
List list = biomebase.getMobs(EnumCreatureType.CREATURE);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
while (random.nextFloat() < biomebase.f()) {
|
||||
BiomeBase.BiomeMeta biomebase_biomemeta = (BiomeBase.BiomeMeta) WeightedRandom.a(world.random, list);
|
||||
int i1 = biomebase_biomemeta.c + random.nextInt(1 + biomebase_biomemeta.d - biomebase_biomemeta.c);
|
||||
GroupDataEntity groupdataentity = null;
|
||||
int j1 = i + random.nextInt(k);
|
||||
int k1 = j + random.nextInt(l);
|
||||
int l1 = j1;
|
||||
int i2 = k1;
|
||||
|
||||
for (int j2 = 0; j2 < i1; ++j2) {
|
||||
boolean flag = false;
|
||||
|
||||
for (int k2 = 0; !flag && k2 < 4; ++k2) {
|
||||
BlockPosition blockposition = world.q(new BlockPosition(j1, 0, k1));
|
||||
|
||||
if (a(EntityInsentient.EnumEntityPositionType.ON_GROUND, world, blockposition)) {
|
||||
EntityInsentient entityinsentient;
|
||||
|
||||
try {
|
||||
entityinsentient = (EntityInsentient) biomebase_biomemeta.b.getConstructor(new Class[] { World.class}).newInstance(new Object[] { world});
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
ServerInternalException.reportInternalException(exception); // Paper
|
||||
continue;
|
||||
}
|
||||
|
||||
entityinsentient.setPositionRotation((double) ((float) j1 + 0.5F), (double) blockposition.getY(), (double) ((float) k1 + 0.5F), random.nextFloat() * 360.0F, 0.0F);
|
||||
// CraftBukkit start - Added a reason for spawning this creature, moved entityinsentient.prepare(groupdataentity) up
|
||||
groupdataentity = entityinsentient.prepare(world.D(new BlockPosition(entityinsentient)), groupdataentity);
|
||||
world.addEntity(entityinsentient, SpawnReason.CHUNK_GEN);
|
||||
// CraftBukkit end
|
||||
flag = true;
|
||||
}
|
||||
|
||||
j1 += random.nextInt(5) - random.nextInt(5);
|
||||
|
||||
for (k1 += random.nextInt(5) - random.nextInt(5); j1 < i || j1 >= i + k || k1 < j || k1 >= j + k; k1 = i2 + random.nextInt(5) - random.nextInt(5)) {
|
||||
j1 = l1 + random.nextInt(5) - random.nextInt(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,423 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
// CraftBukkit start
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.FurnaceBurnEvent;
|
||||
import org.bukkit.event.inventory.FurnaceSmeltEvent;
|
||||
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
|
||||
// CraftBukkit end
|
||||
|
||||
public class TileEntityFurnace extends TileEntityContainer implements ITickable, IWorldInventory {
|
||||
|
||||
private static final int[] a = new int[] { 0};
|
||||
private static final int[] f = new int[] { 2, 1};
|
||||
private static final int[] g = new int[] { 1};
|
||||
private NonNullList<ItemStack> items;
|
||||
private int burnTime;
|
||||
private int ticksForCurrentFuel;
|
||||
private int cookTime;
|
||||
private int cookTimeTotal;
|
||||
private String m;
|
||||
|
||||
// CraftBukkit start - add fields and methods
|
||||
private int lastTick = MinecraftServer.currentTick;
|
||||
private int maxStack = MAX_STACK;
|
||||
public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
|
||||
|
||||
public List<ItemStack> getContents() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
public void onOpen(CraftHumanEntity who) {
|
||||
transaction.add(who);
|
||||
}
|
||||
|
||||
public void onClose(CraftHumanEntity who) {
|
||||
transaction.remove(who);
|
||||
}
|
||||
|
||||
public List<HumanEntity> getViewers() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
public void setMaxStackSize(int size) {
|
||||
maxStack = size;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
public TileEntityFurnace() {
|
||||
this.items = NonNullList.a(3, ItemStack.a);
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return this.items.size();
|
||||
}
|
||||
|
||||
public boolean x_() {
|
||||
Iterator iterator = this.items.iterator();
|
||||
|
||||
ItemStack itemstack;
|
||||
|
||||
do {
|
||||
if (!iterator.hasNext()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
itemstack = (ItemStack) iterator.next();
|
||||
} while (itemstack.isEmpty());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public ItemStack getItem(int i) {
|
||||
return (ItemStack) this.items.get(i);
|
||||
}
|
||||
|
||||
public ItemStack splitStack(int i, int j) {
|
||||
return ContainerUtil.a(this.items, i, j);
|
||||
}
|
||||
|
||||
public ItemStack splitWithoutUpdate(int i) {
|
||||
return ContainerUtil.a(this.items, i);
|
||||
}
|
||||
|
||||
public void setItem(int i, ItemStack itemstack) {
|
||||
ItemStack itemstack1 = (ItemStack) this.items.get(i);
|
||||
boolean flag = !itemstack.isEmpty() && itemstack.doMaterialsMatch(itemstack1) && ItemStack.equals(itemstack, itemstack1);
|
||||
|
||||
this.items.set(i, itemstack);
|
||||
if (itemstack.getCount() > this.getMaxStackSize()) {
|
||||
itemstack.setCount(this.getMaxStackSize());
|
||||
}
|
||||
|
||||
if (i == 0 && !flag) {
|
||||
this.cookTimeTotal = this.a(itemstack);
|
||||
this.cookTime = 0;
|
||||
this.update();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.hasCustomName() ? this.m : "container.furnace";
|
||||
}
|
||||
|
||||
public boolean hasCustomName() {
|
||||
return this.m != null && !this.m.isEmpty();
|
||||
}
|
||||
|
||||
public void setCustomName(String s) {
|
||||
this.m = s;
|
||||
}
|
||||
|
||||
public static void a(DataConverterManager dataconvertermanager) {
|
||||
dataconvertermanager.a(DataConverterTypes.BLOCK_ENTITY, (DataInspector) (new DataInspectorItemList(TileEntityFurnace.class, new String[] { "Items"})));
|
||||
}
|
||||
|
||||
public void load(NBTTagCompound nbttagcompound) {
|
||||
super.load(nbttagcompound);
|
||||
this.items = NonNullList.a(this.getSize(), ItemStack.a);
|
||||
ContainerUtil.b(nbttagcompound, this.items);
|
||||
this.burnTime = nbttagcompound.getShort("BurnTime");
|
||||
this.cookTime = nbttagcompound.getShort("CookTime");
|
||||
this.cookTimeTotal = nbttagcompound.getShort("CookTimeTotal");
|
||||
this.ticksForCurrentFuel = fuelTime((ItemStack) this.items.get(1));
|
||||
if (nbttagcompound.hasKeyOfType("CustomName", 8)) {
|
||||
this.m = nbttagcompound.getString("CustomName");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public NBTTagCompound save(NBTTagCompound nbttagcompound) {
|
||||
super.save(nbttagcompound);
|
||||
nbttagcompound.setShort("BurnTime", (short) this.burnTime);
|
||||
nbttagcompound.setShort("CookTime", (short) this.cookTime);
|
||||
nbttagcompound.setShort("CookTimeTotal", (short) this.cookTimeTotal);
|
||||
ContainerUtil.a(nbttagcompound, this.items);
|
||||
if (this.hasCustomName()) {
|
||||
nbttagcompound.setString("CustomName", this.m);
|
||||
}
|
||||
|
||||
return nbttagcompound;
|
||||
}
|
||||
|
||||
public int getMaxStackSize() {
|
||||
return 64;
|
||||
}
|
||||
|
||||
public boolean isBurning() {
|
||||
return this.burnTime > 0;
|
||||
}
|
||||
|
||||
public void e() {
|
||||
boolean flag = (this.getBlock() == Blocks.LIT_FURNACE); // CraftBukkit - SPIGOT-844 - Check if furnace block is lit using the block instead of burn time
|
||||
boolean flag1 = false;
|
||||
|
||||
// CraftBukkit start - Use wall time instead of ticks for cooking
|
||||
int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
|
||||
this.lastTick = MinecraftServer.currentTick;
|
||||
|
||||
// CraftBukkit - moved from below - edited for wall time
|
||||
if (this.isBurning() && this.canBurn()) {
|
||||
this.cookTime += elapsedTicks;
|
||||
if (this.cookTime >= this.cookTimeTotal) {
|
||||
this.cookTime -= this.cookTimeTotal; // Paper
|
||||
this.cookTimeTotal = this.a((ItemStack) this.items.get(0));
|
||||
this.burn();
|
||||
flag1 = true;
|
||||
}
|
||||
} else {
|
||||
this.cookTime = 0;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (this.isBurning()) {
|
||||
this.burnTime -= elapsedTicks; // CraftBukkit - use elapsedTicks in place of constant
|
||||
}
|
||||
|
||||
if (!this.world.isClientSide) {
|
||||
ItemStack itemstack = (ItemStack) this.items.get(1);
|
||||
|
||||
if (!this.isBurning() && (itemstack.isEmpty() || ((ItemStack) this.items.get(0)).isEmpty())) {
|
||||
if (!this.isBurning() && this.cookTime > 0) {
|
||||
this.cookTime = MathHelper.clamp(this.cookTime - 2, 0, this.cookTimeTotal);
|
||||
}
|
||||
} else if(this.items.get(1) != null && this.items.get(1).getItem() != Items.BUCKET) {
|
||||
// CraftBukkit start - Handle multiple elapsed ticks
|
||||
if (this.burnTime <= 0 && this.canBurn()) { // CraftBukkit - == to <=
|
||||
CraftItemStack fuel = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
FurnaceBurnEvent furnaceBurnEvent = new FurnaceBurnEvent(this.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), fuel, fuelTime(itemstack));
|
||||
this.world.getServer().getPluginManager().callEvent(furnaceBurnEvent);
|
||||
|
||||
if (furnaceBurnEvent.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.ticksForCurrentFuel = furnaceBurnEvent.getBurnTime();
|
||||
this.burnTime += this.ticksForCurrentFuel;
|
||||
if (this.burnTime > 0 && furnaceBurnEvent.isBurning()) {
|
||||
// CraftBukkit end
|
||||
flag1 = true;
|
||||
if (!itemstack.isEmpty()) {
|
||||
Item item = itemstack.getItem();
|
||||
|
||||
itemstack.subtract(1);
|
||||
if (itemstack.isEmpty()) {
|
||||
Item item1 = item.q();
|
||||
|
||||
this.items.set(1, item1 == null ? ItemStack.a : new ItemStack(item1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* CraftBukkit start - Moved up
|
||||
if (this.isBurning() && this.canBurn()) {
|
||||
++this.cookTime;
|
||||
if (this.cookTime == this.cookTimeTotal) {
|
||||
this.cookTime = 0;
|
||||
this.cookTimeTotal = this.a((ItemStack) this.items.get(0));
|
||||
this.burn();
|
||||
flag1 = true;
|
||||
}
|
||||
} else {
|
||||
this.cookTime = 0;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if (flag != this.isBurning()) {
|
||||
flag1 = true;
|
||||
BlockFurnace.a(this.isBurning(), this.world, this.position);
|
||||
this.invalidateBlockCache(); // CraftBukkit - Invalidate tile entity's cached block type
|
||||
}
|
||||
}
|
||||
|
||||
if (flag1) {
|
||||
this.update();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int a(ItemStack itemstack) {
|
||||
return 200;
|
||||
}
|
||||
|
||||
private boolean canBurn() {
|
||||
if (((ItemStack) this.items.get(0)).isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
ItemStack itemstack = RecipesFurnace.getInstance().getResult((ItemStack) this.items.get(0));
|
||||
|
||||
if (itemstack.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
ItemStack itemstack1 = (ItemStack) this.items.get(2);
|
||||
|
||||
// CraftBukkit - consider resultant count instead of current count
|
||||
return itemstack1.isEmpty() ? true : (!itemstack1.doMaterialsMatch(itemstack) ? false : (itemstack1.getCount() + itemstack.getCount() <= this.getMaxStackSize() && itemstack1.getCount() + itemstack.getCount() < itemstack1.getMaxStackSize() ? true : itemstack1.getCount() + itemstack.getCount() <= itemstack.getMaxStackSize()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void burn() {
|
||||
if (this.canBurn()) {
|
||||
ItemStack itemstack = (ItemStack) this.items.get(0);
|
||||
ItemStack itemstack1 = RecipesFurnace.getInstance().getResult(itemstack);
|
||||
ItemStack itemstack2 = (ItemStack) this.items.get(2);
|
||||
|
||||
// CraftBukkit start - fire FurnaceSmeltEvent
|
||||
CraftItemStack source = CraftItemStack.asCraftMirror(itemstack);
|
||||
org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemstack1);
|
||||
|
||||
FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(this.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), source, result);
|
||||
this.world.getServer().getPluginManager().callEvent(furnaceSmeltEvent);
|
||||
|
||||
if (furnaceSmeltEvent.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
result = furnaceSmeltEvent.getResult();
|
||||
itemstack1 = CraftItemStack.asNMSCopy(result);
|
||||
|
||||
if (!itemstack1.isEmpty()) {
|
||||
if (itemstack2.isEmpty()) {
|
||||
this.items.set(2, itemstack1.cloneItemStack());
|
||||
} else if (CraftItemStack.asCraftMirror(itemstack2).isSimilar(result)) {
|
||||
itemstack2.add(itemstack1.getCount());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (itemstack2.isEmpty()) {
|
||||
this.items.set(2, itemstack1.cloneItemStack());
|
||||
} else if (itemstack2.getItem() == itemstack1.getItem()) {
|
||||
itemstack2.add(1);
|
||||
}
|
||||
*/
|
||||
// CraftBukkit end
|
||||
|
||||
if (itemstack.getItem() == Item.getItemOf(Blocks.SPONGE) && itemstack.getData() == 1 && !((ItemStack) this.items.get(1)).isEmpty() && ((ItemStack) this.items.get(1)).getItem() == Items.BUCKET) {
|
||||
this.items.set(1, new ItemStack(Items.WATER_BUCKET));
|
||||
}
|
||||
|
||||
itemstack.subtract(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static int fuelTime(ItemStack itemstack) {
|
||||
if (itemstack.isEmpty()) {
|
||||
return 0;
|
||||
} else {
|
||||
Item item = itemstack.getItem();
|
||||
|
||||
return item == Item.getItemOf(Blocks.WOODEN_SLAB) ? 150 : (item == Item.getItemOf(Blocks.WOOL) ? 100 : (item == Item.getItemOf(Blocks.CARPET) ? 67 : (item == Item.getItemOf(Blocks.LADDER) ? 300 : (item == Item.getItemOf(Blocks.WOODEN_BUTTON) ? 100 : (Block.asBlock(item).getBlockData().getMaterial() == Material.WOOD ? 300 : (item == Item.getItemOf(Blocks.COAL_BLOCK) ? 16000 : (item instanceof ItemTool && "WOOD".equals(((ItemTool) item).h()) ? 200 : (item instanceof ItemSword && "WOOD".equals(((ItemSword) item).h()) ? 200 : (item instanceof ItemHoe && "WOOD".equals(((ItemHoe) item).g()) ? 200 : (item == Items.STICK ? 100 : (item != Items.BOW && item != Items.FISHING_ROD ? (item == Items.SIGN ? 200 : (item == Items.COAL ? 1600 : (item == Items.LAVA_BUCKET ? 20000 : (item != Item.getItemOf(Blocks.SAPLING) && item != Items.BOWL ? (item == Items.BLAZE_ROD ? 2400 : (item instanceof ItemDoor && item != Items.IRON_DOOR ? 200 : (item instanceof ItemBoat ? 400 : 0))) : 100)))) : 300)))))))))));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isFuel(ItemStack itemstack) {
|
||||
return fuelTime(itemstack) > 0;
|
||||
}
|
||||
|
||||
public boolean a(EntityHuman entityhuman) {
|
||||
return this.world.getTileEntity(this.position) != this ? false : entityhuman.d((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D) <= 64.0D;
|
||||
}
|
||||
|
||||
public void startOpen(EntityHuman entityhuman) {}
|
||||
|
||||
public void closeContainer(EntityHuman entityhuman) {}
|
||||
|
||||
public boolean b(int i, ItemStack itemstack) {
|
||||
if (i == 2) {
|
||||
return false;
|
||||
} else if (i != 1) {
|
||||
return true;
|
||||
} else {
|
||||
ItemStack itemstack1 = (ItemStack) this.items.get(1);
|
||||
|
||||
return isFuel(itemstack) || SlotFurnaceFuel.d_(itemstack) && itemstack1.getItem() != Items.BUCKET;
|
||||
}
|
||||
}
|
||||
|
||||
public int[] getSlotsForFace(EnumDirection enumdirection) {
|
||||
return enumdirection == EnumDirection.DOWN ? TileEntityFurnace.f : (enumdirection == EnumDirection.UP ? TileEntityFurnace.a : TileEntityFurnace.g);
|
||||
}
|
||||
|
||||
public boolean canPlaceItemThroughFace(int i, ItemStack itemstack, EnumDirection enumdirection) {
|
||||
return this.b(i, itemstack);
|
||||
}
|
||||
|
||||
public boolean canTakeItemThroughFace(int i, ItemStack itemstack, EnumDirection enumdirection) {
|
||||
if (enumdirection == EnumDirection.DOWN && i == 1) {
|
||||
Item item = itemstack.getItem();
|
||||
|
||||
if (item != Items.WATER_BUCKET && item != Items.BUCKET) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getContainerName() {
|
||||
return "minecraft:furnace";
|
||||
}
|
||||
|
||||
public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) {
|
||||
return new ContainerFurnace(playerinventory, this);
|
||||
}
|
||||
|
||||
public int getProperty(int i) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
return this.burnTime;
|
||||
|
||||
case 1:
|
||||
return this.ticksForCurrentFuel;
|
||||
|
||||
case 2:
|
||||
return this.cookTime;
|
||||
|
||||
case 3:
|
||||
return this.cookTimeTotal;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void setProperty(int i, int j) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
this.burnTime = j;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
this.ticksForCurrentFuel = j;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
this.cookTime = j;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
this.cookTimeTotal = j;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int h() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.items.clear();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user